// 
// Decompiled by Procyon v0.6.0
// 

package com.nimbusds.jose.jwk.source;

import java.util.Objects;
import com.nimbusds.jose.util.ResourceRetriever;
import com.nimbusds.jose.util.DefaultResourceRetriever;
import java.net.URL;
import com.nimbusds.jose.util.health.HealthReportListener;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ExecutorService;
import com.nimbusds.jose.util.events.EventListener;
import com.nimbusds.jose.proc.SecurityContext;

public class JWKSourceBuilder<C extends SecurityContext>
{
    public static final int DEFAULT_HTTP_CONNECT_TIMEOUT = 500;
    public static final int DEFAULT_HTTP_READ_TIMEOUT = 500;
    public static final int DEFAULT_HTTP_SIZE_LIMIT = 51200;
    public static final long DEFAULT_CACHE_TIME_TO_LIVE = 300000L;
    public static final long DEFAULT_CACHE_REFRESH_TIMEOUT = 15000L;
    public static final long DEFAULT_REFRESH_AHEAD_TIME = 30000L;
    public static final long DEFAULT_RATE_LIMIT_MIN_INTERVAL = 30000L;
    private final JWKSetSource<C> jwkSetSource;
    private boolean caching;
    private long cacheTimeToLive;
    private long cacheRefreshTimeout;
    private EventListener<CachingJWKSetSource<C>, C> cachingEventListener;
    private boolean refreshAhead;
    private long refreshAheadTime;
    private boolean refreshAheadScheduled;
    private ExecutorService executorService;
    private boolean shutdownExecutorOnClose;
    private ScheduledExecutorService scheduledExecutorService;
    private boolean shutdownScheduledExecutorOnClose;
    private boolean rateLimited;
    private long minTimeInterval;
    private EventListener<RateLimitedJWKSetSource<C>, C> rateLimitedEventListener;
    private boolean retrying;
    private EventListener<RetryingJWKSetSource<C>, C> retryingEventListener;
    private boolean outageTolerant;
    private long outageCacheTimeToLive;
    private EventListener<OutageTolerantJWKSetSource<C>, C> outageEventListener;
    private HealthReportListener<JWKSetSourceWithHealthStatusReporting<C>, C> healthReportListener;
    protected JWKSource<C> failover;
    
    public static <C extends SecurityContext> JWKSourceBuilder<C> create(final URL jwkSetURL) {
        final DefaultResourceRetriever retriever = new DefaultResourceRetriever(500, 500, 51200);
        final JWKSetSource<C> jwkSetSource = new URLBasedJWKSetSource<C>(jwkSetURL, retriever);
        return new JWKSourceBuilder<C>(jwkSetSource);
    }
    
    public static <C extends SecurityContext> JWKSourceBuilder<C> create(final URL jwkSetURL, final ResourceRetriever retriever) {
        return new JWKSourceBuilder<C>(new URLBasedJWKSetSource<C>(jwkSetURL, retriever));
    }
    
    public static <C extends SecurityContext> JWKSourceBuilder<C> create(final JWKSetSource<C> source) {
        return new JWKSourceBuilder<C>(source);
    }
    
    private JWKSourceBuilder(final JWKSetSource<C> jwkSetSource) {
        this.caching = true;
        this.cacheTimeToLive = 300000L;
        this.cacheRefreshTimeout = 15000L;
        this.refreshAhead = true;
        this.refreshAheadTime = 30000L;
        this.refreshAheadScheduled = false;
        this.shutdownExecutorOnClose = true;
        this.shutdownScheduledExecutorOnClose = true;
        this.rateLimited = true;
        this.minTimeInterval = 30000L;
        this.retrying = false;
        this.outageTolerant = false;
        this.outageCacheTimeToLive = -1L;
        Objects.requireNonNull(jwkSetSource);
        this.jwkSetSource = jwkSetSource;
    }
    
