package com.eactive.eai.common.routing;

import java.util.Properties;

import org.apache.commons.lang3.StringUtils;

import com.eactive.eai.adapter.AdapterGroupVO;
import com.eactive.eai.adapter.AdapterManager;
import com.eactive.eai.adapter.AdapterVO;
import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.message.EAIMessage;
import com.eactive.eai.common.message.EAIMessageKeys;
import com.eactive.eai.common.message.ServiceMessage;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.server.EAIServerManager;
import com.eactive.eai.common.server.EAIServerVO;
import com.eactive.eai.common.server.Keys;
import com.eactive.eai.common.util.Logger;

/** 
 * 1. 기능 : Flow Control에서 Outbound 를 호출하기 위한 Router
 * 2. 처리 개요 : 
 * *     - FlowControl에서 Outbound 를 호출한다.
 * 3. 주의사항 
 *
 * @author  : 
 * @version : v 1.0.0
 * @see : 관련 기능을 참조
 * @since   : 
 *  : 
 */
public class IFRouter 
{
    public static final String IFROUTER_ERROR_LOCAL   = "RECEAICIR001"; // Local JPD 호출 오류
    public static final String IFROUTER_ERROR_LOCK    = "RECEAICIR002"; // Error발생하여 Failover가 불가능함(FailOver Flag=false)
    public static final String IFROUTER_ERROR_REMOTE  = "RECEAICIR003"; // FailOver증 JPD 호출 오류
    public static final String IFROUTER_INVALID_ROUT  = "RECEAICIR099"; // Outbound 이외의 Process를 호출하고록 설정된 경우
        
    static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
    
