package com.eactive.eai.inbound.processor;


import java.nio.charset.Charset;
import java.util.Properties;
import java.util.UUID;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;

import com.eactive.eai.adapter.AdapterGroupVO;
import com.eactive.eai.adapter.AdapterManager;
import com.eactive.eai.adapter.AdapterPropManager;
import com.eactive.eai.adapter.AdapterVO;
import com.eactive.eai.adapter.Keys;
import com.eactive.eai.adapter.http.client.HttpClientAdapterServiceKey;
import com.eactive.eai.common.routing.GWRemoteProxyClient;
import com.eactive.eai.common.routing.RouteKeys;
import com.eactive.eai.common.server.EAIServerManager;
import com.eactive.eai.common.server.EAIServerVO;
import com.eactive.eai.common.util.CommonLib;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.control.HttpSender;
import com.eactive.eai.control.SocketSender;

public class GWRequestProcessor extends RequestProcessorSupport {

	static final String OUTBOUND_ADAPTER_GROUP_NAME = "OUTBOUND_ADAPTER_GROUP_NAME";
	static final String PAIR_ADAPTER = "PAIR_ADAPTER";
	static final String PAIR_ADAPTER_HEADER_PREFIX = "PA_";
	static Logger logger = Logger.getLogger(Logger.LOGGER_SIFT);
	
