package com.eactive.eai.adapter.ftp;

import java.io.File;
import java.io.IOException;
import java.net.Socket;
import java.nio.file.Files;

import org.apache.commons.lang3.StringUtils;

import com.eactive.eai.adapter.socket.config.ConfigurationContext;
import com.eactive.eai.adapter.socket.service.SocketService;
import com.eactive.eai.batch.common.CalendarUtil;
import com.eactive.eai.batch.common.CommonKeys;
import com.eactive.eai.batch.common.LogUtil;
import com.eactive.eai.batch.common.StringUtil;
import com.eactive.eai.batch.doc.BatchDoc;
import com.eactive.eai.batch.ftp.FTPUtil;
import com.eactive.eai.batch.message.EAIBatchMsgManager;
import com.eactive.eai.batch.running.BatchRunningJobManager;
import com.eactive.eai.batch.scheduler.SchedulerMessageManager;
import com.eactive.eai.batch.scheduler.SchedulerMessageVO;
import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.Logger;


/**
 * chk 파일(check.file.suffix)이 있는 파일 송수신 
 * 
 */
public class FTPCH extends Transfer implements SocketService{

    private static final String PROP_GROUP_NAME_PREFIX = "TelegramInfo";
    /** 송신 폴더 */
    private static final String SEND_REMOTE_PATH = "send.remote.path";
    /** 수신 폴더 */
    private static final String RECV_REMOTE_PATH = "recv.remote.path";
    /** 체크 파일 확장자 */
    private static final String CHECK_FILE_SUFFIX = "check.file.suffix";
    /** 큐에서 가져오는 최대 갯수 */
    private static final String MAX_ITERATION = "max.iteration";

    public final static String PHASECODE_NORMAL_END = "END";
    
    public void sendFile(BatchDoc batchDoc) throws Exception {
    	internalSendFile(batchDoc);
    	
        String propName = PROP_GROUP_NAME_PREFIX + "{" +
                batchDoc.getBatchMsg().getHeader().getProcessCode() +
                "_" +
                batchDoc.getBatchMsg().getHeader().getInstitutionCode() + "}";
        PropManager pmanager = PropManager.getInstance();
        int iteration = 100;

		String maxIteration = pmanager.getProperties(propName).getProperty(MAX_ITERATION);
		if (maxIteration != null) {
			try {
				iteration = Integer.parseInt(maxIteration);
			} catch(Exception e) {
				logger.error(maxIteration, e);
			}
		}
    	
    	// 만일의 경우 대비해서 100번만 돈다.
    	for (int i = 0; i < iteration; i++) {
    		// Queue에 있는 지 확인한다.
    		BatchDoc newDoc = checkMoreFile(batchDoc);
    		if (newDoc == null) {
    			break;
    		}
    		String uuid = newDoc.getBatchMsg().getHeader().getUUID();
    		String subUuid = newDoc.getBatchMsg().getBody().getSubUUID();
    		try {
	    		internalSendFile(newDoc);

	    		newDoc.getBatchMsg().getBody().setPhaseEndTime(CalendarUtil.getCurrentTimeNoDash());
	    		setDefaultEndStage(newDoc);
	    		// FlowController Log
	    		LogUtil.setLog(newDoc);
	    		// master 정보
	    		LogUtil.updateLogMaster(newDoc);
	    		// master 작업상태 정보: 정상 종료
	    		LogUtil.setEndLog(newDoc);
    		} finally {
	    		// 진행중에서 삭제. 반드시 삭제되어야 함
	    		SchedulerMessageManager.getInstance().deleteJobFromProcessing(uuid, subUuid);
    		}
    	}
    }
    