    /** 
     * 1. 기능 : FlowCOntrol에서 Outbound Process로 라우팅하는 기능을 제공
 	 * 2. 처리 개요 : 
 	 *      - FlowControl에서 전달받은 EAIMessage의 Routing명을 통해 JPDProxy 방식으로 EAIMessage를 전달한다. 
     * 3. 주의사항 
     *
     * @param eaiMessage Inbound에서 생성한 EAI표준메시지
     * @param callProp 라우팅을 위한 환경값(추가적으로 보낼 인자가 있을 경우 사용)
     * @return EAIMessage Outbound Process로부터 처리된 결과가 리턴됨
     * @exception
     **/
    public static EAIMessage process(EAIMessage eaiMessage, Properties callProp) throws Exception {
        ServiceMessage  service =   ((ServiceMessage)eaiMessage.getSvcMsgs().get( eaiMessage.getSvcPssSeq() - 1 ));
        String routingName      =   service.getOutbRtnNm();
        boolean   isLocal       =   true;     // EAIMessage내의 수동시스템인터페이스유형(AdapterGroupName)
        
//        String guidLogPrefix = "IFRouter] GUID["+eaiMessage.getKbMsg().getKBHeader().getGuIdNo()+"] UUID["+eaiMessage.getSvcOgNo()+"] ";
        String guidLogPrefix = "IFRouter] GUID["+eaiMessage.getMapper().getGuid(eaiMessage.getStandardMessage())+"] UUID["+eaiMessage.getSvcOgNo()+"] ";
        
        // TAS 거래는 Local Call 로 처리하도록 한다. (2019.03.04)
        EAIServerManager eaiServerManager = EAIServerManager.getInstance();
        if(eaiServerManager.isTASEnabledEAIServer())  {
        	AdapterManager adapterManager = AdapterManager.getInstance();
            AdapterGroupVO adptGrpVO = adapterManager.getAdapterGroupVO(service.getPsvSysItfTp());
	        if( EAIMessageKeys.TRANTYPE_TAS.equals(eaiMessage.getTranType())
	        		// RESTAdapter는 교체하지 않는다
	        		&& !StringUtils.equals(adptGrpVO.getType(), "RST")
	        		&& !StringUtils.equals(adptGrpVO.getType(), "HTT")) {
//	        	isLocal = true;
//	        	if (logger.isInfo()) logger.info(guidLogPrefix + " TRANTYPE TAS [" + eaiMessage.getTranType() + "]"); 
	        	PropManager manager = PropManager.getInstance();
	            Properties prop  = manager.getProperties(RouteKeys.SIM);
	            routingName  = prop.getProperty(RouteKeys.SIM_ROUTING,"HTTPOUT");
	            String simAdapterGroupName ="";
	            if (service.isSyncItfTp()){
	            	simAdapterGroupName  = prop.getProperty(RouteKeys.SIM_ADAPTER_SYNC,"_SIM_OU_HTT_SyC");
	            }else{
	            	simAdapterGroupName  = prop.getProperty(RouteKeys.SIM_ADAPTER_ASYNC,"_SIM_OU_HTT_AsC");
	            }
	            isLocal = isAlive(simAdapterGroupName);
	        }
	        else if( EAIMessageKeys.TRANTYPE_DUMMYROUT.equals(eaiMessage.getTranType()) ) {
	        	isLocal = false;
	        	if (logger.isInfo()) logger.info(guidLogPrefix + " TRANTYPE_DUMMYROUT [" + eaiMessage.getTranType() + "] Route to Other Domain - Local ["+System.getProperty(Keys.EAI_SYSTEMMODE)+"]");
	        }
	        else {
	        	isLocal= isAlive( service.getPsvSysItfTp());     // EAIMessage내의 수동시스템인터페이스유형(AdapterGroupName)
	        }
        }
        else {
        	isLocal = isAlive( service.getPsvSysItfTp());     // EAIMessage내의 수동시스템인터페이스유형(AdapterGroupName)
        }
        RoutingVO routingUrl    =   RoutingManager.getInstance().getRoutingVO( routingName );
        
        if (logger.isInfo()) {
            logger.info(guidLogPrefix + "ROUTING NAME [" + routingName + "]");
            logger.info(guidLogPrefix + "ROUTING URI [" + routingUrl + "]");
            logger.info(guidLogPrefix + "TRANTYPE [" + eaiMessage.getTranType() + "]");
            logger.info(guidLogPrefix + "isLocal [" + isLocal + "]");
        }
        
        //-----------------------------------------------------
        // IFRouter에서는 Outbound Process만 호출할 수 있도록 체크(무한 Loop방지)
        // 2006.02.24 - DHLEE
        //-----------------------------------------------------
        if(! "O".equals(routingUrl.getLayerDstcd()) ) {
            throw new Exception(IFROUTER_INVALID_ROUT);
        }
        
        EAIMessage  message = null;        
        if ( eaiMessage.getSvcPssTp().equals( EAIMessage.SINGLE_PROCESS ) 
             || eaiMessage.getSvcPssTp().equals( EAIMessage.COMPLEX_PROCESS )  ) {
            // 단일 수동인터페이스 또는 복합거래 유형인 경우

        	if(!eaiServerManager.isPEAIServer()  && EAIMessageKeys.TRANTYPE_DUMMYROUT.equals(eaiMessage.getTranType()) ) {
        		StringBuffer    urlBuf  = new StringBuffer();
        		
        		EAIServerManager    mgr         = EAIServerManager.getInstance();
                EAIServerVO         server      = mgr.getEAIServer( mgr.getLocalServerName());
                
            	// get Routing server info
            	PropManager propManager = PropManager.getInstance();
                String remote_url = propManager.getProperty(RouteKeys.DUMMY_ROUTE_INFO, server.getName());
                if(logger.isWarn()) {
                	logger.warn(guidLogPrefix + " : DUMMY ROUTE CALL - EAISvcCd="+ eaiMessage.getEAISvcCd()
                		+", remote_url : " +remote_url);
                }
                if(remote_url == null) {
                	String errorMsg = " Dummy Route info not found : Property Group[" + RouteKeys.DUMMY_ROUTE_INFO + "] Property["+server.getName()+"}";
                	logger.error(guidLogPrefix + errorMsg);
                	throw new Exception(errorMsg);
                }
            	urlBuf.append( EAIServerVO.IIOP_PROTOCOL ).append("://");
                urlBuf.append( remote_url );
                
                callProp.setProperty( RouteKeys.REMOTE_CALL_URL,    urlBuf.toString() );
                
                if (logger.isInfo()) {
                    logger.info(guidLogPrefix + " DummyRoute Remote Call [" + urlBuf.toString() + "] RemoteProxyClient");
                }
                
                try {
                    if (logger.isDebug()) {
                        logger.debug(guidLogPrefix + " DummyRoute RemoteProxyClient call");
                    }
                	message = RemoteProxyClient.callProxyBean(eaiMessage, callProp);
                	if (logger.isDebug()) {
                        logger.debug(guidLogPrefix + " DummyRoute RemoteProxyClient result : "+message);
                    }
                } catch(Exception e) {
                	logger.error(guidLogPrefix + " DummyRoute Remote Call Error", e);
                    //throw new Exception(IFROUTER_ERROR_REMOTE);   
                    throw new Exception(ExceptionUtil.getErrorCode(e,IFROUTER_ERROR_REMOTE));
                }
            }
        	else if ( isLocal ) {
                try {
                    message = ElinkESBProcessProxy.callElinkESBProcess( routingUrl.getSyncRoutingPath(), eaiMessage, true, callProp );
                } catch(Exception e) {
                    //throw new Exception( IFROUTER_ERROR_LOCAL );   
                    throw new Exception(ExceptionUtil.getErrorCode(e,IFROUTER_ERROR_LOCAL));
                }
            } else {
                EAIServerManager    mgr         = EAIServerManager.getInstance();
                EAIServerVO         server      = mgr.getEAIServer( mgr.getLocalServerName());
                EAIServerVO         backupSvr   = mgr.getEAIServer( server.getFailoverSvr() );
                
                if ( service.getFlOvrCls().equals( EAIMessage.YES_FLAG ) && backupSvr != null) {
                    //Properties      prop    = new Properties();
                    StringBuffer    urlBuf  = new StringBuffer();
                    urlBuf.append( EAIServerVO.IIOP_PROTOCOL ).append("://");
                    urlBuf.append( backupSvr.getAddress() ).append(":");
                    urlBuf.append( backupSvr.getRmiPort() );
                    
                    callProp.setProperty( RouteKeys.REMOTE_CALL_URL,    urlBuf.toString() );
                    
                    if (logger.isInfo()) {
                        logger.info(guidLogPrefix + " FailOver Remote Call [" + urlBuf.toString() + "] RemoteProxyClient");
                    }
                    
                    try {
                        if (logger.isDebug()) {
                            logger.debug(guidLogPrefix + " FailOver RemoteProxyClient call");
                        }
                    	message = RemoteProxyClient.callProxyBean(eaiMessage, callProp);
                    	if (logger.isDebug()) {
                            logger.debug(guidLogPrefix + " FailOver RemoteProxyClient result : "+message);
                        }
                    } catch(Exception e) {
                    	logger.error(guidLogPrefix + " FailOver Remote Call Error - " + e.getMessage());
//                    	e.printStackTrace();
                        //throw new Exception(IFROUTER_ERROR_REMOTE);   
                        throw new Exception(ExceptionUtil.getErrorCode(e,IFROUTER_ERROR_REMOTE));
                    }
                }
                else {
                    throw new Exception( ExceptionUtil.make(IFROUTER_ERROR_LOCK , new String[1]) );
                }
            }
        } else {
            try {
                if (logger.isDebug()) {
                    logger.debug(guidLogPrefix + " PCOMPLEX [" + routingUrl.getSyncRoutingPath() + "]");
                }                
                message = ElinkESBProcessProxy.callElinkESBProcess( routingUrl.getSyncRoutingPath(), eaiMessage, true, callProp );
            } catch(Exception e) {
                //throw new Exception( IFROUTER_ERROR_LOCAL );   
                throw new Exception(ExceptionUtil.getErrorCode(e,IFROUTER_ERROR_LOCAL));
            }
        }
        
        return message;
    }
    
