package com.eactive.eai.adapter.service;

import com.eactive.eai.adapter.AdapterGroupVO;
import com.eactive.eai.adapter.AdapterPropManager;
import com.eactive.eai.adapter.AdapterVO;
import com.eactive.eai.adapter.Keys;
import com.eactive.eai.adapter.http.HttpMemoryLogger;
import com.eactive.eai.adapter.http.HttpMethodType;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceSupport;
import com.eactive.eai.adapter.http.dynamic.filter.JwtAuthFilter;
import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.message.MessageType;
import com.eactive.eai.common.util.CommonLib;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.inbound.action.ActionFactory;
import com.eactive.eai.inbound.action.RequestAction;
import com.eactive.eai.inbound.processor.Processor;
import com.eactive.eai.message.StandardMessageUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.mina.common.ByteBuffer;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.json.simple.parser.JSONParser;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.util.AntPathMatcher;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLDecoder;
import java.util.*;

//for encrypt/decrypt jwhong
import com.eactive.eai.authserver.service.OAuth2Manager;
import com.kjbank.encrypt.exchange.crypto.AES256Cipher;
import com.kjbank.encrypt.exchange.crypto.AESCipher;
import com.kjbank.encrypt.exchange.crypto.AES256GCMCipher;
import java.nio.charset.StandardCharsets;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import org.springframework.security.oauth2.provider.ClientDetails;
import com.nimbusds.jwt.SignedJWT;
import com.eactive.eai.common.context.ElinkTransactionContext;