    /**
     * 실제로 파일을 전송한다.
     * @param batchDoc 전송할 배치 작업
     */
	private void internalSendFile(BatchDoc batchDoc) throws Exception {
		logger.info("[FTPCH] ■ FEP → 대외기관파일 송신 시작...... [" + batchDoc.getBatchMsg().getHeader().getUUID()+ "]");

		firstActivity = System.currentTimeMillis();
		setLastActivity();
		BatchRunningJobManager.getInstance().addRunningJobInfo( batchDoc.getBatchMsg().getHeader().getUUID(), this, batchDoc);
		String checkFileName = null;
		try {
			String ipaddress = batchDoc.getBatchMsg().getHeader().getRemoteIP();
			int    port      = Integer.parseInt(batchDoc.getBatchMsg().getHeader().getPort());
			String userid    = batchDoc.getBatchMsg().getHeader().getUserID();
			String passwd    = batchDoc.getBatchMsg().getHeader().getUserPassword();
	        String propName = PROP_GROUP_NAME_PREFIX + "{" +
                    batchDoc.getBatchMsg().getHeader().getProcessCode() +
                    "_" +
                    batchDoc.getBatchMsg().getHeader().getInstitutionCode() + "}";
	        PropManager pmanager = PropManager.getInstance();

			String remotePath = pmanager.getProperties(propName).getProperty(SEND_REMOTE_PATH);
			if ( remotePath == null ){
				throw new Exception("해당 기관에 전송 경로가 지정되어 있지 않습니다. -propName:" + propName + "," + SEND_REMOTE_PATH);
			}
			if ( !remotePath.endsWith("/") )
				remotePath = remotePath + "/";

	        String authType = pmanager.getProperties(propName).getProperty(AUTH_TYPE);
	        if (StringUtils.isBlank(authType)) {
	        	authType = AUTH_TYPE_ID; // id
	        }
			
			String checkFileSuffix = pmanager.getProperties(propName).getProperty(CHECK_FILE_SUFFIX);
			if ( checkFileSuffix == null ){
				throw new Exception("체크 파일 접미어가 지정되어 있지 않습니다. -propName:" + propName + "," + CHECK_FILE_SUFFIX);
			}

			String sendFileName = batchDoc.getBatchMsg().getHeader().getFilePath() + "/" + batchDoc.getBatchMsg().getHeader().getRenamedFileName();
			String remoteFileName = remotePath + batchDoc.getBatchMsg().getHeader().getFileName();
			setFileHeaderAndTrailer(batchDoc, new File(sendFileName));
			
			batchDoc.getBatchMsg().getBody().setPhaseStartTime(CalendarUtil.getCurrentTimeNoDash());
			LogUtil.setLogFileStart(batchDoc);
			
			FTPUtil.store(ipaddress, port, userid, passwd, remoteFileName, sendFileName, true, batchDoc.getLogger(Logger.LOGGER_ADAPTER), authType);
			
			// check 파일 이름
			checkFileName = sendFileName + checkFileSuffix;				
			
			// 체크 파일을 지우기 이전에 생성한 체크 파일 삭제
			Files.deleteIfExists(new File(checkFileName).toPath());

			// check 파일 생성
			if (new File(checkFileName).createNewFile() == false) {
				throw new Exception("체크 파일을 생성하지 못했습니다. checkFileName = " + checkFileName);
			};
			String remoteCheckfileName =  remoteFileName + checkFileSuffix;
			FTPUtil.store(ipaddress, port, userid, passwd, remoteCheckfileName, checkFileName, true, batchDoc.getLogger(Logger.LOGGER_ADAPTER), authType);
			
			batchDoc.getBatchMsg().getBody().setPhaseEndTime(CalendarUtil.getCurrentTimeNoDash());
			LogUtil.setLogFileEnd(batchDoc);
		} catch(Exception ex) {
            String[] msgArgs = new String[1];
            msgArgs[0] = batchDoc.getBatchMsg().getHeader().getProcessCode();
            String errCode = "BECEAIFJI002";
            String errMsg = ExceptionUtil.getErrorCode (ex, errCode, msgArgs);
			batchDoc.getBatchMsg().getBody().setErrorMsg(errMsg);
            batchDoc.getBatchMsg().getBody().setPhaseEndTime(CalendarUtil.getCurrentTimeNoDash());
            LogUtil.setErrorLog(batchDoc, ex.getMessage());
            logger.error( errMsg, ex );
            throw ex;
		} finally {
			BatchRunningJobManager.getInstance().removeRunningJobInfo( batchDoc.getBatchMsg().getHeader().getUUID());
			this.disconnect();
			// 체크 파일 삭제
			// 재전송할 때 오류 발생 방지
			if (checkFileName != null) {
				Files.deleteIfExists(new File(checkFileName).toPath());
			}
        }
		logger.info("[FTPCHK] ■ FEP → 대외기관 파일 송신 완료. [" + batchDoc.getBatchMsg().getHeader().getUUID()+ "]");
	}

