class DefaultImmutableTokenBucket implements ImmutableTokenBucket {

    @Override
    public Optional<Pair<Long, ImmutableTokenBucket>> tryTake(long min, long max) {
        if (bucketLevel >= max) {
            return Optional.of(Pair.of(max, new DefaultImmutableTokenBucket(bucketSize, bucketLevel - max, refillStrategy)));
        }

        Pair<ImmutableRefillStrategy, Long> consumed = refillStrategy.consume();
        long refill = consumed.getRight();
        ImmutableRefillStrategy newRefillStrategyInstance = consumed.getLeft();

        if (refill == 0) {
            if (bucketLevel >= min) {
                return Optional.of(Pair.of(bucketLevel, new DefaultImmutableTokenBucket(bucketSize, 0, refillStrategy)));
            }
        } else {
            long newBucketLevel = Math.min(bucketSize, bucketLevel + refill);
            if (newBucketLevel >= min) {
                long tokensToTake = Math.min(newBucketLevel, max);
                return Optional.of(Pair.of(tokensToTake, new DefaultImmutableTokenBucket(bucketSize, newBucketLevel - tokensToTake, newRefillStrategyInstance)));
            }
        }
        return Optional.empty();
    }

    public  DefaultImmutableTokenBucket(long bucketSize, long bucketLevel, ImmutableRefillStrategy refillStrategy);

}