	protected Object hookup(Object message, Properties prop, long msgRcvTm) throws Exception {
		
		String inboundAdapterGroupName = prop.getProperty(Processor.ADAPTER_GROUP_NAME);
		logger.debug("GWRequestProcessor] inboundAdapterGroupName: {}", inboundAdapterGroupName);
		
		AdapterManager adapterManager = AdapterManager.getInstance();
		AdapterGroupVO inboundAdptGrpVO = adapterManager.getAdapterGroupVO(inboundAdapterGroupName);

		String inboundAdapterName = prop.getProperty(Processor.ADAPTER_NAME);
		AdapterVO inboundAdapterVO = inboundAdptGrpVO.getAdapterVO(inboundAdapterName);
		
		MDC.put(Logger.DISCRIMINATOR, inboundAdapterGroupName);
        logger.info("<RECV> " + getLogMessage(message));
        logger.debug(CommonLib.getDumpMessage(message));
        MDC.remove(Logger.DISCRIMINATOR);
		
		AdapterPropManager adapterPropManager = AdapterPropManager.getInstance();
		Properties inboundProp = adapterPropManager.getProperties(inboundAdapterVO.getPropGroupName());

        String transactionId = null;
        String instanceId = EAIServerManager.getInstance().getLocalServerName();

		//set pair adapter
        String pairAdapter = inboundProp.getProperty(PAIR_ADAPTER);
        if (StringUtils.isNotBlank(inboundAdptGrpVO.getPairAdapterYn()) && "1".equals(inboundAdptGrpVO.getPairAdapterYn())
				&& StringUtils.isNoneBlank(pairAdapter)) {
        	transactionId = PAIR_ADAPTER_HEADER_PREFIX + UUID.randomUUID().toString().substring(0, 7);
        	GWSessionManager.getInstance().adapterIn(transactionId, pairAdapter);
        	logger.debug("GWRequestProcessor] transactionId|pairAdapter: {}, {}", transactionId, pairAdapter);
		}
        
		//set use same session
        String sessionId = prop.getProperty("SESSION_ID");
        if (StringUtils.isNotBlank(sessionId)) {
			transactionId = sessionId;
        	logger.debug("GWRequestProcessor] transactionId|sessionId: {}", sessionId);
        }
		
		//get Outbound Adapter Group
		Object resObject = null;
		String outboundAdapterGroupName = inboundAdptGrpVO.getLinkedAdapterGroup();
		AdapterGroupVO outboundAdptGrpVO = adapterManager.getAdapterGroupVO(outboundAdapterGroupName);
		String outboundAdapterType = outboundAdptGrpVO.getType();	//HTT, HTC, NET
		
		//response header
		String responseTransactionId = prop.getProperty(HttpClientAdapterServiceKey.TRANSACTION_ID);
		String responseInstanceId = prop.getProperty(HttpClientAdapterServiceKey.INSTANCE_ID);
		if (StringUtils.isNotBlank(responseTransactionId) && StringUtils.isNotBlank(responseInstanceId)) {
			if (!responseInstanceId.equals(instanceId)) {
				logger.warn("GWRequestProcessor] intanceId not matched.[{}<{}>], call Remote Proxy", outboundAdapterGroupName, responseTransactionId);
				resObject = callRemoteProxy(message, outboundAdapterGroupName, responseTransactionId, responseInstanceId);
				
				return resObject;
			}
			
			if (!GWSessionManager.getInstance().isExists(responseTransactionId)) {
				logger.error("GWRequestProcessor] No matched transactionId[{}], at {}", responseTransactionId, responseInstanceId);
				throw new Exception("No matched transactionId.[" + responseTransactionId + "]");
			}
			
			logger.debug("GWRequestProcessor] responseTransactionId: {}", responseTransactionId);
		}
		
		AdapterVO adptVO = outboundAdptGrpVO.nextAdapterVO();
		// 가용 Adapter가 없는 경우
		if (adptVO == null) {
//			throw new Exception("No adapters available.[" + outboundAdapterGroupName + "]");
			logger.warn("GWRequestProcessor] No adapters available.[{}], call Remote Proxy", outboundAdapterGroupName);
			resObject = callRemoteProxy(message, outboundAdapterGroupName);
		} else {
			Properties outboundProp = adapterPropManager.getProperties(adptVO.getPropGroupName());
			
			Properties tempProp = new Properties();
			tempProp.setProperty(com.eactive.eai.adapter.Keys.CHARSET_KEY_OUT,
					StringUtils.defaultIfBlank(outboundAdptGrpVO.getMessageEncode(), Charset.defaultCharset().name()));
			
			MDC.put(Logger.DISCRIMINATOR, outboundAdapterGroupName);
	        logger.info("<SEND> " + getLogMessage(message));
	        logger.debug(CommonLib.getDumpMessage(message));
	        MDC.remove(Logger.DISCRIMINATOR);

			try {
				if (outboundAdapterType.equals(Keys.TYPE_HTTP) || outboundAdapterType.equals(Keys.TYPE_HTTP_CUSTOM)) {
					tempProp.setProperty(HttpClientAdapterServiceKey.ADAPTER_GROUP_NAME, outboundAdapterGroupName);
					
					//set header
					if (StringUtils.isNotBlank(pairAdapter) || StringUtils.isNotBlank(sessionId)) {
				        String adapterHeader = "{ \"" + HttpClientAdapterServiceKey.TRANSACTION_ID + "\", \"" + transactionId + "\" },"
	                             + "{ \"" + HttpClientAdapterServiceKey.INSTANCE_ID + "\", \"" + instanceId + "\" }";
						outboundProp.put(HttpClientAdapterServiceKey.ADAPTER_HEADER, adapterHeader);
					}

					//set pair adapter
					if (StringUtils.isNotBlank(responseTransactionId) && responseTransactionId.startsWith(PAIR_ADAPTER_HEADER_PREFIX)) {
						tempProp.setProperty(HttpClientAdapterServiceKey.ADAPTER_NAME, GWSessionManager.getInstance().getAdapter(responseTransactionId));
						GWSessionManager.getInstance().adapterOut(responseTransactionId);;
					} else {
						tempProp.setProperty(HttpClientAdapterServiceKey.ADAPTER_NAME, adptVO.getName());
					}

					resObject = HttpSender.callService(outboundAdapterGroupName, outboundProp, message, tempProp);
				} else if (outboundAdapterType.equals(Keys.TYPE_NET2)) {
					tempProp.setProperty(Keys.OUT_ADAPTER_GROUP_NAME, outboundAdapterGroupName);
					
					//set pair adapter
					if (StringUtils.isNotBlank(responseTransactionId) && responseTransactionId.startsWith(PAIR_ADAPTER_HEADER_PREFIX)) {
						tempProp.setProperty(Keys.OUT_ADAPTER_NAME, GWSessionManager.getInstance().getAdapter(responseTransactionId));
						GWSessionManager.getInstance().adapterOut(responseTransactionId);;
					} else {
						tempProp.setProperty(Keys.OUT_ADAPTER_NAME, adptVO.getName());
						//set session
						if (StringUtils.isNotBlank(responseTransactionId)) {
							tempProp.setProperty(HttpClientAdapterServiceKey.TRANSACTION_ID, responseTransactionId);
						}
					}
					
					resObject = SocketSender.callService(outboundAdapterGroupName, outboundProp, message, tempProp);
				}
			} catch(Exception e) {
				logger.warn("GW.Sender] callService " + e.getMessage(), e);
				logger.warn("call Remote Proxy");
//				resObject = e.getMessage();
				resObject = callRemoteProxy(message, outboundAdapterGroupName);
			}
			
			if (resObject != null) {
				MDC.put(Logger.DISCRIMINATOR, outboundAdapterGroupName);
				logger.info("<RECV> " + getLogMessage(resObject));
				logger.debug(CommonLib.getDumpMessage(resObject));
				MDC.remove(Logger.DISCRIMINATOR);
			}
		}

		if (resObject != null) {
			MDC.put(Logger.DISCRIMINATOR, inboundAdapterGroupName);
	        logger.info("<SEND> " + getLogMessage(resObject));
	        logger.debug(CommonLib.getDumpMessage(resObject));
	        MDC.remove(Logger.DISCRIMINATOR);
		}

		return resObject;
	}

