package org.apereo.cas.adaptors.x509.authentication.revocation.policy;

import org.apereo.cas.adaptors.x509.authentication.ExpiredCRLException;
import org.apereo.cas.adaptors.x509.util.CertUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.security.cert.X509CRL;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;

/**
 * Implements a policy to handle expired CRL data whereby expired data is permitted
 * up to a threshold period of time but not afterward.
 *
 * @author Marvin S. Addison
 * @since 3.4.6
 *
 */
public class ThresholdExpiredCRLRevocationPolicy implements RevocationPolicy<X509CRL> {

    private static final Logger LOGGER = LoggerFactory.getLogger(ThresholdExpiredCRLRevocationPolicy.class);
    
    /** Expired threshold period in seconds after which expired CRL data is rejected. */
    private final int threshold;

    /**
     * Creates a new instance.
     *
     * @param threshold Number of seconds; MUST be non-negative integer.
     */
    public ThresholdExpiredCRLRevocationPolicy(final int threshold) {
        this.threshold = threshold;
    }

    /**
     * {@inheritDoc}
     * The CRL next update time is compared against the current time with the threshold
     * applied and rejected if and only if the next update time is in the past.
     *
     * @param crl CRL instance to evaluate.
     *
     * @throws ExpiredCRLException On expired CRL data. Check the exception type for exact details
     *
     * @see RevocationPolicy#apply(java.lang.Object)
     */
    @Override
    public void apply(final X509CRL crl) throws ExpiredCRLException {
        final ZonedDateTime cutoff = ZonedDateTime.now(ZoneOffset.UTC);
        if (CertUtils.isExpired(crl, cutoff)) {
            if (CertUtils.isExpired(crl, cutoff.minusSeconds(this.threshold))) {
                throw new ExpiredCRLException(crl.toString(), cutoff, this.threshold);
            }
            LOGGER.info(String.format("CRL expired on %s but is within threshold period, %s seconds.",
                        crl.getNextUpdate(), this.threshold));
        }
    }
}
