package com.eactive.eai.adapter.http.dynamic.filter.custom;

import java.io.UnsupportedEncodingException;
import java.util.Properties;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.text.SimpleDateFormat;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.Mac;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.eactive.eai.adapter.AdapterManager;
import com.eactive.eai.common.property.PropManager;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import org.apache.commons.net.util.Base64;
import org.springframework.http.HttpStatus;
import org.apache.commons.lang3.StringUtils;

import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilter;
import com.eactive.eai.adapter.http.dynamic.filter.JwtAuthException;
import com.eactive.eai.common.server.Keys;
import com.eactive.eai.common.util.Logger;

import com.fasterxml.jackson.databind.ObjectMapper;

public class KbankHmacSha256VerifyFilter implements HttpAdapterFilter {
	
	static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
	
	private static final String SERVICE_CODE_UNKNOWN = "00";
	private static final int TIMESTAMP_EXPIRATION_SECONDS = 60*10*1000;
	private static final String GROUP_NAME = "HMAC_INFO";
	
	private ObjectMapper mapper = new ObjectMapper();
	
	@Override
	public Object doPreFilter(String adptGrpName, String adptName, Object message, Properties prop,
			HttpServletRequest request, HttpServletResponse response) throws Exception {
		
		try {
			
			String hmacSignature = getHeaderCaseInsensitive(request, "hmac_signature");
			String hmacTimestamp = getHeaderCaseInsensitive(request, "hmac_timestamp");
						
			if(StringUtils.isBlank(hmacSignature) || StringUtils.isBlank(hmacTimestamp)) {
				throw new JwtAuthException(String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"), "Can not find hmac info");
			}
			
			long inputTime = new SimpleDateFormat("yyyyMMddHHmmss").parse(hmacTimestamp).getTime();
			long currentTime = new Date().getTime();
			String timeStamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
			
			logger.debug("### inputTime :"+inputTime);			
			logger.debug("### currentTime :"+currentTime);
			logger.debug("### timeStamp :"+timeStamp);
			
			String systemMode = System.getProperty(Keys.EAI_SYSTEMMODE);
			
			if(currentTime - inputTime > TIMESTAMP_EXPIRATION_SECONDS) {
				
				logger.debug("### systemMode:" + systemMode);
				logger.debug("### currentTime - inputTime:" + (currentTime - inputTime));
				
				if("P".equals(systemMode)) {
					throw new JwtAuthException(
							String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN,"00"), "Signature verifying failed");
				}				
			}

			String sMessage = (String)message;

			sMessage = sMessage.replace(" ","");
			sMessage = sMessage.replace("\n","");
			sMessage = sMessage.replace("\r","");

			sMessage = sMessage + hmacTimestamp;

			logger.info("### sMessage:"+sMessage);

			String alCoId = "AL2021012901001";

			JsonNode rootNode = parseJson(adptGrpName, message);

			for(Iterator<String> it = rootNode.fieldNames(); it.hasNext();){
				String fieldName = it.next();
				if(fieldName.equals("ALCO_ID")) alCoId = rootNode.get(fieldName).asText();
			}

			logger.info("### alCoId:"+alCoId);

			Properties hmacProp = PropManager.getInstance().getProperties(GROUP_NAME);

			String hmacKey = hmacProp.getProperty(alCoId+"-hmack-key","");
			String hmacKek = hmacProp.getProperty(alCoId+"-hmack-kek","");
			String hmacIvForKey = hmacProp.getProperty(alCoId+"-hmack-iv-for-key","");

			logger.debug("### hmacKey:"+hmacKey);
			logger.debug("### hmacIvForKey:"+hmacIvForKey);
			logger.debug("### hmacKek:"+hmacKek);

			//kbank.payment.hmac-key
			int hmacKeyLen = hmacKey.length();
			byte[] hmacKeyData = new byte[hmacKeyLen/2];
			for(int i=0;i<hmacKeyLen;i+=2){
				hmacKeyData[i/2] = (byte)((Character.digit(hmacKey.charAt(i), 16) << 4) + Character.digit(hmacKey.charAt(i+1), 16));
			}

			//kbank.payment.hmac-kek
			int hmacKekLen = hmacKek.length();
			byte[] hmacKekData = new byte[hmacKekLen/2];
			for(int i=0;i<hmacKekLen;i+=2){
				hmacKekData[i/2] = (byte)((Character.digit(hmacKek.charAt(i),16) << 4) + Character.digit(hmacKek.charAt(i+1), 16));
			}

			//kbank.payment.hmac-iv-for-key
			int hmacIvForKeyLen = hmacIvForKey.length();
			byte[] hmacIvForKeyData = new byte[hmacIvForKeyLen/2];
			for(int i=0;i<hmacIvForKeyLen;i+=2){
				hmacIvForKeyData[i/2] = (byte)((Character.digit(hmacIvForKey.charAt(i),16) << 4) + Character.digit(hmacIvForKey.charAt(i+1), 16));
			}

			byte[] decrypedHmacKey = null;

			try{
				Cipher cp = Cipher.getInstance("AES/CBC/PKCS5Padding");
				cp.init(Cipher.DECRYPT_MODE, new SecretKeySpec(hmacIvForKeyData,"AES"), new IvParameterSpec(hmacIvForKeyData));
				decrypedHmacKey = cp.doFinal(hmacKeyData);
			} catch (Exception e) {
				logger.debug(e.getMessage());
				throw new JwtAuthException(
						String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"), "Signature verifying failed");
			}

			SecretKeySpec secretKey = new SecretKeySpec(decrypedHmacKey, "HmacSHA256");
			String madeSignature = "";

			logger.info("### secretKey:"+secretKey);

			try{
				Mac mac = Mac.getInstance("HmacSHA256");
				mac.init(secretKey);
				madeSignature= Base64.encodeBase64String(mac.doFinal(sMessage.getBytes()));
			} catch (Exception e) {
				logger.debug(e.getMessage());
				throw new JwtAuthException(String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"), "Signature verifying failed");
			}

			logger.info("### madeSignature:"+madeSignature);

			if(!StringUtils.equals(hmacSignature, madeSignature)){
				logger.info("### hmacSignature:"+hmacSignature);
				logger.info("### madeSignature:"+madeSignature);

				throw new JwtAuthException(String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"), "Signature verifying failed" );
			}
			
		} catch (JwtAuthException e) {
			logger.debug(e.getMessage());
			throw e;
		} catch (Exception e) {
			logger.debug(e.getMessage());
			throw new JwtAuthException(String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"), "Signature verifying failed(Unknown)" );
		}

		logger.info("### pass: "+"success");
				
		return message;	
		
	}
	
	/**
     * 대소문자 구분 없이 HTTP 헤더 값을 가져오는 메소드
     * @param request HTTP 요청 객체
     * @param headerName 찾고자 하는 헤더 이름
     * @return 헤더 값 또는 null
     */
    public static String getHeaderCaseInsensitive(HttpServletRequest request, String headerName) {
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String header = headerNames.nextElement();
            if (header != null && header.equalsIgnoreCase(headerName)) {
                return request.getHeader(header);
            }
        }
        return null;
    }
	

	public JsonNode parseJson(String adptGrpName, Object message) throws UnsupportedEncodingException, JsonMappingException,JsonProcessingException {
		String charset = StringUtils.defaultIfBlank(AdapterManager.getInstance().getAdapterGroupVO(adptGrpName).getMessageEncode(), "UTF-8");
		String orgMessageString;

		if(message instanceof byte[]) {
			orgMessageString = new String((byte[])message, charset);
		} else {
			orgMessageString = (String) message;
		}

		return mapper.readTree(orgMessageString);
	}
	
	@Override
	public Object doPostFilter(String adptGrpName, String adptName, Object resultMessage, Properties prop,
			HttpServletRequest request, HttpServletResponse response) throws Exception {
		return resultMessage;
	}

}