	public static Object callService(Object message, Properties callProp) throws Exception {
        String outboundAdapterGroupName = callProp.getProperty(OUTBOUND_ADAPTER_GROUP_NAME);
        String transactionId = callProp.getProperty(HttpClientAdapterServiceKey.TRANSACTION_ID);
        logger.debug("GWRequestProcessor.callService] transactionId: {}", transactionId);
        
        AdapterManager adapterManager = AdapterManager.getInstance();
        AdapterGroupVO outboundAdptGrpVO = adapterManager.getAdapterGroupVO(outboundAdapterGroupName);
		String outboundAdapterType = outboundAdptGrpVO.getType();	//HTT, HTC, NET
		
		AdapterVO adptVO = outboundAdptGrpVO.nextAdapterVO();
		// 가용 Adapter가 없는 경우
		if (adptVO == null) {
			throw new Exception("GWRequestProcessor] No adapters available.[" + outboundAdapterGroupName + "]");
		}

        Object resObject = null;
		AdapterPropManager manager = AdapterPropManager.getInstance();
		Properties outboundProp = manager.getProperties(adptVO.getPropGroupName());
		Properties tempProp = new Properties();
		tempProp.setProperty(com.eactive.eai.adapter.Keys.CHARSET_KEY_OUT,
				StringUtils.defaultIfBlank(outboundAdptGrpVO.getMessageEncode(), Charset.defaultCharset().name()));

		MDC.put(Logger.DISCRIMINATOR, outboundAdapterGroupName);
        logger.info("<SEND> " + getLogMessage(message));
        logger.debug(CommonLib.getDumpMessage(message));
        MDC.remove(Logger.DISCRIMINATOR);

		if (outboundAdapterType.equals(Keys.TYPE_HTTP) || outboundAdapterType.equals(Keys.TYPE_HTTP_CUSTOM)) {
			tempProp.setProperty(HttpClientAdapterServiceKey.ADAPTER_GROUP_NAME, outboundAdapterGroupName);

			//set pair adapter
			if (StringUtils.isNotBlank(transactionId) && transactionId.startsWith(PAIR_ADAPTER_HEADER_PREFIX)) {
				tempProp.setProperty(HttpClientAdapterServiceKey.ADAPTER_NAME, GWSessionManager.getInstance().getAdapter(transactionId));
				GWSessionManager.getInstance().adapterOut(transactionId);;
			} else {
				tempProp.setProperty(HttpClientAdapterServiceKey.ADAPTER_NAME, adptVO.getName());
			}

			resObject = HttpSender.callService(outboundAdapterGroupName, outboundProp, message, tempProp);
		} else if (outboundAdapterType.equals(Keys.TYPE_NET2)) {
			tempProp.setProperty(Keys.OUT_ADAPTER_GROUP_NAME, outboundAdapterGroupName);
			
			//set pair adapter
			if (StringUtils.isNotBlank(transactionId) && transactionId.startsWith(PAIR_ADAPTER_HEADER_PREFIX)) {
				tempProp.setProperty(Keys.OUT_ADAPTER_NAME, GWSessionManager.getInstance().getAdapter(transactionId));
				GWSessionManager.getInstance().adapterOut(transactionId);;
			} else {
				tempProp.setProperty(Keys.OUT_ADAPTER_NAME, adptVO.getName());
				//set session
				if (StringUtils.isNotBlank(transactionId)) {
					tempProp.setProperty(HttpClientAdapterServiceKey.TRANSACTION_ID, transactionId);
				}
			}
			
			resObject = SocketSender.callService(outboundAdapterGroupName, outboundProp, message, tempProp);
		}
		
		if (resObject != null) {
			MDC.put(Logger.DISCRIMINATOR, outboundAdapterGroupName);
			logger.info("<RECV> " + getLogMessage(resObject));
			logger.debug(CommonLib.getDumpMessage(resObject));
			MDC.remove(Logger.DISCRIMINATOR);
		}
		
		return resObject;
	}
	
