package com.eactive.eai.adapter.socket2.protocol.xc3epb.handshake;

import com.eactive.eai.adapter.socket2.common.SMSLogger;
import com.eactive.eai.adapter.socket2.config.SocketAdapterContext;
import com.eactive.eai.adapter.socket2.protocol.ByteMessage;
import com.eactive.eai.adapter.socket2.protocol.ISocketAdapter;
import com.eactive.eai.adapter.socket2.protocol.xc3epb.Xc3EpbConstants;
import com.eactive.eai.common.message.ExtMessageKeys;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.property.PropertyLoginKeys;
import com.eactive.eai.common.session.TerminalVO;
import com.eactive.eai.common.util.ContainerUtil;
import com.sf.xc3.XCException;
import com.sf.xc3.XC_v3;
import org.apache.mina.common.IoSession;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;

import java.util.Properties;


public class Xc3EpbServerFilter extends Xc3EpbFilter {
	public Xc3EpbServerFilter(ISocketAdapter adapter) {
		super(adapter);
	}
	@Override
	public void sessionCreated(NextFilter nextFilter, IoSession session) throws Exception {
		FilterState filterState = (FilterState) session.getAttribute(FILTER_STATE_KEY);

		if (filterState == null) {
			filterState = new FilterState();
			filterState.state = State.STATE_HANDSHAKE;
			session.setAttribute(FILTER_STATE_KEY, filterState);
		}

		nextFilter.sessionCreated(session);
	}

	@Override
    public void sessionOpened(NextFilter nextFilter, IoSession session) throws Exception {
		try {
	    	if (ContainerUtil.get() != ContainerUtil.TOMCAT) {
	    		XC_v3 xc = new XC_v3();
	    		xc.init(context.getXc3epbPassword(), context.getXc3epbConfigPath(), 2);//MODE 2 (server)
	    		session.setAttribute(XC3_SESSION_KEY, xc);
	    	}

			nextFilter.sessionOpened(session);

		} catch(Exception e) {
			adapter.addRecentError(context.getAdapterName() + " | xc3 init error - " + e.getMessage(), e);
			logger.error(context.getAdapterName() + " | xc3 init error - " + e.getMessage(), e);
			errLogger.error(context.getAdapterName() + " | xc3 init error - " + e.getMessage(), e);

			try {
				adapter.getSessionPool().remove(session);
				session.close();
			} catch(Exception ignor) {
				logger.warn("session.close", ignor);
			}

			throw new Exception(context.getAdapterName() + " | xc3 init error - " + e.getMessage());
		}
	}
	@Override
	public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {
		ByteMessage m = (ByteMessage) message;
		process(nextFilter, session, m);
	}