    public JWKSourceBuilder<C> cache(final boolean enable) {
        this.caching = enable;
        return this;
    }
    
    public JWKSourceBuilder<C> cache(final long timeToLive, final long cacheRefreshTimeout) {
        this.caching = true;
        this.cacheTimeToLive = timeToLive;
        this.cacheRefreshTimeout = cacheRefreshTimeout;
        return this;
    }
    
    public JWKSourceBuilder<C> cache(final long timeToLive, final long cacheRefreshTimeout, final EventListener<CachingJWKSetSource<C>, C> eventListener) {
        this.caching = true;
        this.cacheTimeToLive = timeToLive;
        this.cacheRefreshTimeout = cacheRefreshTimeout;
        this.cachingEventListener = eventListener;
        return this;
    }
    
    public JWKSourceBuilder<C> cacheForever() {
        this.caching = true;
        this.cacheTimeToLive = Long.MAX_VALUE;
        this.refreshAhead = false;
        return this;
    }
    
    public JWKSourceBuilder<C> refreshAheadCache(final boolean enable) {
        if (enable) {
            this.caching = true;
        }
        this.refreshAhead = enable;
        return this;
    }
    
    public JWKSourceBuilder<C> refreshAheadCache(final long refreshAheadTime, final boolean scheduled) {
        this.caching = true;
        this.refreshAhead = true;
        this.refreshAheadTime = refreshAheadTime;
        this.refreshAheadScheduled = scheduled;
        return this;
    }
    
    public JWKSourceBuilder<C> refreshAheadCache(final long refreshAheadTime, final boolean scheduled, final EventListener<CachingJWKSetSource<C>, C> eventListener) {
        this.caching = true;
        this.refreshAhead = true;
        this.refreshAheadTime = refreshAheadTime;
        this.refreshAheadScheduled = scheduled;
        this.cachingEventListener = eventListener;
        return this;
    }
    
    public JWKSourceBuilder<C> refreshAheadCache(final long refreshAheadTime, final EventListener<CachingJWKSetSource<C>, C> eventListener, final ExecutorService executorService, final boolean shutdownExecutorOnClose, final ScheduledExecutorService scheduledExecutorService, final boolean shutdownScheduledExecutorOnClose) {
        this.caching = true;
        this.refreshAhead = true;
        this.refreshAheadTime = refreshAheadTime;
        this.refreshAheadScheduled = (scheduledExecutorService != null);
        this.cachingEventListener = eventListener;
        this.executorService = executorService;
        this.shutdownExecutorOnClose = shutdownExecutorOnClose;
        this.scheduledExecutorService = scheduledExecutorService;
        this.shutdownScheduledExecutorOnClose = shutdownScheduledExecutorOnClose;
        return this;
    }
    
    public JWKSourceBuilder<C> rateLimited(final boolean enable) {
        this.rateLimited = enable;
        return this;
    }
    
    public JWKSourceBuilder<C> rateLimited(final long minTimeInterval) {
        this.rateLimited = true;
        this.minTimeInterval = minTimeInterval;
        return this;
    }
    
    public JWKSourceBuilder<C> rateLimited(final long minTimeInterval, final EventListener<RateLimitedJWKSetSource<C>, C> eventListener) {
        this.rateLimited = true;
        this.minTimeInterval = minTimeInterval;
        this.rateLimitedEventListener = eventListener;
        return this;
    }
    
    public JWKSourceBuilder<C> failover(final JWKSource<C> failover) {
        this.failover = failover;
        return this;
    }
    
    public JWKSourceBuilder<C> retrying(final boolean enable) {
        this.retrying = enable;
        return this;
    }
    
    public JWKSourceBuilder<C> retrying(final EventListener<RetryingJWKSetSource<C>, C> eventListener) {
        this.retrying = true;
        this.retryingEventListener = eventListener;
        return this;
    }
    
    public JWKSourceBuilder<C> healthReporting(final HealthReportListener<JWKSetSourceWithHealthStatusReporting<C>, C> listener) {
        this.healthReportListener = listener;
        return this;
    }
    
