package com.rjconsultores.ventaboletos.web.utilerias.security;

import java.text.ParseException;
import java.time.Duration;
import java.util.Calendar;

import javax.xml.bind.DatatypeConverter;

import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.Payload;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jwt.JWTClaimsSet;

import net.minidev.json.JSONObject;

public class SecurityEmpresaToken {
	private static Logger log = LogManager.getLogger(SecurityEmpresaToken.class);
			
	private String secret = "#KO&Fm4_k.sU9M8`6Mx'F\\\"H:*Qxu]6F4r,)JmZ2Jwafd)I.2[RET'1:)VQ6mG9,";
	private static final Duration ttl = Duration.ofDays(7);
	
	private Gson gson = new Gson();
	
	public String bodyRequestGenerate(final Integer empresaId, final String cnpj) throws SecurityException {
		try {
			AESGSMHelper crypto = new AESGSMHelper();

			JsonObject json = new JsonObject();
			json.addProperty("empresaId", empresaId);
			json.addProperty("CNPJ", cnpj);
			
			return crypto.encrypt(json.toString());			
			
		} catch (Exception e) {
			log.error("Erro ao gerar o body usado no request da licença: " + e.getMessage(), e);
			
			throw new SecurityException(e);
		}
	}
	
	public String licenseDefaultGenerate(final Integer empresaId, final String cnpj) throws SecurityException {
		try {
			AESGSMHelper crypto = new AESGSMHelper();

			JsonObject json = new JsonObject();
			json.addProperty("empresaId", empresaId);
			json.addProperty("CNPJ", cnpj);
			json.addProperty("aprovado", 1);
			
			return crypto.encrypt(json.toString());			
			
		} catch (Exception e) {
			log.error("Erro ao gerar a licença padrão para as empresas existentes: " + e.getMessage(), e);
			
			throw new SecurityException(e);
		}
	}

	public boolean licenseValidate(final String license, final Integer empresaId, final String cnpj) {
		try {
			if (StringUtils.isBlank(license)){
				return false;
			}
			
			AESGSMHelper crypto = new AESGSMHelper();
			
			final String value = crypto.decrypt(license);
			final JsonObject json = gson.fromJson(value, JsonObject.class);
			
			if (json.has("empresaId") && json.get("empresaId").getAsInt() == empresaId.intValue()
					&& json.has("CNPJ") && json.get("CNPJ").getAsString().equals(cnpj)
					&& json.has("aprovado")) {
				log.debug("[empresaId=" + json.get("empresaId").getAsString() + ", CNPJ=" + json.get("CNPJ").getAsString() + ", aprovado=" + json.get("aprovado").getAsString() + "]");
				
				return json.get("aprovado").getAsString().equals("1");
			}
		} catch (Exception e) {
			log.error("Erro ao gerar o body usado no request da licença: " + e.getMessage(), e);
		}

		return false;
	}
	
	public String requestGenerate(String licenseRequest) throws SecurityException {
		return requestGenerate(licenseRequest, ttl);
	}
	
	public String requestGenerate(String licenseRequest, Duration ttl) throws SecurityException {
		try {
			Calendar cal = Calendar.getInstance();
			cal.add(Calendar.MILLISECOND, (int) ttl.toMillis());
			
			JWTClaimsSet claims = new JWTClaimsSet.Builder()
					.expirationTime(cal.getTime())
					.claim("sub", licenseRequest)
					.claim("userId", "adm")
					.claim("role", "ROLE_TOKEN")
					.build();

			JWSObject jwsObject = new JWSObject(new JWSHeader(JWSAlgorithm.HS256), new Payload(claims.toJSONObject()));

			jwsObject.sign(new MACSigner(DatatypeConverter.parseBase64Binary(secret)));

			return jwsObject.serialize();
		} catch (Exception e) {
			log.error("Erro ao gerar a request: " + e.getMessage(), e);
			
			throw new SecurityException(e);
		}
	}
	
	public String tokenValidate(final String token) throws SecurityException {
		try {
			JWSObject jwsObject = JWSObject.parse(token);
			JSONObject jsonPayload = jwsObject.getPayload().toJSONObject();
			JWTClaimsSet claims = JWTClaimsSet.parse(jsonPayload);
			
			if (claims.getExpirationTime().compareTo(Calendar.getInstance().getTime()) < 0) {
				throw new SecurityException("Token expirado");
			}
			
			return claims.getSubject();
			
		} catch (SecurityException e) {
			throw e;
			
		} catch (ParseException e) {
			log.error("Erro no parser do token: " + e.getMessage(), e);
			
			throw new SecurityException(e);
			
		} catch (Exception e) {
			log.error("Erro ao validar o token: " + e.getMessage(), e);
			
			throw new SecurityException(e);
		}
	}
}