	public void process(NextFilter nextFilter, IoSession session, Object message) throws Exception {

		FilterState filterState = (FilterState) session.getAttribute(FILTER_STATE_KEY);
		ByteMessage m = (ByteMessage) message;

		/**
		 * set state offset , length   : value
		 * 메시지구분    [0     |1       ] : S(세션키교환) / D (표준전문) / P (폴링) / E(장애전문)
		 * 암화화여부    [1     |1       ] : Y(세션키로 암호화된 메시지) / N (평문메시지)
		 */
		String messageType = new String(m.getBytes(Xc3EpbConstants.MESSAGE_TYPE_OFFSET, 1));
		String encryptedYn = new String(m.getBytes(Xc3EpbConstants.ENCRYPTED_YN_OFFSET, 1));


		if("S".equals(messageType)){
			filterState.state = State.STATE_HANDSHAKE;
		}else if ("P".equals(messageType)){
			filterState.state = State.STATE_MESSAGE;
		}else if ("D".equals(messageType) || "E".equals(messageType)){
			String type = (String)session.getAttribute("messageType");
			if (type != null){
				//오류
				throw new Exception("messageType exist " + type);
			}
			session.setAttribute("messageType", messageType);
			filterState.state = State.STATE_MESSAGE;
		}else{
			throw new Exception("not support message type ("+messageType+")");
		}
		session.setAttribute(FILTER_STATE_KEY, filterState);

		String oldEncryptedYn = (String)session.getAttribute(ENCRYPTED_YN_KEY);

		try {
			if (oldEncryptedYn != null && !oldEncryptedYn.equals(encryptedYn)){
				throw new Exception("change encrypted yn ");
			}

			if ("Y".equals(encryptedYn)){
				;
			}else if ("N".equals(encryptedYn)){
				;
			}else {
				throw new Exception("not support encrypted code ("+encryptedYn+")");
			}
			session.setAttribute(ENCRYPTED_YN_KEY,encryptedYn);


			switch (filterState.state) {
			case STATE_HANDSHAKE:
				XC_v3 xc = (XC_v3) session.getAttribute(XC3_SESSION_KEY);
				if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_TRACE) {
					logger.debug(context.getAdapterName() + " | Xc3EpbServerFilter | <RECV> " + filterState.state.toString() + " | " + m.hexDump());
					adapter.addRecentRecvTrace(session, context.getAdapterName() + " | Xc3EpbServerFilter | <RECV> key init " + filterState.state.toString() + " | " + m.hexDump());
				}
				byte[] finalKey = xc.keyFinal(m.getBytes(2),null);

				ByteMessage keyFinal = new ByteMessage().append(m.getBytes(0, 2)).append(finalKey);

				if ( context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_TRACE && m != null) {
					logger.debug(context.getAdapterName() + " | Xc3EpbServerFilter | <SEND> key final " + filterState.state.toString() + " | " + keyFinal.hexDump());
					adapter.addRecentSendTrace(session, context.getAdapterName() + " | Xc3EpbServerFilter | <SEND> key final " + filterState.state.toString() + " | " + keyFinal.hexDump());
				}

				session.write(keyFinal.getBytes());

				break;

			case STATE_MESSAGE:
				m = decrypt(m, session);
				if (!"P".endsWith(messageType)){
					recvParsing(m.getBytes(),session);
				}
				nextFilter.messageReceived(session, m);
				break;

			default:
				throw new Exception("<RECV> invalid message during handshake | " + m.hexDump());
			}

		} catch(Exception e) {
			adapter.addRecentError(context.getAdapterName() + " | xc3epb error - " + e.getMessage(), e);
			logger.error(context.getAdapterName() + " | xc3epb error - " + e.getMessage(), e);
			errLogger.error(context.getAdapterName() + " | xc3epb error - " + e.getMessage(), e);
			SMSLogger.loggingSMS4Soc(context.getAdapterName(), context.getDescription(), "RECEAINNA012");

			nextFilter.filterClose(session);
			adapter.getSessionPool().remove(session);
			session.close();

			throw new Exception(context.getAdapterName() + " | xc3epb error - " + e.getMessage());
		}
	}
	@SuppressWarnings("static-access")
	@Override
    public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
		FilterState filterState = (FilterState) session.getAttribute(FILTER_STATE_KEY);
		ByteMessage m = null;

		if (writeRequest.getMessage() instanceof byte[]) {
			m = new ByteMessage((byte[]) writeRequest.getMessage());
		} else if (writeRequest.getMessage() instanceof ByteMessage) {
			m = (ByteMessage) writeRequest.getMessage();
		} else if (writeRequest.getMessage() instanceof String) {
			m = new ByteMessage((String) writeRequest.getMessage());
		} else {
			throw new Exception("Unknown data format");
		}

		if ( context.getTraceLevel() >= context.TRACE_LEVEL_TRACE ) {
			logger.debug(context.getAdapterName() + " | XC3EpbFilter | <SEND> " + filterState.state.toString() + " | " + m.hexDump());
		}

		try {
			switch (filterState.state) {
				case STATE_KEYINIT :
				case STATE_KEYFINAL :
				case STATE_HANDSHAKE :

					break;
				case STATE_MESSAGE:
					/**
					 * check polling message - skip polling
					 */
					sendParsing(m.getBytes(), session);
					m = encrypt(m, session);
					break;
				default :
					break;

			}

		} catch(XCException xce) {
			adapter.addRecentError(context.getAdapterName() + " | xc3epb error - " + xce.getMessage(), xce);
			logger.error(context.getAdapterName() + " | xc3epb error - " + xce.getMessage(), xce);
			errLogger.error(context.getAdapterName() + " | xc3epb error - " + xce.getMessage(), xce);
			throw new XCException(context.getAdapterName() + " | xc3epb error - " + xce.getMessage());
		}

		/**
		 * next filter
		 */
		nextFilter.filterWrite(session, new WriteRequest(m, writeRequest.getFuture(), writeRequest.getDestination()));
    }

	protected ByteMessage encrypt(ByteMessage m, IoSession session) throws Exception {
		//bytemessage에는  업무데이터만 있음

		String encryptedYn = (String)session.getAttribute(ENCRYPTED_YN_KEY);

		ByteMessage em = new ByteMessage();

		if (context.isPollingUse() && isPollingPattern(m)) {
			//메시지구분/암호화여부추가
			em.append("P".getBytes()).append(encryptedYn.getBytes());
		}else{
			String type = (String)session.removeAttribute("messageType");
			if (type == null){
				//오류
				throw new Exception("messageType empty");
			}
			//메시지구분/암호화여부추가
			em.append(type.getBytes()).append(encryptedYn.getBytes());
		}
		/**
		 * 암호화 여부 체크
		 */
		if ("Y".equals(encryptedYn) ){
			XC_v3 xc = (XC_v3) session.getAttribute(XC3_SESSION_KEY);
			byte[] encrypted = xc.encode(m.getBytes());
			em.append(encrypted);

		}else{
			em.append(m.getBytes());
		}

		return em;
	}
	protected ByteMessage decrypt(ByteMessage m, IoSession session) throws Exception {
		/**
		 * 암호화 여부 체크
		 */
		if ('Y' != m.getByte(Xc3EpbConstants.ENCRYPTED_YN_OFFSET)){
			ByteMessage dm = new ByteMessage(m.getBytes(2));
			return dm;
		}

		XC_v3 xc = (XC_v3) session.getAttribute(XC3_SESSION_KEY);

		byte[] decrypted = xc.decode(m.getBytes(2));

		ByteMessage dm = new ByteMessage(decrypted);
		if(context.getTraceLevel() >= SocketAdapterContext.TRACE_LEVEL_TRACE){
			logger.debug(context.getAdapterName() + " | Xc3EpbFilter | decrypt " + dm.hexDump());
		}
		return dm;
	}
	@Override
	public void sessionClosed(NextFilter nextFilter, IoSession session) throws Exception {
//		TerminalVO vo = (TerminalVO)session.getAttribute(TERMINAL_KEY);
//		if (vo != null){
//			try{
//				SessionManager.getInstance().removeTerminal(vo.getTerminalNo());
//			}catch(Exception e){
//				logger.error("sessionManager error",e);
//			}
//		}
		nextFilter.sessionClosed(session);
	}
	/**
	 * 요청 과 응답 분리
	 * 분리될경우 요청에서 세션을 넣고 응답에서 셋션을 꺼낸다음 응답에서 ehcache에 넣어야됨
	 * 2번 파싱하는건
	 * 1. 거래일경우 표준전문의 서비스ID 추출
	 * 2. 개국거래일경우 국코드,청코드 추출
	 *
	 * @param data
	 */
	private void recvParsing(byte[] data, IoSession session){
    	PropManager pManager  = PropManager.getInstance();
        Properties prop       = pManager.getProperties(PropertyLoginKeys.BEGIN);
        String ATM            = prop.getProperty(PropertyLoginKeys.BEGIN_SERVICE_ID_ATM);
        String UNMANNED       = prop.getProperty(PropertyLoginKeys.BEGIN_SERVICE_ID_UNMANNED);
        String ROFC_CD        = "";
        String BRN_CD         = "";
        String terminalNo     = "";
        try{
	    	JSONObject requestJson = (JSONObject) JSONValue.parse(new String(data,ENCODING));
	    	JSONObject headerJson = (JSONObject) requestJson.get(ExtMessageKeys.GROUP_HEADER);
	    	JSONObject systemHeaderJson = (JSONObject) headerJson.get(ExtMessageKeys.GROUP_STD_HEADER);
	    	String serviceId = (String) systemHeaderJson.get(ExtMessageKeys.FIELD_SERVICE_ID);

	    	logger.debug("Xc3EpbServerFilter] serviceId = " + serviceId);
	        if (ATM.equals(serviceId) || UNMANNED.equals(serviceId)){//개국거래일경우
	        	JSONObject commonHeaderJson = (JSONObject) headerJson.get(ExtMessageKeys.GROUP_STD_COMMON);
	        	terminalNo = (String) commonHeaderJson.get(ExtMessageKeys.FIELD_DLNG_TMNL_NO);// 로그인거래 와 다를경우 확인필요

	        	ROFC_CD = (String) commonHeaderJson.get(ExtMessageKeys.FIELD_ROFC_CD);
	        	BRN_CD  = (String) commonHeaderJson.get(ExtMessageKeys.FIELD_BRN_CD);
	        	TerminalVO vo = new TerminalVO();
	        	vo.setTerminalNo(terminalNo);
	        	vo.setRofcCd(ROFC_CD);
	        	vo.setBrncd(BRN_CD);
	        	vo.setOpenTran(true);
	        	session.setAttribute(TERMINAL_KEY,vo);
	        	logger.debug("set terminl vo=" + vo);
	        }else{//개국이외의 거래
	    		TerminalVO vo = (TerminalVO)session.getAttribute(TERMINAL_KEY);
	    		if (vo == null || !vo.isOpen()){ // 개국전에 들어왔을경우
	    			logger.error("before open tran close session");
	    			session.close();
	    		}
	        }
        }catch(Exception e){
        	logger.error("recvParsing error "+e.getMessage(),e);
        }

	}
	private void sendParsing(byte[] data, IoSession session) throws Exception{
		if (context.isPollingUse() && isPollingPattern(new ByteMessage(data))) {
			return;
		}
		TerminalVO vo = (TerminalVO)session.getAttribute(TERMINAL_KEY);
		if (vo == null) {
			session.close();
			logger.error("session vo null" );
			return;
		}
		boolean isOpenTran = vo.isOpenTran();
		if (isOpenTran){
	        try{
		    	JSONObject requestJson = (JSONObject) JSONValue.parse(new String(data,ENCODING));
		    	JSONObject headerJson = (JSONObject) requestJson.get(ExtMessageKeys.GROUP_HEADER);
		    	JSONObject systemHeaderJson = (JSONObject) headerJson.get(ExtMessageKeys.GROUP_STD_HEADER); //20220405
		    	String responseType = (String) systemHeaderJson.get(ExtMessageKeys.FIELD_RSPNS_RSLT_DVSN_CD);

		    	logger.debug("Xc3EpbServerFilter] responseType = " + responseType);
//		    	if(STDMessage.RESPONSE_TYPE_CODE_N.contentEquals(responseType)) {//정상
//		    		EAIServerManager eaiServerManager =  EAIServerManager.getInstance();
//		    		String serverName = eaiServerManager.getLocalServerName();
//		    		TerminalInfoVO terminalInfoVO = new TerminalInfoVO(vo.getTerminalNo());
//		    		terminalInfoVO.setBrnCode(vo.getBrncd());
//		    		terminalInfoVO.setRofcCode(vo.getRofcCd());
//		    		terminalInfoVO.setSocInstance(serverName);
//		    		terminalInfoVO.setSocRemoteAddr(session.getRemoteAddress().toString());
//		    		terminalInfoVO.setSocSessionStatus(TerminalInfoVO.Status.active);
//		    		terminalInfoVO.setCreateTime(DatetimeUtil.getCurrentDateTime());
//		    		try{
//		    			SessionManager.getInstance().putTerminal(vo.getTerminalNo(), vo, terminalInfoVO);
//		    		}catch(Exception e){
//		    			logger.error("sessionManager error",e);
//		    		}
//		    		vo.setOpen(true);
//		    	}else{
//		    		//SessionManager.getInstance().removeTerminal(vo.getTerminalNo());
//		    		//session.close();
//		    		vo.setOpen(false);
//		    	}
				vo.setOpenTran(false);//개국거래 종료로 false로 셋팅
				session.setAttribute(TERMINAL_KEY,vo);
	        }catch(Exception e){
	        	logger.error("sendParsing error "+e.getMessage(),e);
	        }
		}

	}

}
