package com.auth0;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.apache.commons.lang3.Validate;

import java.util.Arrays;
import java.util.List;

abstract class SignatureVerifier {

    private final JWTVerifier verifier;
    private final List<String> acceptedAlgorithms;

    /**
     * Creates a new JWT Signature Verifier.
     * This instance will validate the token was signed using an expected algorithm
     * and then proceed to verify its signature
     *
     * @param verifier  the instance that knows how to verify the signature. When null, the signature will not be checked.
     * @param algorithm the accepted algorithms. Must never be null!
     */
    SignatureVerifier(JWTVerifier verifier, String... algorithm) {
        Validate.notEmpty(algorithm);
        this.verifier = verifier;
        this.acceptedAlgorithms = Arrays.asList(algorithm);
    }

    private DecodedJWT decodeToken(String token) throws TokenValidationException {
        try {
            return JWT.decode(token);
        } catch (JWTDecodeException e) {
            throw new TokenValidationException("ID token could not be decoded", e);
        }
    }

    DecodedJWT verifySignature(String token) throws TokenValidationException {
        DecodedJWT decoded = decodeToken(token);
        if (!this.acceptedAlgorithms.contains(decoded.getAlgorithm())) {
            throw new TokenValidationException(String.format("Signature algorithm of \"%s\" is not supported. Expected the ID token to be signed with \"%s\".", decoded.getAlgorithm(), this.acceptedAlgorithms));
        }
        if (verifier != null) {
            try {
                verifier.verify(decoded);
            } catch (SignatureVerificationException e) {
                throw new TokenValidationException("Invalid token signature", e);
            } catch (JWTVerificationException ignored) {
                //NO-OP. Will be catch on a different step
                //Would only trigger for "expired tokens" (invalid exp)
            }
        }

        return decoded;
    }
}