package com.fp.frontend.webservices.rest.services;

import java.util.LinkedList;
import java.util.List;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import org.apache.commons.lang.StringUtils;
import org.jose4j.jwk.JsonWebKey;
import org.jose4j.jwk.JsonWebKeySet;
import org.jose4j.jwk.RsaJsonWebKey;
import org.jose4j.jwk.RsaJwkGenerator;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.consumer.InvalidJwtException;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.lang.JoseException;

import com.fp.common.logger.APPLogger;
import com.fp.frontend.webservices.rest.dto.StatusMessage;
import com.fp.frontend.webservices.rest.util.JwtValidateException;

public class CommonResource {

    private static String BEARER = "Bearer ";
    final static String ISSUER = "ccffaa.mil.ec";
    static List<JsonWebKey> jwkList = null;

    static {
        APPLogger.getLogger().info("Inicializador estatico JWT...");
        jwkList = new LinkedList<>();
        for (int kid = 1; kid <= 3; kid++) {
            JsonWebKey jwk = null;
            try {
                jwk = RsaJwkGenerator.generateJwk(2048);
                APPLogger.getLogger().info("PUBLIC KEY (" + kid + "): "
                        + jwk.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY));
            } catch (JoseException e) {
                APPLogger.getLogger().error(e.getMessage());
            }
            jwk.setKeyId(String.valueOf(kid));
            jwkList.add(jwk);
        }
    }

    protected String generateTokenJwt(String username) {
        RsaJsonWebKey senderJwk = (RsaJsonWebKey) jwkList.get(0);
        senderJwk.setKeyId("1");
        // Create the Claims, which will be the content of the JWT
        JwtClaims claims = new JwtClaims();
        claims.setIssuer(ISSUER);
        claims.setExpirationTimeMinutesInTheFuture(60);
        claims.setGeneratedJwtId();
        claims.setIssuedAtToNow();
        claims.setNotBeforeMinutesInThePast(2);
        claims.setSubject(username);
        JsonWebSignature jws = new JsonWebSignature();

        jws.setPayload(claims.toJson());

        jws.setKeyIdHeaderValue(senderJwk.getKeyId());
        jws.setKey(senderJwk.getPrivateKey());

        jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);

        String jwt = null;
        try {
            jwt = jws.getCompactSerialization();
        } catch (JoseException e) {
            throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(new StatusMessage(Status.BAD_REQUEST.getStatusCode(), "Error al generar el token JWT")).build());
        }
        return jwt;
    }

    public void validateToken(String bearerToken) {
        try {
            String token = getToken(bearerToken);
            if (token == null || token.trim().isEmpty()) {
                StatusMessage statusMessage = new StatusMessage();
                statusMessage.setStatus(Status.FORBIDDEN.getStatusCode());
                statusMessage.setMessage(
                        "Acceso Denegado debe enviar el token !!!");
                throw new JwtValidateException(statusMessage);
            }

            JsonWebKeySet jwks = new JsonWebKeySet(jwkList);
            JsonWebKey jwk = jwks.findJsonWebKey("1", null, null, null);
            JwtConsumer jwtConsumer = new JwtConsumerBuilder()
                    .setRequireExpirationTime()
                    .setAllowedClockSkewInSeconds(30)
                    .setRequireSubject()
                    .setExpectedIssuer(ISSUER)
                    .setVerificationKey(jwk.getKey())
                    .build();
            JwtClaims jwtClaims = jwtConsumer.processToClaims(token);
            APPLogger.getLogger().info("JWT validation succeeded! " + jwtClaims);
        } catch (InvalidJwtException e) {
            APPLogger.getLogger().error("JWT el token no es valida: ");
            StatusMessage statusMessage = new StatusMessage();
            statusMessage.setStatus(Status.FORBIDDEN.getStatusCode());
            statusMessage.setMessage("Acceso denegado el token no es valido !!!");
            throw new JwtValidateException(statusMessage);
        } catch (Exception e) {
            APPLogger.getLogger().error("JWT error a validar el token: ");
            StatusMessage statusMessage = new StatusMessage();
            statusMessage.setStatus(Status.FORBIDDEN.getStatusCode());
            statusMessage.setMessage("Acceso denegado el token no es valido !!!");
            throw new JwtValidateException(statusMessage);
        }
    }

    private String getToken(String bearerToken) {
        String token = null;
        if (bearerToken.startsWith(BEARER)) {
            token = bearerToken.replace(BEARER, StringUtils.EMPTY);
        }
        return token;
    }
}
