package org.radarcns.auth.authentication;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import org.radarcns.auth.exception.TokenValidationException;
import org.radarcns.auth.security.jwk.JavaWebKey;
import org.radarcns.auth.security.jwk.JavaWebKeySet;
import org.radarcns.auth.token.validation.ECTokenValidationAlgorithm;
import org.radarcns.auth.token.validation.RSATokenValidationAlgorithm;
import org.radarcns.auth.token.validation.TokenValidationAlgorithm;
import org.radarcns.auth.token.validation.deprecated.DeprecatedEcTokenValidationAlgorithm;

public class AlgorithmLoader {

    private final List<TokenValidationAlgorithm> supportedAlgorithmsForWebKeySets;
    private final List<TokenValidationAlgorithm> supportedAlgorithmsForPublicKeys;

    /**
     * Creates an instance of {@link AlgorithmLoader} with lists of
     * {@link TokenValidationAlgorithm} provided.
     * @param algorithmsForWebKeys default support. Algorithms to be supported for
     *                             public keys shared from public key endpoints as
     *                             {@link JavaWebKeySet}.
     * @param algorithmsForLocalPublicKeys deprecated support. Algorithms to be supported for
     *                                     public keys configured in config file.
     */
    public AlgorithmLoader(List<TokenValidationAlgorithm> algorithmsForWebKeys,
            List<TokenValidationAlgorithm> algorithmsForLocalPublicKeys) {
        this.supportedAlgorithmsForWebKeySets = algorithmsForWebKeys;
        this.supportedAlgorithmsForPublicKeys = algorithmsForLocalPublicKeys;
    }

    /**
     * Creates algorithm loader with default algorithms.
     */
    public AlgorithmLoader() {
        this(Arrays.asList(
                new ECTokenValidationAlgorithm(),
                new RSATokenValidationAlgorithm()),
                Arrays.asList(
                new DeprecatedEcTokenValidationAlgorithm(),
                new RSATokenValidationAlgorithm()));
    }

    private Algorithm algorithmFromPublicKey(String publicKey) {
        // We deny to trust the public key if the reported algorithm is unknown to us
        // https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/
        return loadAlgorithmFromPublicKey(supportedAlgorithmsForWebKeySets, publicKey);
    }

    private static Algorithm loadAlgorithmFromPublicKey(
            List<TokenValidationAlgorithm> supportedAlgorithms, String publicKey) {
        return supportedAlgorithms
                .stream()
                .filter(algorithm -> publicKey.startsWith(algorithm.getKeyHeader()))
                .findFirst()
                .orElseThrow(() ->
                        new TokenValidationException("Unsupported public key: " + publicKey))
                .getAlgorithm(publicKey);
    }

    /**
     * Loads algorithms using deprecated method.
     * @param publicKey publicKeys to load algorithms.
     * @return instance of {@link Algorithm}.
     */
    public Algorithm loadDeprecatedAlgorithmFromPublicKey(String publicKey) {
        // We deny to trust the public key if the reported algorithm is unknown to us
        // https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/
        return loadAlgorithmFromPublicKey(supportedAlgorithmsForPublicKeys, publicKey);
    }

    /**
     * Loads Algorithms from {@link JavaWebKeySet}.
     * @param publicKeyInfo java web key set to load algorithm.
     * @return List of {@link Algorithm}.
     */
    public List<Algorithm> loadAlgorithmsFromJavaWebKeys(JavaWebKeySet publicKeyInfo) {
        return publicKeyInfo
                .getKeys()
                .stream()
                .map(JavaWebKey::getValue)
                .map(this::algorithmFromPublicKey)
                .collect(Collectors.toList());
    }

    /**
     * Creates in {@link JWTVerifier} using Algorithms and audience.
     * @param algorithm instance of {@link Algorithm} to create verifier from.
     * @param audience to which audience.
     * @return instance of {@link JWTVerifier}.
     */
    public static JWTVerifier buildVerifier(Algorithm algorithm, String audience) {
        return JWT.require(algorithm)
                .withAudience(audience)
                .build();
    }
}

@@ -885,16 +885,24 @@
 blicKeys
+InConfig
 ;%0A%0A    /
@@ -1028,33 +1028,42 @@
 .%0A     * @param 
-a
+supportedA
 lgorithmsForWebK
@@ -1064,16 +1064,19 @@
 orWebKey
+Set
 s defaul
@@ -1264,33 +1264,42 @@
 .%0A     * @param 
-a
+supportedA
 lgorithmsForLoca
@@ -1286,37 +1286,32 @@
 tedAlgorithmsFor
-Local
 PublicKeys depre
@@ -1304,16 +1304,24 @@
 blicKeys
+InConfig
  depreca
@@ -1345,38 +1345,24 @@
 rithms to be
- supported for
 %0A     *     
@@ -1385,32 +1385,47 @@
                 
+ supported for 
 public keys conf
@@ -1505,33 +1505,42 @@
 ationAlgorithm%3E 
-a
+supportedA
 lgorithmsForWebK
@@ -1541,16 +1541,19 @@
 orWebKey
+Set
 s,%0A     
@@ -1590,17 +1590,26 @@
 orithm%3E 
-a
+supportedA
 lgorithm
@@ -1604,37 +1604,32 @@
 tedAlgorithmsFor
-Local
 PublicKeys) %7B%0A  
@@ -1622,16 +1622,24 @@
 blicKeys
+InConfig
 ) %7B%0A    
@@ -1678,25 +1678,34 @@
 ebKeySets = 
-a
+supportedA
 lgorithmsFor
@@ -1710,16 +1710,19 @@
 orWebKey
+Set
 s;%0A     
@@ -1765,12 +1765,29 @@
 Keys
- = a
+InConfig = supportedA
 lgor
@@ -1798,13 +1798,8 @@
 sFor
-Local
 Publ
@@ -1804,16 +1804,24 @@
 blicKeys
+InConfig
 ;%0A    %7D%0A
@@ -3526,16 +3526,24 @@
 blicKeys
+InConfig
 , public

rename variables