	/**
	 * 같은 업무, 기관 코드의 작업이 큐에 있는지 확인한다.
	 * 
	 * @param batchDoc
	 * @return 큐에서 가져온 배치 잡 
	 */
    private BatchDoc checkMoreFile(BatchDoc batchDoc) {
		BatchDoc newDoc = null;
    	String processCode = batchDoc.getBatchMsg().getHeader().getProcessCode(); 
    	String institutionCode = batchDoc.getBatchMsg().getHeader().getInstitutionCode();
    	SchedulerMessageVO info;
    	try{
    		info = SchedulerMessageManager.getInstance().getSendJobFromQueue(processCode, institutionCode );
    		if ( info == null ) {
    			return null;
    		}
    		newDoc = EAIBatchMsgManager.createBatchMsg(true);

			//해당 작업을 Job_Queue 테이블에서 Job_Processing 테이블로 이동
    		SchedulerMessageManager.getInstance().moveJobQueueToHandle(info);
    		//BatchMsgDoc Layer, SubLayer, StartTime 설정
    		EAIBatchMsgManager.updateBatchMsgWithStageTime(newDoc, CommonKeys.LAYER_FLOW_CONTROLLER, CommonKeys.SUB_LAYER_FLOW_CONTROLLER);

    		//BatchMsgDoc 값 설정
    		newDoc.getBatchMsg().getHeader().setUUID(info.getUUID());
    		newDoc.getBatchMsg().getBody().setSubUUID(info.getSubUUID());
    		newDoc.getBatchMsg().getHeader().setProcessCode(info.getProcessCode());
    		newDoc.getBatchMsg().getHeader().setProcessName(info.getProcessName());
    		newDoc.getBatchMsg().getHeader().setProcessType(info.getProcessType());
    		newDoc.getBatchMsg().getHeader().setInstitutionCode(info.getInstitutionCode());
    		newDoc.getBatchMsg().getHeader().setInstitutionName(info.getInstitutionName());
    		newDoc.getBatchMsg().getHeader().setScheduleCode(info.getScheduleCode());
    		newDoc.getBatchMsg().getHeader().setScheduleSTime(info.getStartTime());
    		newDoc.getBatchMsg().getHeader().setScheduleETime(info.getEndTime());
    		newDoc.getBatchMsg().getHeader().setFilePath(info.getFilePath());
    		newDoc.getBatchMsg().getHeader().setFileName(info.getFileName());
    		newDoc.getBatchMsg().getHeader().setRenamedFileName(info.getRenamedFileName());
    		newDoc.getBatchMsg().getHeader().setJobCode(info.getJobCode());
    		newDoc.getBatchMsg().getHeader().setBizCode(info.getBizCode());
    		newDoc.getBatchMsg().getHeader().setFlowCode(info.getFlowRuleCode());
    		newDoc.getBatchMsg().getHeader().setRuleCode(info.getFlowRuleCode());
    		newDoc.getBatchMsg().getHeader().setBlockSize(info.getBlockSize());
    		newDoc.getBatchMsg().getHeader().setSequenceSize(info.getSequenceSize());
    		newDoc.getBatchMsg().getHeader().setPacketSize(info.getPacketSize());
    		newDoc.getBatchMsg().getHeader().setUserID(batchDoc.getBatchMsg().getHeader().getUserID());
    		newDoc.getBatchMsg().getHeader().setUserPassword(batchDoc.getBatchMsg().getHeader().getUserPassword());
    		newDoc.getBatchMsg().getHeader().setPort(batchDoc.getBatchMsg().getHeader().getPort());
    		newDoc.getBatchMsg().getHeader().setSystemConnCode(info.getSystemConnCode());
    		newDoc.getBatchMsg().getHeader().setRequestResponse(info.getRqstRspnsDstcd());
    		newDoc.getBatchMsg().getHeader().setRemoteIP(batchDoc.getBatchMsg().getHeader().getRemoteIP());

			// 다음 파일의 체크 파일 가져오기
			if (info.getProcessType().equals(CommonKeys.PROCESS_REQUEST_SEND)
					|| info.getProcessType().equals(CommonKeys.PROCESS_RESPONSE_SEND)) {
				String hdrInfoName = info.getHdrInfoName();
				newDoc.getBatchMsg().getHeader().setHdrInfoName(hdrInfoName);
				newDoc.getBatchMsg().getHeader().setTotRecCnt(StringUtil.getTotRecCountFromHeader(hdrInfoName));
				newDoc.getBatchMsg().getHeader().setBaseDate(StringUtil.getBaseDateFromHeader(hdrInfoName));

				String dirPath = newDoc.getBatchMsg().getHeader().getFilePath();
				String renamedFileName = newDoc.getBatchMsg().getHeader().getRenamedFileName();
				File file = new File(dirPath + File.separatorChar + renamedFileName);
				if (!file.exists()) {
					throw new Exception();
				}
				long fileLen = file.length();

				newDoc.getBatchMsg().getHeader().setFileSize(fileLen);
			}
    	} catch ( Exception e){
    		logger.error(newDoc == null ? "null" : newDoc.toString(), e);
    		return null;
    	}
    	
    	return newDoc;
    }

