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

import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

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.security.keyderiv.DerivedKey;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.custom.common.security.keyderiv.HsmContextSha256KeyDerivationStrategy;
import com.eactive.eai.custom.common.security.keyderiv.HsmKeyAndIvSliceDerivationStrategy;
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 GenKeyFilter 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 JSONObject) {
				jsonStr = ((JSONObject) message).toJSONString();
			} else {
				return message;
			}
			rootNode = (ObjectNode) OBJECT_MAPPER.readTree(jsonStr);

			JsonNode hsmKey = rootNode.get("hsmKey");
			JsonNode hsmIv = rootNode.get("hsmIv");
			JsonNode contextKey = rootNode.get("contextKey");
			JsonNode type = rootNode.get("type");
			
			JsonNode enc = rootNode.get("enc");
			String strEnc = "BASE64";
			if(enc != null)
				strEnc = enc.asText();
			
			byte[] binIv = null;
			if(hsmIv != null) {
				String strIvValue = hsmIv.asText();
				if(strIvValue.length() == 32||strEnc.equalsIgnoreCase("HEX"))
					binIv = HexaConverter.hexToBin(strIvValue);
				else
					binIv = Base64.getDecoder().decode(strIvValue.trim());
			}
		
			String strEncKeyValue = hsmKey.asText();
			byte[] binEncKey = null;
			if(strEnc.equalsIgnoreCase("BASE64"))
				binEncKey = Base64.getDecoder().decode(strEncKeyValue.trim());
			else
				binEncKey = HexaConverter.hexToBin(strEncKeyValue);

			DerivedKey derivedKey = null;
			if(type.asText().equals("douzone")) {
				Map<String, String> params = new HashMap<>();
				Map<String, String> runtimeContext = new HashMap<>();
				params.put("contextKey", "contextKey");
				runtimeContext.put("contextKey", contextKey.asText());
				HsmContextSha256KeyDerivationStrategy douzoneStrategy = new HsmContextSha256KeyDerivationStrategy();
				derivedKey = douzoneStrategy.deriveKey(params, runtimeContext, binEncKey);
			} else if(type.asText().equals("kakaobank")) {
				HsmKeyAndIvSliceDerivationStrategy kakaobank = new HsmKeyAndIvSliceDerivationStrategy();
				derivedKey = kakaobank.deriveKey(binEncKey, binIv);
			}
			
			JsonNode aad = rootNode.get("aad");
			if (aad != null) {
				rootNode.put("generatedAdd", HexaConverter.binToHex(aad.asText().getBytes()));
			}
			
			String encBase64 = Base64.getEncoder().encodeToString(derivedKey.getDecKey());
			logger.debug("generated KEY {} : [{}]", type.asText(), encBase64);
			rootNode.put("generatedKeyBase64", encBase64);
			rootNode.put("generatedKeyHex", HexaConverter.binToHex(derivedKey.getDecKey()));
			rootNode.put("generatedKeyRaw", new String(derivedKey.getDecKey()));
			
			if(derivedKey.getIv() != null) {
				String encBase64Iv = Base64.getEncoder().encodeToString(derivedKey.getIv());
				logger.debug("generated IV {} : [{}]", type.asText(), encBase64Iv);
				rootNode.put("generatedIvBase64", encBase64Iv);
				rootNode.put("generatedIvHex", HexaConverter.binToHex(derivedKey.getIv()));
				rootNode.put("generatedIvRaw", new String(derivedKey.getIv()));
			}
			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;
	}

}
