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

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Properties;

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

//import org.apache.commons.net.util.Base64;
import org.springframework.http.HttpStatus;
import org.apache.commons.lang3.StringUtils;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;

import com.eactive.eai.adapter.http.HttpMethodType;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceSupport;
import com.eactive.eai.authserver.service.OAuth2Manager;
import com.eactive.eai.authserver.vo.ClientVO;
import com.eactive.eai.common.TransactionContextKeys;
import com.eactive.eai.common.util.Logger;

public class HmacSha256VerifyFilterKjb implements HttpAdapterFilter  {
	
	static final String DJB_ROOTLESS_ARRAY = "{ \"DJB_ROOTLESS_ARRAY\" : ";

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

	public HmacSha256VerifyFilterKjb() {
		super();
	}

	@Override
	public Object doPreFilter(String adptGrpName, String adptName, Object message, Properties prop,
			HttpServletRequest request, HttpServletResponse response) throws Exception {

		String inboundMethod = prop.getProperty(HttpAdapterServiceKey.INBOUND_METHOD);
		String inboundUri = prop.getProperty(HttpAdapterServiceKey.INBOUND_URI);
		String inboundBody = prop.getProperty(HttpAdapterServiceKey.INBOUND_REQUEST_MESSAGE);

		try {
			String xObpPartnercode = request.getHeader("x-obp-partnercode");
			String xObpSignatureUrl = request.getHeader("x-obp-signature-url");
			String xObpSignatureBody = request.getHeader("x-obp-signature-body");
			if (StringUtils.isBlank(xObpPartnercode) 
					|| StringUtils.isBlank(xObpSignatureUrl)) {
				throw new JwtAuthException(
						String.valueOf(HttpStatus.UNAUTHORIZED.value()), 
						"Can not find mandatory Http Header");
			}
			
			if (StringUtils.isNotBlank(prop.getProperty(HttpAdapterServiceKey.INBOUND_QUERY_STRING))) {
				inboundUri = inboundUri + "?" + prop.getProperty(HttpAdapterServiceKey.INBOUND_QUERY_STRING);
				inboundBody = "";
			}
			String chkUrl = inboundMethod + "&" + inboundUri;
			String chkBody = inboundBody;
			String clientSecret = assignClientSecret(prop, request);
			
			if (chkBody.startsWith(DJB_ROOTLESS_ARRAY)) {
				chkBody = chkBody.substring(DJB_ROOTLESS_ARRAY.length());
				
				if (chkBody.endsWith("}")) {
					chkBody = chkBody.substring(0, chkBody.length() - 1);
				}
			}
			
			String macUrl = calculateHMAC(chkUrl, clientSecret);
			String macBody = calculateHMAC(chkBody, clientSecret);

			if (!StringUtils.equals(xObpSignatureUrl, macUrl)) {
				throw new JwtAuthException(
						String.valueOf(HttpStatus.UNAUTHORIZED.value()),
						"Signature verifying failed : x-obp-signature-url");
			}

			switch (HttpMethodType.getValue(request.getMethod())) {
			case GET:
			case DELETE:
				break;
			default:
				if (StringUtils.isNotBlank(inboundBody) && !StringUtils.equals("{}", inboundBody)) {
					if (!StringUtils.equals(xObpSignatureBody, macBody)) {
						throw new JwtAuthException(
								String.valueOf(HttpStatus.UNAUTHORIZED.value()),
								"Signature verifying failed : x-obp-signature-body");
					}
					break;
				}
			}

		} catch (JwtAuthException e) {
			logger.debug(e.getMessage());
			throw e;
		} catch (Exception e) {
			logger.error(e.getMessage());
			throw new JwtAuthException(
					String.valueOf(HttpStatus.UNAUTHORIZED.value()),
					"Signature verifying failed(unkown)");
		}

		return message;
	}
	
	public static String calculateHMAC(String data, String key) 
			throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
		
		Mac mac = Mac.getInstance("HmacSHA256");
		mac.init(new SecretKeySpec(key.getBytes(), "HmacSHA256"));
		return Base64.getEncoder().encodeToString(mac.doFinal(data.getBytes("UTF-8")));
	}
    
	private String assignClientSecret(Properties prop, HttpServletRequest request) throws Exception {
		String clientId = assignClientId(prop, request);
		if (StringUtils.isBlank(clientId)) {
			throw new JwtAuthException(
					String.valueOf(HttpStatus.UNAUTHORIZED.value()),
					"Can not find clientId info");
		}

		ClientVO client = (ClientVO) OAuth2Manager.getInstance().getClientDeatilsStore().get(clientId);
		if (client == null) {
			throw new JwtAuthException(
					String.valueOf(HttpStatus.UNAUTHORIZED.value()),
					"Can not find client info");
		}

		return client.getClientSecret();
	}

	private String assignClientId(Properties prop, HttpServletRequest request) {
		String clientId = null;
		try {
			// 이전 filter에서 셋팅한 clientId 확보
			clientId = prop.getProperty(HttpAdapterServiceSupport.PROPERTIES_NAME_CLIENT_ID);

			// 이전 filter에서 셋팅한 값이 없다면 header 정보에서 확보
			if (StringUtils.isBlank(clientId)) {
				clientId = request.getHeader(HttpAdapterServiceSupport.HEADER_NAME_CLIENT_ID);
			}
		} catch (Exception e) {
			logger.error(e.getMessage());
		}

		return clientId;
	}

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

		String outboundBody = (String) resultMessage;

		try {

			Object root = JSONValue.parse(outboundBody);

			if (root instanceof JSONObject && ((JSONObject) root).containsKey("DJB_ROOTLESS_ARRAY")) {
				JSONArray jsonArray = (JSONArray) ((JSONObject) root).get("DJB_ROOTLESS_ARRAY");
				outboundBody = jsonArray.toJSONString();
			}

			String clientSecret = assignClientSecret(prop, request);
			String macBody = calculateHMAC(outboundBody, clientSecret);

			response.addHeader("x-obp-signature-body", macBody);
			response.addHeader("x-obp-partnercode", request.getHeader("x-obp-partnercode"));
			response.addHeader("x-obp-txid", prop.getProperty(TransactionContextKeys.TRANSACTION_UUID,""));
		} catch (Exception e) {
			logger.error(e.getMessage());
		}

		return resultMessage;
	}
	

}
