package com.eactive.eai.adapter.ftp;

import static com.eactive.eai.adapter.ftp.SFTPConstants.RECV_REMOTE_PATH;
import static com.eactive.eai.adapter.ftp.SFTPConstants.SEND_REMOTE_PATH;

import java.io.File;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTPFile;

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.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.logger.LogManager;
import com.eactive.eai.batch.osd.OutsideManager;
import com.eactive.eai.batch.osd.OutsideVO;
import com.eactive.eai.batch.running.BatchRunningJobManager;
import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.DatetimeUtil;
import com.eactive.eai.common.util.Logger;


/**
 * 금용결제원 SFTP 
 * 빅데이터 기반의 금융의심거래정보 분석 공유 서비스[FAS]
 * 
 * 이름은 KS(Kftc Sftp)가 사용 중이라 FAS에 따옴
 * 
 * 파일명 : FS0001
 *
 */

public class FTPFS extends Transfer implements SocketService {

    private static final String PROP_GROUP_NAME_PREFIX = "TelegramInfo";

    // 파일경로 관련 프로퍼티
    private static final String REMOTE_PATH = "remote.path";
    private static final String REMOTE_PATH_BASEDATE_YN = "remote.path.basedate.yn";
    
    /** 임시 파일 확장자 .FILEPART. 해당 파일 무시 */
    private static final String REMOTE_FILE_WORKING_POSTFIX = "remote.file.working.postfix";    
    // 파일관련 프로퍼티
    private static final String REMOTE_FILE_BASEDATE_DELIM = "remote.file.basedate.delim";
    /** 현재일 기준 일자 변경: -1 인경우 전일자 */
    private static final String REMOTE_FILE_BASEDATE_ADDDAYS = "remote.file.basedate.adddays";
    private static final String REMOTE_FILE_BASEDATE_YN = "remote.file.basedate.yn";
    private static final String REMOTE_FILE_BASETIME_DELIM = "remote.file.basetime.delim";
    private static final String REMOTE_FILE_BASETIME_YN = "remote.file.basetime.yn";
    private static final String REMOTE_FILE_BASETIME = "remote.file.basetime";
    
    /** 인증 방법 1 = id, 2 = key 방식 */
    private static final String AUTH_TYPE = "sftp.auth.type";
    