    /** 
     * FlowController END를 위한 값으로 설정한다.
     * @param batchDoc 설정 값을 변경할 배치 작업
     */
	private void setDefaultEndStage(BatchDoc batchDoc) {
        batchDoc.getBatchMsg().getBody().setFlowPhaseCode(PHASECODE_NORMAL_END);
        batchDoc.getBatchMsg().getBody().setPhaseType(PHASECODE_NORMAL_END);
        //Phase 쪽 단계정보 설정
		batchDoc.getBatchMsg().getPhaseinfo().setRuleCode          ("FTPCH");
		batchDoc.getBatchMsg().getPhaseinfo().setPhaseCode         (PHASECODE_NORMAL_END);
		batchDoc.getBatchMsg().getPhaseinfo().setPhaseType         (PHASECODE_NORMAL_END);
		batchDoc.getBatchMsg().getPhaseinfo().setFlowClassName     ("com.eactive.eai.batch.job.jobModule.Component.END");
		batchDoc.getBatchMsg().getPhaseinfo().setLengthFieldIndex  (0);
		batchDoc.getBatchMsg().getPhaseinfo().setLengthTelegramID  ("");
		batchDoc.getBatchMsg().getPhaseinfo().setLengthClassName   ("");
		batchDoc.getBatchMsg().getPhaseinfo().setLengthMsgCode     ("");
		batchDoc.getBatchMsg().getPhaseinfo().setPhaseSeq          ("1");
		batchDoc.getBatchMsg().getPhaseinfo().setPhaseTelegramID   ("");
		batchDoc.getBatchMsg().getPhaseinfo().setPhaseClassName    ("");
		batchDoc.getBatchMsg().getPhaseinfo().setPhaseMsgCode      ("");
		batchDoc.getBatchMsg().getPhaseinfo().setTelegramTypeValue ("");
		batchDoc.getBatchMsg().getPhaseinfo().setBizCodeValue      ("");
		batchDoc.getBatchMsg().getPhaseinfo().setControlCodeValue  ("");
		batchDoc.getBatchMsg().getPhaseinfo().setResponseCodeValue ("");	
	}

	public void recvFile(BatchDoc batchDoc) throws Exception {
		throw new Exception("해당 Component 에 의한 FTP 송신이 구현되지 않았습니다.");
	}
	
	@Override
	public void shutdown() {
		
	}

	@Override
	public Socket getCurrentSocket() {
		return null;
	}

	@Override
	public boolean connect() {
		return false;
	}

	@Override
	public ConfigurationContext getContext() {
		return null;
	}

	@Override
	public void setControl(Object control) {
		
	}

	@Override
	public void notifyMessage() {
		
	}

	@Override
	public boolean idle(long timeout) {
		return false;
	}

	@Override
	public int getCurrentState() {
		return 0;
	}

	@Override
	public boolean isConnected() throws IOException {
		return false;
	}

	@Override
	public boolean isActive() {
		return false;
	}

	@Override
	public void checkActivity() {
		
	}

	@Override
	public String getRemoteIPAddress() {
		return null;
	}

	@Override
	public void setLastActivity() {
		lastActivity = System.currentTimeMillis();
	}

	@Override
	public long getFirstActivity() {
		return firstActivity;
	}

	@Override
	public long getLastActivity() {
		return lastActivity;
	}
}