    /** 
     * 1. 기능 : Adapter그룹명의 Adapter들의 상태정보를 확인하는 메소드 
     * 2. 처리 개요 : 
     *     - AdapterManager를 통해 해당 Adapter그룹의 Adapter의 상태를 확인하여
     *       정상적인 Adapter가 있는지 여부를 리턴한다.
     * 3. 주의사항 
     *
     * @param adapterGroupName Adapter그룹명
     * @return boolean
     * @exception Exception
     **/
    public static boolean isAlive(String adapterGroupName) throws Exception {
        //-------------------------------------------------
        // CISCO Router에 대한 처리로직 추가 : 2007.06.15 DHLEE
        //-------------------------------------------------
        String cadapterGroupName = "";
        String cadapterName = "";
        
        if (adapterGroupName.indexOf('.') != -1) {
            cadapterName = adapterGroupName.substring(adapterGroupName.indexOf('.')+1);
            cadapterGroupName = adapterGroupName.substring(0, adapterGroupName.indexOf('.'));
        }
        else {
            cadapterGroupName = adapterGroupName;
        }
        
        AdapterGroupVO  adapterGroup = AdapterManager.getInstance().getAdapterGroupVO( cadapterGroupName );
        if(adapterGroup==null) {
            throw new Exception("IFRouter] Cannot find the AdapterGroupVO. - "+ cadapterGroupName);   
        }
        if (adapterGroupName.indexOf('.') != -1) {
        	AdapterVO vo = adapterGroup.getAdapterVO(cadapterName);
        	
        	if (vo == null) {
        		return false;
        	}else{
        		//라우팅 특정 포트 매핑시 어댑터 업무 이름 선택시 오류 수정
        		//isStarted 체크는 node를 변경한경우 대비
        		if (vo.isStarted() && vo.isStatus()){
        			return true;
        		}else{
        			return false;
        		}
        	}
        }else{
        	return adapterGroup.isAlive();
        }
    }
} 
