// Path: src/main/java/io/appform/dropwizard/sharding/exceptions/ShardBlacklistedException.java
// public class ShardBlacklistedException extends RuntimeException {
//     public ShardBlacklistedException(int shard) {
//         super(String.format("Shard %d is blacklisted", shard));
//     }
// }


import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import io.appform.dropwizard.sharding.exceptions.ShardBlacklistedException;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;
    abstract public int numBuckets();

    abstract protected int numShards();

    abstract protected int shardForBucketImpl(int bucketId);

    protected ShardManager(ShardBlacklistingStore shardBlacklistingStore) {
        this.shardBlacklistingStore = shardBlacklistingStore;
        this.blackListedShards = Caffeine.newBuilder()
                .maximumSize(10_000)
                .expireAfterWrite(1, TimeUnit.MINUTES)
                .refreshAfterWrite(15, TimeUnit.SECONDS)
                .build(shardBlacklistingStore::blacklisted);
    }

    public int shardForBucket(int bucketId) {
        final int shard = shardForBucketImpl(bucketId);
        final Boolean isBlacklisted = blackListedShards.get(shard);
        if(null != isBlacklisted && isBlacklisted) {
            throw new ShardBlacklistedException(shard);
        }
        return shard;
    }

    public boolean isMappedToValidShard(int bucketId) {
        final int shard = shardForBucketImpl(bucketId);
        final Boolean isBlacklisted = blackListedShards.get(shard);
        if(null != isBlacklisted && isBlacklisted) {
            return false;
        }
// Path: src/main/java/io/appform/dropwizard/sharding/exceptions/ShardBlacklistedException.java
// public class ShardBlacklistedException extends RuntimeException {
//     public ShardBlacklistedException(int shard) {
//         super(String.format("Shard %d is blacklisted", shard));
//     }
// }


// Path: src/main/java/io/appform/dropwizard/sharding/sharding/ShardManager.java
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import io.appform.dropwizard.sharding.exceptions.ShardBlacklistedException;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;

    abstract public int numBuckets();

    abstract protected int numShards();

    abstract protected int shardForBucketImpl(int bucketId);

    protected ShardManager(ShardBlacklistingStore shardBlacklistingStore) {
        this.shardBlacklistingStore = shardBlacklistingStore;
        this.blackListedShards = Caffeine.newBuilder()
                .maximumSize(10_000)
                .expireAfterWrite(1, TimeUnit.MINUTES)
                .refreshAfterWrite(15, TimeUnit.SECONDS)
                .build(shardBlacklistingStore::blacklisted);
    }

    public int shardForBucket(int bucketId) {
        final int shard = shardForBucketImpl(bucketId);
        final Boolean isBlacklisted = blackListedShards.get(shard);
        if(null != isBlacklisted && isBlacklisted) {
            throw new ShardBlacklistedException(shard);
        }
        return shard;
    }

    public boolean isMappedToValidShard(int bucketId) {
        final int shard = shardForBucketImpl(bucketId);
        final Boolean isBlacklisted = blackListedShards.get(shard);
        if(null != isBlacklisted && isBlacklisted) {
            return false;
        }
        return true;