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

import java.security.KeyStore;
import java.util.Base64;
import java.util.Properties;

import javax.crypto.SecretKey;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.json.simple.JSONObject;
import org.springframework.http.HttpStatus;

import com.eactive.eai.adapter.http.dynamic.filter.FilterException;
import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilter;
import com.eactive.eai.common.hsm.HsmManager;
import com.eactive.eai.common.security.AESCryptoModuleExtension;
import com.eactive.eai.common.security.ARIACryptoModuleExtension;
import com.eactive.eai.common.security.CryptoModuleExtension;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.util.HexaConverter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

public class EncrytTestFilter implements HttpAdapterFilter {

	private static final Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);

	private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

	@Override
	public Object doPreFilter(String adptGrpName, String adptName, Object message, Properties prop,
			HttpServletRequest request, HttpServletResponse response) throws Exception {
		try {
			ObjectNode rootNode = null;
			String jsonStr = null;
			if (message instanceof String) {
				jsonStr = (String) message;
			} else if (message instanceof ObjectNode) {
				rootNode = (ObjectNode) message;
			}  else if (message instanceof JSONObject) {
				jsonStr = ((JSONObject) message).toJSONString();
			} else {
				return message;
			}
			if(rootNode == null)
				rootNode = (ObjectNode) OBJECT_MAPPER.readTree(jsonStr);

			// 1. HSM에서 키 가져오기
			byte[] hsmKeyBytes = null;
			JsonNode hsmKeyAlias = rootNode.get("hsmKeyAlias");
			if (hsmKeyAlias != null) {
				String hsmKeyAliasValue = hsmKeyAlias.textValue();
				KeyStore keyStore = HsmManager.getInstance().getKeyStore();

				java.security.Key key = keyStore.getKey(hsmKeyAliasValue, null);
				if (key instanceof SecretKey) {
					SecretKey secretKey = (SecretKey) key;
					hsmKeyBytes = secretKey.getEncoded();
					if (hsmKeyBytes != null) {
						String hsmKeyBase64 = Base64.getEncoder().encodeToString(hsmKeyBytes);
						String hsmKeyHex = HexaConverter.binToHex(hsmKeyBytes);
						logger.debug("HsmManager] HSM - {} : [{}]", hsmKeyAliasValue, hsmKeyHex);
						rootNode.put("hsmKeyBase64", hsmKeyBase64);
						rootNode.put("hsmKeyHex", hsmKeyHex);
						rootNode.put("hsmKeyRaw", new String(hsmKeyBytes));
					} else {
						logger.debug("HsmManager] HSM - secretKey null {} : [{}]", hsmKeyAliasValue, secretKey);
					}
					
				}
			}

			// 2. 암호화 파라미터가 있으면 암호화 후 복호화 검증
			JsonNode alg = rootNode.get("alg");
			JsonNode mode = rootNode.get("mode");
			JsonNode padding = rootNode.get("padding");
			
			JsonNode enc = rootNode.get("enc");
			String strEnc = "BASE64";
			if(enc != null)
				strEnc = enc.asText();
				
			JsonNode ivNode = rootNode.get("iv");
			JsonNode aadNode = rootNode.get("aad");
			JsonNode encKeyNode = rootNode.get("encKey");
			JsonNode dataNode = rootNode.get("data");

			if (alg != null && mode != null && padding != null && encKeyNode != null
					&& dataNode != null) {
				byte[] binIv = null;
				if(ivNode != null) {
					String strIvValue = ivNode.asText();
					if(strIvValue.length() == 32||strEnc.equalsIgnoreCase("HEX"))
						binIv = HexaConverter.hexToBin(strIvValue);
					else
						binIv = Base64.getDecoder().decode(strIvValue.trim());
				}
				
				String strEncKeyValue = encKeyNode.asText();
				byte[] binEncKey = null;
				if(strEnc.equalsIgnoreCase("BASE64"))
					binEncKey = Base64.getDecoder().decode(strEncKeyValue.trim());
				else
					binEncKey = HexaConverter.hexToBin(strEncKeyValue);

				CryptoModuleExtension ext = "ARIA".equalsIgnoreCase(alg.asText()) ? new ARIACryptoModuleExtension()
						: new AESCryptoModuleExtension();
				ext.init(alg.asText(), mode.asText(), padding.asText(), binIv, binEncKey, binEncKey);

				byte[] plainBytes = dataNode.asText().getBytes();

				// 암호화
				byte[] encryptedBytes = null;
				if(rootNode.has("aad"))
					encryptedBytes = ext.encrypt(plainBytes, HexaConverter.hexToBin(aadNode.asText()));
				else
					encryptedBytes = ext.encrypt(plainBytes);
				
				String encryptedBase64 = Base64.getEncoder().encodeToString(encryptedBytes);
				rootNode.put("encryptedData", encryptedBase64);
				logger.debug("암호화 결과: [{}]", encryptedBase64);

				// 복호화 검증
				byte[] decryptedBytes = null;
				if(rootNode.has("aad"))
					decryptedBytes = ext.decrypt(encryptedBytes, HexaConverter.hexToBin(aadNode.asText()));
				else
					decryptedBytes = ext.decrypt(encryptedBytes);
				
				rootNode.put("decryptedVerify", new String(decryptedBytes));
				logger.debug("복호화 검증: [{}]", new String(decryptedBytes));
			}

			String jsonString = OBJECT_MAPPER.writeValueAsString(rootNode);
			return jsonString;

		} catch (Exception e) {
			throw new FilterException("HSM key 암호화/복호화 검증 실패", ERROR_PRE_FAIL, HttpStatus.INTERNAL_SERVER_ERROR.value(), e);
		}
	}

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

}