@Service
public class ApiAdapterService extends HttpAdapterServiceSupport {
    static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);

    public static final String HEADER_GROUP = "HEADER_GROUP";
	public static final String HTTP_STATUS = "HTTP_STATUS";   // jwhong
    // HEADER_GROUP JSON에 추가할 항목 정의, 없으면 전체 header 추가
    public static final String HEADER_KEYS = "HEADER_KEYS";
    public static final String PROPERTIES_NAME_HTTP_REQUEST_METHOD = "httpRequestMethod";

    private static final String JSON_CONTENT_TYPE = "application/json";
    private static final String JSON_FIELD_NAME = "json-body";
    private static final String FILE_GROUP_NAME = "image-file";
    private static final String UPLOAD_ROOT_PATH = "UPLOAD_ROOT_PATH";
	public static final String PAYLOAD_PARAM_NAME_CLIENT_ID = "client_id";  // jwhong
	private boolean encryptResponseApply;   // inbound에 대한 응답 암호화 여부 flag
    
    public String callApi(HttpServletRequest request, HttpServletResponse response, Properties httpProp, AdapterGroupVO adapterGroupVO, AdapterVO adapterVO, Properties transactionProp) throws Exception {
        int traceLevel = 0;

        AdapterPropManager manager = null;
        String adptGrpName = adapterGroupVO.getName();
        String adptName = adapterVO.getName();

        String urlDecodeYn = httpProp.getProperty(URL_DECODE_YN, "N");
        String encode = StringUtils.defaultIfBlank(adapterGroupVO.getMessageEncode(), "UTF-8");

        String traceLevelTemp = httpProp.getProperty(TRACE_LEVEL, "0");
        String relayRequestHeaderKeys = httpProp.getProperty(HEADER_KEYS);
        String headerGroupName = httpProp.getProperty(HEADER_GROUP);

        boolean isParameterType = false;
        String message = null;

        String paramValue = null;
        String adptMsgType = null;
        
        transactionProp.put(INBOUND_METHOD, request.getMethod());
        transactionProp.put(INBOUND_URI, request.getRequestURI());
        transactionProp.put(INBOUND_HEADER, getHeaders(request));
        transactionProp.put(INBOUND_EXTPARAMS, StringUtils.defaultString(request.getQueryString()));
        if (StringUtils.equals(adapterVO.getAdapterGroupVO().getType(), Keys.TYPE_REST)
                || StringUtils.equals(adapterVO.getAdapterGroupVO().getType(), Keys.TYPE_HTTP_CUSTOM)) {
            // /api/v1/public/getUserInfo.svc
            String extUrl = StringUtils.removeStart(request.getRequestURI(), request.getContextPath());
            transactionProp.put(INBOUND_EXTURI, extUrl);
        } else {
            transactionProp.put(INBOUND_EXTURI, getExtUri(request)); 
        }
        transactionProp.put(INBOUND_CLIENT_IP, getClientIp(request));  // Client IP 추가
        transactionProp.put(Processor.REQUEST_ACTION, adapterVO.getAdapterGroupVO().getRefClass());
        transactionProp.put(API_PATH, httpProp.getProperty(API_PATH, ""));
        transactionProp.put(PRE_FILTERS, httpProp.getProperty(PRE_FILTERS, ""));
        transactionProp.put(POST_FILTERS, httpProp.getProperty(POST_FILTERS, ""));
        transactionProp.put(PROPERTIES_NAME_HTTP_REQUEST_METHOD, request.getMethod());
        transactionProp.put(ALLOW_IP, httpProp.getProperty(ALLOW_IP, ""));
        
        transactionProp.put(ENC_PATHS, httpProp.getProperty(ENC_PATHS, ""));
        
        transactionProp.put(ENCRYPT_ALGORITHM, httpProp.getProperty(ENCRYPT_ALGORITHM, ""));  //jwhong
        if (getHeaders(request).get(HEADER_NAME_CLIENT_ID) != null) {    // jwhong
        	transactionProp.put(HEADER_NAME_CLIENT_ID, getHeaders(request).get(HEADER_NAME_CLIENT_ID));  
        }
        transactionProp.put(ENCRYPT_BASE_TYPE, httpProp.getProperty(ENCRYPT_BASE_TYPE, ""));  //jwhong
//        //transactionProp.put(INBOUND_TOKEN, getHeaders(request).get(INBOUND_TOKEN));  // jwhong
//        transactionProp.put(INBOUND_TOKEN, getHeaders(request).get(INBOUND_TOKEN) != null ? getHeaders(request).get(INBOUND_TOKEN) : "");  //jwhong , null 이면 default로 "" put
        String inboundToken = getHeaders(request).getOrDefault(INBOUND_TOKEN, "").toString();
        if (StringUtils.isNotBlank(inboundToken) && StringUtils.startsWith(inboundToken, "Bearer ")) {
        	inboundToken = inboundToken.substring(7);
        }
        transactionProp.put(INBOUND_TOKEN, inboundToken);
        transactionProp.put(ENCRYPT_AES256_IV, httpProp.getProperty(ENCRYPT_AES256_IV, ""));  //jwhong
        transactionProp.put(ENCRYPT_AES256_KEY, httpProp.getProperty(ENCRYPT_AES256_KEY, ""));  //jwhong
      
        // SEED 컬럼암호하 시  Key로 사용함
        String seedkey = getHeaders(request).getOrDefault("x-obp-partnercode", "").toString();
        ElinkTransactionContext.setSeedKey(seedkey);

        try {
            traceLevel = Integer.parseInt(traceLevelTemp);
        } catch (Exception e) {
            traceLevel = 0;
        }

        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        logger.debug("시작 >> encode = [" + encode + "]");

        switch (HttpMethodType.getValue(request.getMethod())) {
            case GET:
            case DELETE:
                isParameterType = true;
                break;
            case POST:
            case PUT:
                if (StringUtils.contains(request.getContentType(), "application/x-www-form-urlencoded")) {
                    isParameterType = true;
                } else if ( StringUtils.isNoneBlank(request.getQueryString()) )  { // jwhong
                	isParameterType = true;
                } else {
                    isParameterType = false;
                }
                break;
            default:
                break;
        }

        if (isParameterType) {
            paramValue = request.getQueryString();
            transactionProp.put(INBOUND_QUERY_STRING, StringUtils.defaultString(paramValue));  // Filter에서 QueryString 검증을 위해 저장 
            if (paramValue == null)
                paramValue = "";
            if (traceLevel >= 3) {
                HttpMemoryLogger.txlog(adptGrpName + adptName,
                        "RECV " + "[" + paramValue + "]" + CommonLib.getDumpMessage(paramValue));
            }

            // json으로 변환
            StringBuilder sb = new StringBuilder();
            sb.append("{");
            Map<String, String[]> paramMap = assignParameterMap(request, adptGrpName, adptName, null, transactionProp);
            int i = 0;
            for (Map.Entry<String, String[]> entry : paramMap.entrySet()) {
                if (i > 0) {
                    sb.append(",");
                }
                sb.append("\"").append(entry.getKey()).append("\":");
                String[] values = entry.getValue();
                if (values.length > 1) {
                    // ["111", "222"]
                    sb.append("[");
                    for (int j = 0; j < values.length; j++) {
                        if (j > 0) {
                            sb.append(",");
                        }
                        sb.append("\"").append(JSONValue.escape(values[j])).append("\"");
                    }
                    sb.append("]");
                } else {
                    sb.append("\"").append(JSONValue.escape(values[0])).append("\"");
                }

                i++;
            }

            sb.append("}");

            paramValue = sb.toString();
            
            if (logger.isDebug()) {
                logger.debug("HttpAdapterServiceRest] RECV (" + adptGrpName + ") = [" + paramValue + "]\n"
                        + CommonLib.getDumpMessage(paramValue.getBytes(encode)));
            }
	} else {
	    if (request.getContentLength() > 0) {
		ServletInputStream sis = request.getInputStream();
		ByteBuffer bb = ByteBuffer.allocate(1024).setAutoExpand(true);
		int i = 0;
		byte[] cbuf = new byte[1024];
		while ((i = sis.read(cbuf, 0, 1024)) != -1) {
		    if (i == 1024) {
			bb.put(cbuf);
		    } else {
			byte[] tail = new byte[i];
			System.arraycopy(cbuf, 0, tail, 0, i);
			bb.put(tail);
		    }
		}
		byte[] data = new byte[bb.position()];
		bb.position(0);
		bb.get(data);
		paramValue = new String(data, encode);

		if (traceLevel >= 3) {
		    HttpMemoryLogger.txlog(adptGrpName + adptName,
			    "RECV " + "[" + paramValue + "]" + CommonLib.getDumpMessage(data));
		}

		if (logger.isDebug()) {
		    logger.debug("HttpAdapterServiceRest] RECV (" + adptGrpName + ") = [" + paramValue + "]\n"
			    + CommonLib.getDumpMessage(data));
		}
	    }
	}

        if (paramValue == null) { // parameter가 없는 경우때문에 처리
            paramValue = "";
        }
        // 순수한 Body값을 저장을 위해 위치 변경.
        transactionProp.put(INBOUND_REQUEST_MESSAGE, paramValue);

        // paramValue가 null이 아닌 빈문자열(""," ")인 경우에 대비하여 조건 수정.
        if (StringUtils.isNotBlank(paramValue)) {      // jwhong decrypt
        	paramValue = doPreDecryption(paramValue, transactionProp, request);
        }

        if ("Y".equals(urlDecodeYn) && isParameterType) {
            message = URLDecoder.decode(paramValue);
        } else {
            message = paramValue;
        }

        if (logger.isDebug()) {
            String[] msgArgs = new String[2];
            msgArgs[0] = adptGrpName;
            msgArgs[1] = message;
            String resMsg = ExceptionUtil.make("RICEAIAHA005", msgArgs);
            logger.debug(resMsg);
        }


        adptMsgType = adapterVO.getAdapterGroupVO().getMessageType();
//        HttpHeaders responseHeaders = new HttpHeaders();
//        responseHeaders.setContentType(MediaType.valueOf("application/json;charset=" + encode));


        // HEADER_GROUP 셋팅
        if (MessageType.JSON.equals(adptMsgType) && StringUtils.isNotBlank(headerGroupName)
                && StringUtils.isNotBlank(relayRequestHeaderKeys)) {
            JSONObject jsonMessage = (JSONObject) JSONValue.parse(message);
            JSONObject headerJson = new JSONObject();
            if (StringUtils.equalsIgnoreCase(relayRequestHeaderKeys, "ALL")) {
                for (Enumeration<String> e = request.getHeaderNames(); e.hasMoreElements(); ) {
                    String key = e.nextElement();
                    headerJson.put(key, request.getHeader(key));
                }
            } else {
                String[] relayKeyArr = org.springframework.util.StringUtils
                        .tokenizeToStringArray(relayRequestHeaderKeys, ",");

                for (String key : relayKeyArr) {
                    String headerValue = request.getHeader(key);
                    if (StringUtils.isNotBlank(headerValue)) {
                        headerJson.put(key, headerValue);
                    }
                }
            }
            
            if (headerJson.size() > 0) {
                jsonMessage.put(headerGroupName, headerJson);
                message = jsonMessage.toJSONString();
            }
        }

        if (message == null) {
            message = "";
        }

        // 위치변경 : 가공되지 않은 Body값을 저장하기 위하여 위쪽으로 이동.
//        transactionProp.put(INBOUND_REQUEST_MESSAGE, message);
        // 로컬 서비스 호출 ,encoding 처리 추가
        String result = (String) service(adptGrpName, adptName, message, transactionProp, request, response);

        if (logger.isDebug()) {
            logger.debug("HttpAdapterServiceRest] result " + encode + " (" + adptGrpName + ") = [" + result + "]");
        }

        stopWatch.stop();

        String syncAsyncType = transactionProp.getProperty(INBOUND_SYNC_ASYNC_TYPE);

        if (!"ASYN".equals(syncAsyncType) && MessageType.JSON.equals(adptMsgType) && StringUtils.isNotBlank(headerGroupName)) {
        	ObjectMapper mapper = new ObjectMapper();
        	ObjectNode rootNode = (ObjectNode) mapper.readTree(result);
        	JsonNode headerGroup = rootNode.get(headerGroupName);
        	if(headerGroup != null) {
        		for(Iterator<String> it = headerGroup.fieldNames(); it.hasNext();) {
    				String name = it.next();
    				String value = headerGroup.get(name).asText();
    				response.addHeader(name, value);
        		}
        		rootNode.remove(headerGroupName);
        		result = mapper.writeValueAsString(rootNode);
        	}
        }
                        
        // KJBank는 요청에 대한 응답 (Sync응답, Aync 에 대한 Ack응답) 에 대하여 암호화 하지 않는다. 무조건 안한다. 따라서 아래부분은 구현은 했지만 사용하지 않는다.   
        // 위 2가지 경우 암호화 하는걸로 요청 변경되어 아래 암호화 수행하도록 수정함
    	encryptResponseApply = StringUtils.equalsIgnoreCase(httpProp.getProperty("ENCRYPT_RESPONSE_APPLY", "N"), "Y");
		if ( encryptResponseApply)  {
			result = doPostEncryption(result, transactionProp, request);   // jwhong Encrypt
		}
        return result;
    }
    
    
    // jwhong decrypt

    private String doPreDecryption(String eaiBody, Properties transactionProp, HttpServletRequest request) throws Exception {
    	String decryptAlgorithm = transactionProp.getProperty(ENCRYPT_ALGORITHM, "");
    	String xElinkClientId = transactionProp.getProperty(HEADER_NAME_CLIENT_ID, "");   // 이 값이 OAuth의 client id 값이다 . http header 에 포함되어 온다. jwhong
		String encryptBaseKeyType = transactionProp.getProperty(ENCRYPT_BASE_TYPE);   
    	//String inboundToken2 = transactionProp.getProperty(INBOUND_TOKEN, "");   // 이건 token 값이 아니고 authorization 값이다
		String inboundToken = "";
    	
		String secretAES256Iv = transactionProp.getProperty("ENCRYPT_AES256_IV");  
		String secretAES256Key = transactionProp.getProperty("ENCRYPT_AES256_KEY");  
		
    	if ( decryptAlgorithm == "" ) {   // 복호화 대상이 아님
    		return eaiBody;
    	}

    	inboundToken = JwtTokenExtractor(request);
    	xElinkClientId = JwtClientIdExtractor(inboundToken);   // token에서 client id를 추출함
    	
    	// 암호화 key를 token 내용으로 할지 client secret 내용으로 할지 결정함. adapter property에 정의함
    	// ENCRYPT_ALGORITHM 을 설정했는데 ENCRYPT_BASE_TYPE을 설정안하면 Decrypt 수행시 Exception 발생함
    	String clientSecret = "";
    	if ("TOKEN".equalsIgnoreCase(encryptBaseKeyType)) {
    		clientSecret  = inboundToken;
    	} else if ( "SECRETKEY".equalsIgnoreCase(encryptBaseKeyType)) {
        	OAuth2Manager manager = OAuth2Manager.getInstance();
        	ClientDetails clientDetail = manager.getClientDeatilsStore().get(xElinkClientId);

        	if ( clientDetail != null ) {   // 여기서 not null 이라는 것은 apim oauth server에서 발급된 token 임.
        		                            // 즉 KJBank 에서 요청이 들어온 것이다.
        		clientSecret = clientDetail.getClientSecret();
        	}   // 그럼 업체에서 요청이 들어오면?? 업체도 apim oauth server에서 발급된 token을 사용하는것이다.
    	} 
    	
    	byte[] decryptedMessage = null;
    	decryptedMessage = doInboundPreDecrypt(eaiBody, decryptAlgorithm,clientSecret,secretAES256Iv, secretAES256Key );
    	
		if (decryptedMessage == null) {   
			throw new Exception("[ApiAdapterService] Decrypt Error : Invalid encrypted message");
		}
    	
    	
    	String resultMessage = new String(decryptedMessage, StandardCharsets.UTF_8 );
    	return resultMessage;
    	//return new String(decryptedMessage, StandardCharsets.UTF_8 );
    }
    
    
    // jwhong encrypt
    public String doPostEncryption(String eaiBody, Properties transactionProp, HttpServletRequest request) throws Exception {
		
    	String decryptAlgorithm = transactionProp.getProperty(ENCRYPT_ALGORITHM, "");
    	String xElinkClientId = transactionProp.getProperty(HEADER_NAME_CLIENT_ID, "");   // 이 값이 OAuth의 client id 값이다 . http header 에 포함되어 온다. jwhong
		String encryptBaseKeyType = transactionProp.getProperty(ENCRYPT_BASE_TYPE);   
		String inboundToken = "";
    	
		String secretAES256Iv = transactionProp.getProperty("ENCRYPT_AES256_IV");  
		String secretAES256Key = transactionProp.getProperty("ENCRYPT_AES256_KEY");  
		  	
    	if ( decryptAlgorithm == "" ) {   // 복호화 대상이 아님
    		return eaiBody;
    	}
    	
    	inboundToken = JwtTokenExtractor(request);
    	xElinkClientId = JwtClientIdExtractor(inboundToken);   // token에서 client id를 추출함

    	// 암호화 key를 token 내용으로 할지 client secret 내용으로 할지 결정함. adapter property에 정의함
    	// ENCRYPT_ALGORITHM 을 설정했는데 ENCRYPT_BASE_TYPE을 설정안하면 Decrypt 수행시 Exception 발생함
    	String clientSecret = "";
    	if ("TOKEN".equalsIgnoreCase(encryptBaseKeyType)) {
    		clientSecret  = inboundToken;
    	} else if ( "SECRETKEY".equalsIgnoreCase(encryptBaseKeyType)) {
        	OAuth2Manager manager = OAuth2Manager.getInstance();
        	ClientDetails clientDetail = manager.getClientDeatilsStore().get(xElinkClientId);

        	if ( clientDetail != null ) {
        		clientSecret = clientDetail.getClientSecret();
        	}
    	} 
    	
    	String encryptedMessage = null;
    	encryptedMessage = doInboundPostEncrypt(eaiBody, decryptAlgorithm,clientSecret,secretAES256Iv, secretAES256Key );
    	
		if (encryptedMessage == null) {   
			throw new Exception("[ApiAdapterService] Encrypt Error : Invalid PlainText message");
		}
		
		return encryptedMessage;
    	//String resultMessage = new String(encryptedMessage, StandardCharsets.UTF_8 );
    	//return resultMessage;
    }
    

 	private byte[] convertObjectToBytes(Object obj) throws IOException {
 	    ByteArrayOutputStream bos = new ByteArrayOutputStream();
 	    ObjectOutputStream oos = new ObjectOutputStream(bos);
 	    oos.writeObject(obj);
 	    oos.flush();
 	    return bos.toByteArray();
 	}
 	
 	private byte[] convertObjectToBase64Bytes(Object obj) throws IOException {
 	    ByteArrayOutputStream bos = new ByteArrayOutputStream();
 	    ObjectOutputStream oos = new ObjectOutputStream(bos);
 	    oos.writeObject(obj);
 	    oos.flush();
 	    oos.close();

 	    // 직렬화된 byte[] → Base64 문자열 → 다시 byte[]로 변환
 	    String base64String = Base64.getEncoder().encodeToString(bos.toByteArray());
 	    return base64String.getBytes(StandardCharsets.UTF_8);
 	}
 	
 	public static Object convertBytesToObject(byte[] bytes) throws IOException, ClassNotFoundException {
 	    try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
 	         ObjectInputStream ois = new ObjectInputStream(bis)) {
 	        return ois.readObject();
 	    }
 	}
 	
 	
 	private byte[] doInboundPreDecrypt(String eaiStringBody, String decryptAlgorithm, String clientSecretKey, String secretAES256Iv, String secretAES256Key) throws Exception {
 	      
 		byte[] decryptedMessage = null;
 		//String eaiStringBody = new String(eaiBody, StandardCharsets.UTF_8);
 		
 		eaiStringBody = extractBodyMsg(decryptAlgorithm, eaiStringBody);
 		
		if (eaiStringBody == null) {   
			throw new Exception("[ApiAdapterService] Cannot decrypt Error : input is plain text");
		}
 		
 		//test source
 		logger.debug("Base64 문자열: " + eaiStringBody);
 		logger.debug("Base64 문자열 길이: " + eaiStringBody.length());
 		logger.debug("Base64 문자열 끝: " + eaiStringBody.substring(eaiStringBody.length() - 10));

 		// Base64 디코딩 테스트
 		try {
 		    byte[] decoded = Base64.getDecoder().decode(eaiStringBody);
 		    logger.debug("디코딩 성공, 길이: " + decoded.length);
 		} catch (IllegalArgumentException e) {
 		    logger.debug("Base64 디코딩 실패: " + e.getMessage());
 		}
         // end test source
 	
 		switch (decryptAlgorithm) {
 		case "AES128":	
 		case "AES128-TOSS":			
 		    try {
 	 		    String key128 = clientSecretKey.substring(22,38);   //togetherEncoder 경우는 substring(44,60) 이네?? 
 		    	//String decryptedStrMessage = AESCipher.decrypt(eaiStringBody, key128);
 		    	String decryptedStrMessage = AESCipher.decryptExistException(eaiStringBody, key128);
 		    	decryptedMessage =  decryptedStrMessage.getBytes(StandardCharsets.UTF_8);
 		    } catch (Exception e) {
 		        e.printStackTrace();
 		        logger.error("HttpClientAdapterServiceRest] AES128 Decryption error=" + e.getMessage());
 		    }
 		    break; 
 		case "AES128-TOGETHER":			
 		    try {
 		    	String key128 = clientSecretKey.substring(44,60);   //togetherEncoder 경우는 substring(44,60) 이네?? 
 		    	//String decryptedStrMessage = AESCipher.decrypt(eaiStringBody, key128);
 		    	String decryptedStrMessage = AESCipher.decryptExistException(eaiStringBody, key128);
 		    	decryptedMessage =  decryptedStrMessage.getBytes(StandardCharsets.UTF_8);
 		    } catch (Exception e) {
 		        e.printStackTrace();
 		        logger.error("HttpClientAdapterServiceRest] AES128 Decryption error=" + e.getMessage());
 		    }
 		    break;
 		case "AES256":
 			try {
 	 			String key = clientSecretKey.substring(10,42);
 				String decryptedStrMessage = AES256Cipher.decrypt(eaiStringBody, key);
 				decryptedMessage =  decryptedStrMessage.getBytes(StandardCharsets.UTF_8);
 			} catch (Exception e) {
 		        e.printStackTrace();
 		        logger.error("HttpClientAdapterServiceRest] AES256 Decryption error=" + e.getMessage());
 			} 	
 			break;
 		case "AES256-KAKAO":
 			try {
 				String decryptedStrMessage = AES256Cipher.decrypt(eaiStringBody, secretAES256Iv, secretAES256Key);
 				decryptedMessage =  decryptedStrMessage.getBytes(StandardCharsets.UTF_8);
 			} catch (Exception e) {
 		        e.printStackTrace();
 		        logger.error("HttpClientAdapterServiceRest] AES256 Decryption error=" + e.getMessage());
 			} 	
 			break;
 		case "AES256-TOSS":
 			try {
 				String decryptedStrMessage = AES256Cipher.decrypt(eaiStringBody, secretAES256Iv, secretAES256Key);
 				decryptedMessage =  decryptedStrMessage.getBytes(StandardCharsets.UTF_8);
 			} catch (Exception e) {
 		        e.printStackTrace();
 		        logger.error("HttpClientAdapterServiceRest] AES256 Decryption error=" + e.getMessage());
 			} 	
 			break;
 			
		case "AES256GCM":
			try {
				byte[] key = AES256GCMCipher.generateKey();
				AES256GCMCipher cipher = new AES256GCMCipher(key);
				byte[] aad = "metadata".getBytes();
				
				String decryptedStrMessage = cipher.decrypt(eaiStringBody , aad);
				decryptedMessage =  decryptedStrMessage.getBytes(StandardCharsets.UTF_8);
				
			} catch (Exception e) {
		        e.printStackTrace();
		        logger.error("HttpClientAdapterServiceRest] AES256GCM Encryption error=" + e.getMessage());
			} 		 			
			break;
 		default:
 			try {
 				decryptedMessage = eaiStringBody.getBytes();
 			} catch (Exception e) {
 		        e.printStackTrace();
 		        logger.error("HttpClientAdapterServiceRest] doOutboundPostFilter Array copy error=" + e.getMessage());
 			} 		 			
 			break;
 		}
 		
 		return decryptedMessage;		
 	}
   
	private String extractBodyMsg(String decryptAlgorithm, String eaiStringBody) {

		String decryptedJsonKey = "";
		String decryptedBodyMessage = "";
		
		decryptedJsonKey = getJsonKey(decryptAlgorithm);
		
		try {
			JSONParser parser = new JSONParser();
			JSONObject jsonObject = (JSONObject) parser.parse(eaiStringBody);
			decryptedBodyMessage = (String) jsonObject.get(decryptedJsonKey);	
		} catch (Exception e) {
	        e.printStackTrace();
	        logger.error("HttpClient5AdapterServiceRest] extractBodyMsg error : " + e.getMessage());
		}
	
		return decryptedBodyMessage;
		
	}
	
	private String getJsonKey(String Algorithm) {

		String jsonKey = "";

		switch (Algorithm) {
			case "AES128":
			case "AES128-TOSS":
				jsonKey = "preScreeningRequest";
				break;
			case "AES128-TOGETHER":
				jsonKey = "obpTxData";
				break;
			case "AES256":
				jsonKey = "preScreeningRequest";
				break;
			case "AES256-KAKAO":
				jsonKey = "encrypted_data";
				break;
			case "AES256-TOSS":
				jsonKey = "encryptedData";
				break;
			case "AES256-NICEON":
				jsonKey = "enc_data";
				break;
			case "AES256GCM":
				break;
			default:
				break;
		}
		return jsonKey;
	}
 	
 	private String doInboundPostEncrypt(String eaiStringBody, String encryptAlgorithm, String clientSecretKey, String secretAES256Iv, String secretAES256Key) {
	      
 		//byte[] encryptedMessage = null;
 		String encryptedStrMessage = null;
 		String encryptedJsonKey = "";

 		encryptedJsonKey = getJsonKey(encryptAlgorithm);
 			
 		switch (encryptAlgorithm) {
 		case "AES128":		
 		case "AES128-TOSS":			
 		    try {
 	 		    String key128 = clientSecretKey.substring(22,38);   //togetherEncoder 경우는 substring(44,60) 이네?? 
 		    	encryptedStrMessage = AESCipher.encrypt(eaiStringBody, key128);
 		    	//encryptedMessage =  encryptedStrMessage.getBytes(StandardCharsets.UTF_8);
 		    } catch (Exception e) {
 		        e.printStackTrace();
 		        logger.error("HttpClientAdapterServiceRest] AES128 Encryption error=" + e.getMessage());
 		    }
 		    break;
 		case "AES128-TOGETHER":			
 		    try {
 		    	String key128 = clientSecretKey.substring(44,60);   //togetherEncoder 경우는 substring(44,60) 이네?? 
 		    	encryptedStrMessage = AESCipher.encrypt(eaiStringBody, key128);
 		    	//encryptedMessage =  encryptedStrMessage.getBytes(StandardCharsets.UTF_8);
 		    } catch (Exception e) {
 		        e.printStackTrace();
 		        logger.error("HttpClientAdapterServiceRest] AES128 Encryption error=" + e.getMessage());
 		    }
 		    break;
 		case "AES256":
 			try {
 	 			String key = clientSecretKey.substring(10,42);
 				encryptedStrMessage = AES256Cipher.encrypt(eaiStringBody, key);
 				//encryptedMessage =  encryptedStrMessage.getBytes(StandardCharsets.UTF_8);
 			} catch (Exception e) {
 		        e.printStackTrace();
 		        logger.error("HttpClientAdapterServiceRest] AES256 Encryption error=" + e.getMessage());
 			} 	
 			break;
 		case "AES256-KAKAO":
 			try {
 				encryptedStrMessage = AES256Cipher.encrypt(eaiStringBody, secretAES256Iv, secretAES256Key);
 				//encryptedMessage =  encryptedStrMessage.getBytes(StandardCharsets.UTF_8);
 			} catch (Exception e) {
 		        e.printStackTrace();
 		        logger.error("HttpClientAdapterServiceRest] AES256 Encryption error=" + e.getMessage());
 			} 	
 			break;
 		case "AES256-TOSS":
 			try {
 				encryptedStrMessage = AES256Cipher.encrypt(eaiStringBody, secretAES256Iv, secretAES256Key);
 				//encryptedMessage =  encryptedStrMessage.getBytes(StandardCharsets.UTF_8);
 			} catch (Exception e) {
 		        e.printStackTrace();
 		        logger.error("HttpClientAdapterServiceRest] AES256 Encryption error=" + e.getMessage());
 			} 	
 			break;
 			
		case "AES256GCM":
			try {
				byte[] key = AES256GCMCipher.generateKey();
				AES256GCMCipher cipher = new AES256GCMCipher(key);
				byte[] aad = "metadata".getBytes();
				
				encryptedStrMessage = cipher.encrypt(eaiStringBody , aad);
				//encryptedMessage =  encryptedStrMessage.getBytes(StandardCharsets.UTF_8);
				
			} catch (Exception e) {
		        e.printStackTrace();
		        logger.error("HttpClientAdapterServiceRest] AES256GCM Encryption error=" + e.getMessage());
			} 		 			
			break;
 		default:
 			try {
 				encryptedStrMessage = eaiStringBody;
 				//encryptedMessage = eaiStringBody.getBytes();
 			} catch (Exception e) {
 		        e.printStackTrace();
 		        logger.error("HttpClientAdapterServiceRest] doOutboundPostFilter Array copy error=" + e.getMessage());
 			} 		 			
 			break;
 		}
 		
		Map<String, Object> map = new HashMap<>();
		map.put(encryptedJsonKey, encryptedStrMessage);
		JSONObject json = new JSONObject();
		json.putAll(map);
		return json.toJSONString();
 		//return encryptedMessage;		
 	}
 	
 	
 	
 	private String JwtTokenExtractor(HttpServletRequest request) {

 		String Token = "";
 		try {
 			Token = JwtAuthFilter.extractJWTToken(request);
		} catch (Exception e) {
			logger.error(e.getMessage());
		}
 		
 		logger.debug("Token is: " + Token);
 		return Token;

 	}
 	
 	private String JwtClientIdExtractor(String inboundToken) {

 		String tokenClientId ="";
 		try {
 	 		SignedJWT signedJWT = SignedJWT.parse(inboundToken);
 	 		tokenClientId = (String) signedJWT.getJWTClaimsSet().getClaim(PAYLOAD_PARAM_NAME_CLIENT_ID);			
		} catch (Exception e) {
			logger.error(e.getMessage());
			tokenClientId = "";
		}
 		
 		logger.debug("Token Client ID: " + tokenClientId);
 		return tokenClientId;

 	}
 	
    // jwhong until here


    private Map<String, String[]> assignParameterMap(HttpServletRequest request, String adptGrpName, String adptName,
                                                     Object requestBytes, Properties prop) {
        // PathVariable 체크
        if (StringUtils.equalsAnyIgnoreCase(request.getMethod(), HttpMethod.GET.name(), HttpMethod.DELETE.name())
                && StringUtils.isBlank(request.getQueryString())) {
            try {
                String actionName = prop.getProperty(Processor.REQUEST_ACTION);
                RequestAction action = ActionFactory.createAction(actionName);
                action.setAdapterInfo(adptGrpName, adptName, prop);
                String[] keys = action.perform(requestBytes);
                String requestPath = keys[0];

                // PathVariable 지원 추가
                String ruledPath = StandardMessageUtil.getMatchedKey(requestPath, actionName);
                if (!StringUtils.equals(requestPath, ruledPath) && StringUtils.contains(ruledPath, "{")) {
                    Map<String, String> paramMap = new AntPathMatcher().extractUriTemplateVariables(ruledPath,
                            requestPath);
                    if (paramMap != null && paramMap.size() > 0) {
                        Map<String, String[]> returnMap = new HashMap<>();
                        for (String key : paramMap.keySet()) {
                            if (StringUtils.equalsIgnoreCase(key, "method")) {
                                continue;
                            }
                            returnMap.put(key, new String[] { paramMap.get(key) });
                        }

                        return returnMap;
                    }
                }
            } catch (Exception e) {
                logger.error(e.getMessage());
            }
        }

        return request.getParameterMap();
    }

    private Properties getHeaders(HttpServletRequest request) {
        Properties prop = new Properties();

        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String key = headerNames.nextElement();
            String value = request.getHeader(key);
            prop.setProperty(key, value);
        }

        return prop;
    }

    private String getExtUri(HttpServletRequest request) {
        String orgUri = request.getRequestURI().replaceAll(request.getContextPath(), "");
        String uri = getExtUri(orgUri, 3);
        if (uri != null && uri.trim().length() > 0) {
            return "/" + uri;
        } else {
            return "";
        }
    }

    private static String getExtUri(String url, int length) {
        String[] urls = url.split("/");
        List<String> newUrls = new ArrayList<>();
        Collections.addAll(newUrls, urls);
        return StringUtils.join(newUrls.subList(length, urls.length).toArray(), "/");
    }

    @Override
    @Deprecated
    public void service(String adptGrpName, String adptName, HttpServletRequest request, HttpServletResponse response) {

    }

    /**
     * HTTP 요청에서 클라이언트 IP를 추출합니다.
     * X-Forwarded-For 헤더가 있는 경우 이를 우선적으로 사용하고,
     * 없는 경우 remoteAddr을 사용합니다.
     */
    private String getClientIp(HttpServletRequest request) {
        String ipAddress = request.getHeader("X-Forwarded-For");
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
        }
        return ipAddress;
    }
}