	@Override
	public void sendFile(BatchDoc batchDoc) throws Exception {
		// TODO Auto-generated method stub
		logger.info("[FTPFS] ■ 대외기관→FEP 파일 송신 시작...... ");
		firstActivity = System.currentTimeMillis();
		setLastActivity();
		BatchRunningJobManager.getInstance().addRunningJobInfo(batchDoc.getBatchMsg().getHeader().getUUID(), this, batchDoc);
		
		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(REMOTE_PATH);
			if ( remotePath == null ) {
				remotePath = "";
			}
			String sendRemotePath = pmanager.getProperties(propName).getProperty(SEND_REMOTE_PATH);
			if ( sendRemotePath != null && sendRemotePath.trim().length() > 0) {
				remotePath = sendRemotePath;
			}
	        if ( !remotePath.endsWith("/") )
				remotePath = remotePath + "/";

	        String authType = pmanager.getProperties(propName).getProperty(AUTH_TYPE);
	        if (StringUtils.isBlank(authType)) {
	        	authType = "1"; // id
	        }
	        
			// 업로드 서버가 일자별 경로 생성 시, 경로에 일자정보 추가.
	        String remotePathBasedateYn = pmanager.getProperties(propName).getProperty(REMOTE_PATH_BASEDATE_YN);
	        if ( remotePathBasedateYn == null || !remotePathBasedateYn.equalsIgnoreCase("y") ) {
	        	remotePathBasedateYn = "n";
	        }
	        else {
	        	remotePathBasedateYn = "y";
	        }
			
			String baseDate = DatetimeUtil.getCurrentDate();
	        if ( remotePathBasedateYn == "y" )
	        	remotePath = remotePath + baseDate + "/";

	        // 실제 파일이름
	        String orgFileName = batchDoc.getBatchMsg().getHeader().getFileName();
	        // FEP 에서 Rename(송신Real->송신Arch)한 파일이름
			String sendFileName = batchDoc.getBatchMsg().getHeader().getRenamedFileName();
			
			logger.debug( "[FTPFS]sendFileName:" + orgFileName);
			logger.debug( "[FTPFS]remotePath:" + remotePath);			
			
	        //File 시작시간 설정
			batchDoc.getBatchMsg().getBody().setPhaseStartTime(CalendarUtil.getCurrentTimeNoDash());
			LogUtil.setLogFileStart(batchDoc);

	        String sendFilePath = batchDoc.getBatchMsg().getHeader().getFilePath();
	        	        
	        try {
	        	FTPUtil.store(ipaddress, port, userid, passwd, remotePath + orgFileName, sendFilePath + File.separator + sendFileName, true, batchDoc.getLogger(), authType);

	        } catch (Exception e) {
	        	logger.error("[FTPFS]파일송신에 실패하였습니다.", e);
	        	throw e;
	        }
	        
			//File 종료시간 설정
	        setLastActivity();
			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);
            LogUtil.setErrorLog(batchDoc, ex.getMessage());
            logger.error( errMsg, ex );
            throw ex;
		} finally {
			// SFTP는 실제 disconnect는 FTPUtil-SFTPAdaptor에서 하지만, 연결해제 로그를 찍어주기 위해 호출.
			this.disconnect();
        }

		logger.info("[FTPFS] ■ FEP → VAN사 파일 송신 완료.");
		
	}
	
	public void recvFile(BatchDoc batchDoc) throws Exception {
		logger.info("[FTPFS] ■ 대외기관→FEP 파일 수신 시작...... ");
		firstActivity = System.currentTimeMillis();
		setLastActivity();
		BatchRunningJobManager.getInstance().addRunningJobInfo(batchDoc.getBatchMsg().getHeader().getUUID(), this, batchDoc);
		
		try {
			
            int bizCodeStartIndex = 0; // 거래시작인덱스
            int bizCodeEndIndex   = 0; // 거래종료인덱스
			String processCode = batchDoc.getBatchMsg().getHeader().getProcessCode();
			String institutionCode = batchDoc.getBatchMsg().getHeader().getInstitutionCode();			
			ArrayList<OutsideVO> organList = OutsideManager.getInstance().getOutsideInfo(processCode);		
            for (int i=0; organList!=null && i<organList.size(); i++) {
                OutsideVO organInfo = organList.get(i);
                if ( institutionCode.equals(organInfo.getOsdCode()) ) {
                    bizCodeStartIndex = organInfo.getBizCdStartIdx();
                    bizCodeEndIndex   = organInfo.getBizCdEndIdx();
                    break;
                }
            }
            
			PropManager pmanager = PropManager.getInstance();
            
			// 기관 프로퍼티그룹 명
			String propName = PROP_GROUP_NAME_PREFIX + "{" +
					processCode +
            		"_" +
            		institutionCode + "}";
			
	        String remotePath = pmanager.getProperties(propName).getProperty(REMOTE_PATH);
	        if ( remotePath == null ) {
	        	remotePath = "";
	        }
	        String recvRemotePath = pmanager.getProperties(propName).getProperty(RECV_REMOTE_PATH);
			if ( recvRemotePath != null && recvRemotePath.trim().length() > 0) {
				remotePath = recvRemotePath;
			}
	        if ( !remotePath.endsWith("/") )
				remotePath = remotePath + "/";

			//20220712 
			String connTimeoutStr = pmanager.getProperties(propName).getProperty(CONNECTION_TIMEOUT, "10");
			int connTimeout = Integer.parseInt(connTimeoutStr);
			String setTimeoutStr = pmanager.getProperties(propName).getProperty(READ_TIMEOUT, "10");
			int setTimeout =  Integer.parseInt(setTimeoutStr);			
			String bandWidthLimit = pmanager.getProperties(propName).getProperty(BADNWIDTH, "0");
			bandWidthLimit = bandWidthLimit.replace("K", "000").replace("M", "000000").replaceAll(",", "");
			int bandWidth = Integer.parseInt(bandWidthLimit);		
	        
	        // 1. 파일 경로에 일자 디렉토리 포함 여부 확인
	        String remotePathBasedateYn = pmanager.getProperties(propName).getProperty(REMOTE_PATH_BASEDATE_YN);
	        if ( remotePathBasedateYn == null || !remotePathBasedateYn.equalsIgnoreCase("y") ) {
	        	remotePathBasedateYn = "n";
	        }
	        else {
	        	remotePathBasedateYn = "y";
	        }
			
			// 2. 파일 이름에 일자 포함 여부 확인
	        String remoteFileBasedateYn = pmanager.getProperties(propName).getProperty(REMOTE_FILE_BASEDATE_YN);
	        if ( remoteFileBasedateYn == null || !remoteFileBasedateYn.equalsIgnoreCase("y") ) {
				remoteFileBasedateYn = "n";
	        }
	        else {
	        	remoteFileBasedateYn = "y";
	        }

	        // 2-1. 2 에 해당 시 구분자
	        String remoteFileBasedateDelim = pmanager.getProperties(propName).getProperty(REMOTE_FILE_BASEDATE_DELIM);
	        if ( remoteFileBasedateDelim == null )
	        	remoteFileBasedateDelim = "";
			
	     // 3. 임시파일 확장자
	        String remoteFileWorkingPostfix = pmanager.getProperties(propName).getProperty(REMOTE_FILE_WORKING_POSTFIX);
	        if ( remoteFileWorkingPostfix == null )
	        	remoteFileWorkingPostfix = "";

	        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();

			// 특정일자 파일을 수기로 수신요청 시, 해당 일자의 경로 설정
			logger.debug( "[FTPFS]recvFileName:" + batchDoc.getBatchMsg().getHeader().getFileName());			
			String reqFileName = batchDoc.getBatchMsg().getHeader().getFileName();
			String baseDate = DatetimeUtil.getCurrentDate();
			boolean isManualRcv = false;
			if ( reqFileName != null && reqFileName.length() > 8 ){
				if ( DatetimeUtil.isRightDate(reqFileName.substring(reqFileName.length() - 8 )) ){
					baseDate = reqFileName.substring(reqFileName.length() - 8 );
					batchDoc.getBatchMsg().getHeader().setBaseDate(baseDate);
					isManualRcv = true;
				}else{
					batchDoc.getBatchMsg().getHeader().setBaseDate(baseDate);
				}				
			}else{
				batchDoc.getBatchMsg().getHeader().setBaseDate(baseDate);
			}

			// 다운로드 서버가 일자별 경로 생성 시, 경로에 일자정보 추가.
			if ("y".equalsIgnoreCase(remotePathBasedateYn))
	        	remotePath = remotePath + baseDate;

	        String authType = pmanager.getProperties(propName).getProperty(AUTH_TYPE);
	        if (StringUtils.isBlank(authType)) {
	        	authType = AUTH_TYPE_ID; // id
	        }

	        FTPFile[] list = new FTPFile[0];
			try {
	    		list = FTPUtil.listFiles(ipaddress, port, userid, passwd, remotePath, null, true, batchDoc.getLogger(), authType);
            } catch ( Exception e ){
            	if ( !e.getMessage().contains("No such file") )
            		throw e;
            }	    		
    		for ( int inx = 0 ; inx < list.length; inx++ ){
    			FTPFile file = list[inx];	    			
    			String remoteFileName = file.getName();
    			try {
	    			
	    			if (remoteFileWorkingPostfix.length() > 0 && remoteFileName.endsWith(remoteFileWorkingPostfix)) {
						logger.debug( "[FTPFS]임시파일은 무시 " + remoteFileName + ", 임시파일 확장자 = " + remoteFileWorkingPostfix);
	    				continue;
	    			}
		            
	    			// 파일이름으로부터  등록된 파일명을 구함
	    			if (bizCodeStartIndex <= 0 || bizCodeStartIndex >= remoteFileName.length()) bizCodeStartIndex = 0;
		            if (bizCodeEndIndex   <= 0 || bizCodeEndIndex   >  remoteFileName.length()) bizCodeEndIndex = remoteFileName.length();
					String bizCode = remoteFileName.substring(bizCodeStartIndex, bizCodeEndIndex);

					reqFileName = batchDoc.getBatchMsg().getHeader().getFileName();

					// 수기요청 파일명은 아래 로직에서 기관 프로퍼티값에 따라 재설정.
			        if ( reqFileName != null && reqFileName.length() > 8 ) {
		        		reqFileName = reqFileName.substring(bizCodeStartIndex, bizCodeEndIndex);
			        }
					
					// 파일 프로퍼티그룹 명
					String propJobName = PROP_GROUP_NAME_PREFIX + "{" +
							processCode +
		    				"_" +
		    				bizCode + "}";
						        
			        // 3. 시간대별 수신이 필요한 파일여부 확인
			        String remoteFileBasetimeYn = pmanager.getProperties(propJobName).getProperty(REMOTE_FILE_BASETIME_YN);
			        if ( remoteFileBasetimeYn == null || !remoteFileBasetimeYn.equalsIgnoreCase("y") ) {
			        	remoteFileBasetimeYn = "n";
			        }
			        else {
			        	remoteFileBasetimeYn = "y";
			        }
			        
			        // 3-1. 3 에 해당 시 구분자
			        String remoteFileBasetimeDelim = pmanager.getProperties(propJobName).getProperty(REMOTE_FILE_BASETIME_DELIM);
			        if ( remoteFileBasetimeDelim == null )
			        	remoteFileBasetimeDelim = "";
	
			        // 3-2. 3 에 해당 시 기준시 추출
			        String baseTime = "";
			        if ("y".equalsIgnoreCase(remoteFileBasetimeYn)) {
			        	baseTime =  pmanager.getProperties(propJobName).getProperty(REMOTE_FILE_BASETIME);
			        	
			        	// 기준시 설정이 DEFAULT(99)일 경우 현재 시간(24HH)으로 설정
			        	if ( baseTime.equals("99") ) {
			        		baseTime = DatetimeUtil.getTimeString().substring(0,2);
			        	}
			        }
															
					// 파일이름에 날짜를 포함하는 경우, 특정일자 파일을 수신요청 시 요청일자의 파일명 설정
					if ("y".equalsIgnoreCase(remoteFileBasedateYn)) {
						// 전일자 등 날짜 추가
				        String addDays = pmanager.getProperties(propJobName).getProperty(REMOTE_FILE_BASEDATE_ADDDAYS, "0");
				        String tempBaseDate = baseDate;
				        if (!StringUtils.equals(addDays, "0")) {
					        try {
						        int days = Integer.parseInt(addDays);
						        tempBaseDate = DatetimeUtil.addDays(tempBaseDate, days);
					        } catch (Exception e) {
					        	// ignore
					        	logger.error(e.getMessage());
					        }
				        }
						reqFileName =  reqFileName + remoteFileBasedateDelim + tempBaseDate;
					}
					
					logger.debug( "[FTPFS]reqFileName: {}", reqFileName );
					logger.debug( "[FTPFS]remotePath: {}", remotePath);
					logger.debug( "[FTPFS]remoteFileName: {}", remoteFileName);
					
					boolean balreadyRcv = false;
					// 수기요청이 아닐때,
					if ( isManualRcv == false ) {
						String compareFileName = bizCode + reqFileName;
						// 특정시점(24HH)수신 파일인 경우, 자동요청시점과의 일치여부 검증
	    				if ( "y".equalsIgnoreCase(remoteFileBasetimeYn) ) {
	    					// 시간만 비교하면 이전 일짜도 가져올 수 있기 때문에 년월일시까지 비교함 
	    					compareFileName = bizCode + reqFileName + remoteFileBasetimeDelim + baseTime; 
	    				}
    					if (!remoteFileName.startsWith(compareFileName)) {
    						logger.debug( "[FTPFS]basetime not equal.. receive skipped : "+remoteFileName+" / "+compareFileName);
    						continue;
    					}
	    				// 기수신여부 확인
						balreadyRcv = LogManager.getInstance().getFileLogSuccessCheck(processCode, institutionCode, bizCode, remoteFileName);
					}
					// 수기 요청 시,
					else {
						if ( remoteFileName.startsWith(reqFileName) ) {
							String compareFileName = reqFileName;
							// 특정시점(24HH)수신 파일인 경우, 수기요청시점과의 일치여부 검증
		    				if ( "y".equalsIgnoreCase(remoteFileBasetimeYn) ) {
		    					// 시각을 붙여서 같은 시각인지 확인
		    					compareFileName = reqFileName + remoteFileBasetimeDelim + baseTime;
		    				}
	    					if (!remoteFileName.startsWith(compareFileName)) {
	    						logger.debug( "[FTPFS]basetime not equal.. receive skipped : " + remoteFileName + "/" + compareFileName);
	    						continue;
	    					}
						}
						else {
							continue;
						}
					}
	
					if ( balreadyRcv == false ) {
	
	    	            batchDoc.getBatchMsg().getHeader().setBizCode(bizCode);
						batchDoc.getBatchMsg().getHeader().setFileSize(file.getSize());
	    				batchDoc.getBatchMsg().getBody().setRecCnt(1);
	    				batchDoc.getBatchMsg().getBody().setPhaseStartTime(CalendarUtil.getCurrentTimeNoDash());
	    				// 반복 실행을 위해서 FileName을 원복
	    				String restoreFileName = batchDoc.getBatchMsg().getHeader().getFileName();
	    	            batchDoc.getBatchMsg().getHeader().setFileName(remoteFileName);
	    				LogUtil.setLogFileStart(batchDoc);
//	    				FTPUtil.retrieve(ipaddress, port, userid, passwd, remotePath + "/" + remoteFileName, batchDoc.getBatchMsg().getHeader().getFilePath(), true, batchDoc.getLogger(), authType);
    					FTPUtil.retrieve(batchDoc, ipaddress, port, userid, passwd, remotePath, file.getName(), batchDoc.getBatchMsg().getHeader().getFilePath(),
    							connTimeout, setTimeout, true, bandWidth, batchDoc.getLogger(Logger.LOGGER_ADAPTER), authType);

	    				batchDoc.getBatchMsg().getHeader().setTotRecCnt(1);
	    				String recvFile = batchDoc.getBatchMsg().getHeader().getFilePath() + File.separator + remoteFileName;
	    				File rcvRealFile = new File( recvFile );  					
	
	    				try {
	    					File rcvArchFile = new File( StringUtil.realToArchDir(recvFile) );
	    					if (rcvRealFile.exists() && rcvRealFile.length() == batchDoc.getBatchMsg().getHeader().getFileSize() ) {
	    						rcvRealFile.renameTo(rcvArchFile);
	    						File rcvRootFile = new File( StringUtil.realToRootDir(recvFile) );
	    						rcvRootFile.createNewFile();
	    					} else {
	    						throw new Exception("[FTPFS]파일수신에 실패하였습니다.");
	    					}
	    					//File 종료시간 설정
//	    					setLastActivity();
	    					batchDoc.getBatchMsg().getBody().setPhaseEndTime(CalendarUtil.getCurrentTimeNoDash());
	    					LogUtil.setLogFileEnd(batchDoc);
	    				} catch (Exception e) {
	    					LogUtil.setErrorLog(batchDoc, e.getMessage());
	    					throw e;
	    				} finally {
	    					batchDoc.getBatchMsg().getHeader().setFileName(restoreFileName);
	    				}
					}
					else {
						logger.info( "[FTPFS] " + remoteFileName + " 은 기수신되었으므로 수신하지 않습니다.");
					}
    			} catch (Exception e) {
    				logger.error(remoteFileName + " 파일 처리 중 오류, 다음 파일 처리는 계속 진행함", e);
    			}
            }
				
        } 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);
            LogUtil.setErrorLog(batchDoc, ex.getMessage());
            logger.error( errMsg, ex );
            throw ex;
		} finally {
			// SFTP는 실제 disconnect는 FTPUtil-SFTPAdaptor에서 하지만, 연결해제 로그를 찍어주기 위해 호출.
			this.disconnect();
        }

		logger.info("[FTPFS] ■ VAN사 → FEP 파일 수신 완료.");
			
	}

	@Override
	public void shutdown() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public Socket getCurrentSocket() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean connect() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public ConfigurationContext getContext() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void setControl(Object control) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void notifyMessage() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public boolean idle(long timeout) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public int getCurrentState() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public boolean isConnected() throws IOException {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean isActive() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void checkActivity() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public String getRemoteIPAddress() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void setLastActivity() {
		// TODO Auto-generated method stub
		lastActivity = System.currentTimeMillis();
	}

	@Override
	public long getFirstActivity() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public long getLastActivity() {
		// TODO Auto-generated method stub
		return 0;
	}
}