    public static String getLogMessage(Object obj) {
    	String message = null;
    	int size = 0;
    	if(obj instanceof byte[]) {
    	    message = new String((byte[]) obj);
    	    size = ((byte[]) obj).length;
    	}
    	else if(obj instanceof String) {
    	    message = (String) obj;
//    	    size = (((String) obj).getBytes()).length;
    	    size = ((String) obj).length();
    	}
    	
        StringBuffer logMessage = new StringBuffer();
        logMessage.append("size[" + size + "], message[" + message + "]");

    	return logMessage.toString();
    }
    
    public String getRemoteCallUrl(String instanceId) {
        EAIServerManager mgr = EAIServerManager.getInstance();
        EAIServerVO server, backupSvr = null;
        if (instanceId == null) {
        	server = mgr.getEAIServer(mgr.getLocalServerName());
        	backupSvr = mgr.getEAIServer(server.getFailoverSvr());
        } else {
        	backupSvr = mgr.getEAIServer(instanceId);
        }
        
        
        StringBuffer urlBuf = new StringBuffer();
        urlBuf.append(EAIServerVO.IIOP_PROTOCOL).append("://");
        urlBuf.append(backupSvr.getAddress()).append(":");
        urlBuf.append(backupSvr.getRmiPort());
            
    	return urlBuf.toString();
    }
    
    public Object callRemoteProxy(Object message, String outboundAdapterGroupName) {
    	return callRemoteProxy(message, outboundAdapterGroupName, null, null);
    }
	
	private Object callRemoteProxy(Object message, String outboundAdapterGroupName, String responseTransactionId, String responseInstanceId) {
    	Properties callProp = new Properties();
    	callProp.setProperty(RouteKeys.REMOTE_CALL_URL, getRemoteCallUrl(responseInstanceId));
    	callProp.setProperty(OUTBOUND_ADAPTER_GROUP_NAME, outboundAdapterGroupName);
    	if (responseTransactionId != null)
    		callProp.setProperty(HttpClientAdapterServiceKey.TRANSACTION_ID, responseTransactionId);
    	
    	Object resObject = null;
    	
    	try {
    		resObject = GWRemoteProxyClient.callProxyBean(message, callProp);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	
    	return resObject;
	}
}