    public JWKSourceBuilder<C> outageTolerant(final boolean enable) {
        this.outageTolerant = enable;
        return this;
    }
    
    public JWKSourceBuilder<C> outageTolerantForever() {
        this.outageTolerant = true;
        this.outageCacheTimeToLive = Long.MAX_VALUE;
        return this;
    }
    
    public JWKSourceBuilder<C> outageTolerant(final long timeToLive) {
        this.outageTolerant = true;
        this.outageCacheTimeToLive = timeToLive;
        return this;
    }
    
    public JWKSourceBuilder<C> outageTolerant(final long timeToLive, final EventListener<OutageTolerantJWKSetSource<C>, C> eventListener) {
        this.outageTolerant = true;
        this.outageCacheTimeToLive = timeToLive;
        this.outageEventListener = eventListener;
        return this;
    }
    
    public JWKSource<C> build() {
        if (!this.caching && this.rateLimited) {
            throw new IllegalStateException("Rate limiting requires caching");
        }
        if (!this.caching && this.refreshAhead) {
            throw new IllegalStateException("Refresh-ahead caching requires general caching");
        }
        if (this.caching && this.rateLimited && this.cacheTimeToLive <= this.minTimeInterval) {
            throw new IllegalStateException("The rate limiting min time interval between requests must be less than the cache time-to-live");
        }
        if (this.caching && this.outageTolerant && this.cacheTimeToLive == Long.MAX_VALUE && this.outageCacheTimeToLive == Long.MAX_VALUE) {
            throw new IllegalStateException("Outage tolerance not necessary with a non-expiring cache");
        }
        if (this.caching && this.refreshAhead && this.cacheTimeToLive == Long.MAX_VALUE) {
            throw new IllegalStateException("Refresh-ahead caching not necessary with a non-expiring cache");
        }
        JWKSetSource<C> source = this.jwkSetSource;
        if (this.retrying) {
            source = new RetryingJWKSetSource<C>(source, this.retryingEventListener);
        }
        if (this.outageTolerant) {
            if (this.outageCacheTimeToLive == -1L) {
                if (this.caching) {
                    this.outageCacheTimeToLive = this.cacheTimeToLive * 10L;
                }
                else {
                    this.outageCacheTimeToLive = 3000000L;
                }
            }
            source = new OutageTolerantJWKSetSource<C>(source, this.outageCacheTimeToLive, this.outageEventListener);
        }
        if (this.healthReportListener != null) {
            source = new JWKSetSourceWithHealthStatusReporting<C>(source, this.healthReportListener);
        }
        if (this.rateLimited) {
            source = new RateLimitedJWKSetSource<C>(source, this.minTimeInterval, this.rateLimitedEventListener);
        }
        if (this.refreshAhead) {
            if (this.refreshAheadScheduled && this.scheduledExecutorService == null) {
                this.scheduledExecutorService = RefreshAheadCachingJWKSetSource.createDefaultScheduledExecutorService();
            }
            if (this.executorService == null) {
                this.executorService = RefreshAheadCachingJWKSetSource.createDefaultExecutorService();
            }
            source = new RefreshAheadCachingJWKSetSource<C>(source, this.cacheTimeToLive, this.cacheRefreshTimeout, this.refreshAheadTime, this.executorService, this.shutdownExecutorOnClose, this.cachingEventListener, this.scheduledExecutorService, this.shutdownScheduledExecutorOnClose);
        }
        else if (this.caching) {
            source = new CachingJWKSetSource<C>(source, this.cacheTimeToLive, this.cacheRefreshTimeout, this.cachingEventListener);
        }
        final JWKSource<C> jwkSource = new JWKSetBasedJWKSource<C>(source);
        if (this.failover != null) {
            return new JWKSourceWithFailover<C>(jwkSource, this.failover);
        }
        return jwkSource;
    }
}
