package com.eactive.eai.rms.ext.cm.epb;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import com.eactive.eai.common.util.DatetimeUtil;
import com.eactive.eai.rms.common.context.MonitoringContext;
import com.eactive.eai.rms.common.datasource.DataSourceContextHolder;
import com.eactive.eai.rms.common.datasource.DataSourceType;
import com.eactive.eai.rms.common.datasource.DataSourceTypeManager;
import com.eactive.eai.rms.common.interceptor.InterceptorSkipController;
import com.eactive.eai.rms.common.interceptor.dynamicLog.DynamicLogController;
import com.eactive.eai.rms.onl.common.service.AgentUtilService;
import com.eactive.eai.rms.onl.common.util.FileWriteUtil;
import com.eactive.eai.rms.onl.manage.rule.transform.TransformManService;

@Controller
@RequestMapping(value={"/epb/cm.do","/epb/cm.json"})
public class CmController implements InterceptorSkipController, DynamicLogController{
	
	private static final Logger logger = Logger.getLogger("DYNAMIC_LOGGER");
	private static final String resultView ="common/loadResult";

	@Autowired
	@Qualifier("monitoringContext")
	private MonitoringContext monitoringContext; 

	@Autowired
    private CmService cmService;    
	
	@Autowired
    private CmBATService cmBatService;    
	
	@Autowired
	private TransformManService transformManService;
	
	@Autowired
	@Qualifier("onlAgentUtilService")
    public AgentUtilService agentUtilService;
	
	@RequestMapping(params = { "cmd=load" })
	public ModelAndView load(HttpServletRequest request, String deployId, String serviceType, String appCode, String rollbackYn) throws Exception {
        boolean isJSONResult=false;
        if (request.getRequestURI().endsWith("json")){
        	isJSONResult = true;
        }
		try {
//			if (SystemUtil.isDevServer()) {
//				logger.info("CmController: 해당 시스템에선 사용할 수 없습니다. ["+ System.getProperty("eai.systemmode") +"]");
//	        	HashMap<String,String> result = new HashMap<String,String>();
//		    	result.put("status", "error");
//		    	result.put("message", "해당 시스템에선 사용할 수 없습니다. ["+ System.getProperty("eai.systemmode") +"]");
//		    	if (isJSONResult){
//		    		return new ModelAndView("jsonView", result);
//		    	}else{
//		    		return new ModelAndView(resultView, "result", result);
//		    	}
//			}
			Properties prop = monitoringContext.getProperties();
			String uploadPath   = prop.getProperty(MonitoringContext.CM_DEVELOPER_UPLOAD_PATH, 	"/dat/eaiData/upload/");
			String afterPath    = prop.getProperty(MonitoringContext.CM_DEVELOPER_AFTER_PATH, 	"/dat/eaiData/after/");
			String backupPath   = prop.getProperty(MonitoringContext.CM_DEVELOPER_BACKUP_PATH, 	"/dat/eaiData/backup/");
			
			run(uploadPath, afterPath, backupPath, serviceType, appCode, deployId, rollbackYn );
			
			HashMap result = new HashMap();
	    	result.put("status", "success");
	    	result.put("message", "");
	    	if (isJSONResult){
	    		return new ModelAndView("jsonView", result);
	    	}else{
	    		return new ModelAndView(resultView, "result", result);
	    	}
			
		} catch (Exception e) {
			logger.error("CmController: property module error", e);
			//throw new Exception(e);
	    	HashMap result = new HashMap();
	    	result.put("status", "error");
	    	result.put("message", e.getMessage());
	    	if (isJSONResult){
	    		return new ModelAndView("jsonView", result);
	    	}else{
	    		return new ModelAndView(resultView, "result", result);
	    	}
		}
		
	}
	/**
	 * path : /dat/eaiData/{serviceType}/{deployId}/{appCode}/
	 * 1. 배포 id 추출
	 * 2. 배포 id 기준으로 목록 수집
	 * 3. 배포 id 기준으로 layout, transform, interface 로딩
	 * 
	 * @throws Exception
	 */
	private void run(String uploadPath, String afterPath, String backupPath, String serviceType, String appCode, String deployId, String rollbackYn ) throws Exception {
		
		String path = uploadPath + serviceType + File.separator + deployId + File.separator + appCode;
		
		File uploadDir = new File(path);
		if (!uploadDir.exists()){
			throw new Exception("폴더존재안함 +" + uploadDir.getAbsolutePath());
		}
		
		// 시스템셋팅(datasourceType)
		DataSourceType dt = DataSourceTypeManager.getDataSourceType(serviceType);
		if (dt == null){
			logger.info("not support serviceType : "+ serviceType);
			return;
		}
		DataSourceContextHolder.setDataSourceType(dt);
		
		
		HashMap<String,ArrayList<String>> interfaceSync = new HashMap<String,ArrayList<String>>();
		ArrayList<String> layoutSync = new ArrayList<String>();
		ArrayList<String> transformSync = new ArrayList<String>();
		List<String> additionalTransformSync = new ArrayList<String>();
		//layout 식별
		File layoutDir = new File(path + File.separator + FileWriteUtil.LAYOUT);
		if (layoutDir.exists()){
			//DB 적재하는 부분
			layoutSync = cmService.insertLayoutFile(dt,layoutDir);
		}
		
		//변환 식별
		File transformDir = new File(path + File.separator + FileWriteUtil.TRANSFORM);
		if (transformDir.exists()){
			//DB 적재하는 부분
			transformSync = cmService.insertTransformFile(dt,transformDir);
		}
		
		// layout을 갖고있는 transform중 upload 파일목록으로 들어온 transform목록과 중복을 제거  
		if(layoutSync.size() > 0) {
			additionalTransformSync = transformManService.selectTransformListByLayoutList(layoutSync);
			additionalTransformSync = filterDuplicateTransformList(transformSync, additionalTransformSync);
			
			if(additionalTransformSync.size() > 0) {
				transformSync.addAll(additionalTransformSync);
				
				logger.info("additional transform sync list ▼");
				additionalTransformSync.forEach(logger::info);
			}
		}
		
		//인터페이스 식별
		File interfaceDir = new File(path + File.separator + FileWriteUtil.INTERFACE);
		if (interfaceDir.exists()){
			//DB 적재하는 부분
			interfaceSync =  cmService.insertInterfaceFile(dt,interfaceDir);
		}
		
		
		//메모리 동기화
		//레이아웃
		if (layoutSync != null && layoutSync.size() > 0){
			cmService.layoutMemorySync(layoutSync);
		}
		//변환
		if (transformSync != null && transformSync.size() > 0){
			cmService.transformMemorySync(transformSync);
		}
		//인터페이스
		if (interfaceSync != null && interfaceSync.size() > 0){
			cmService.interfaceMemorySync(interfaceSync.get(FileWriteUtil.INTERFACE),interfaceSync.get(FileWriteUtil.INTERFACE+"EXT"));
		}
		
		//폴더 이동
		// 5. after 경로로 이동
		//after folder check & move
		// 이동순서 uploadFolder => afterFolder => backupFolder
		// uploadFolder는 원본데이터, afterFolder는 적용된 데이터(최근꺼만 존재), backupFolder 은 적용된게 여러개일경우(히스토리성)
		
		//afterfolder에 존재하면 backupFolder로 이관 중복안나게 폴더에 날짜포맷추가
    	File afterFolder = new File(afterPath + File.separator + serviceType + File.separator + deployId);
    	logger.info("afterFolder : " + afterFolder.getAbsolutePath() + " , " + afterFolder.exists());
    	if (afterFolder.exists()){
    		File backupFolder = new File(backupPath+ File.separator + serviceType + File.separator+ deployId + "_" + DatetimeUtil.getCurrentTimeMsNoMark(afterFolder.lastModified()));
    		logger.info("backupFolder : " + backupFolder.getAbsolutePath());
    		if (backupFolder.exists() && backupFolder.isDirectory()){
    			FileUtils.deleteDirectory(backupFolder);
    		}
    		boolean result = afterFolder.renameTo(backupFolder);
    		logger.info("rename "+afterFolder.getAbsolutePath()+" to   " + backupFolder.getAbsolutePath() + " result=" + result);
    	}
    	//upload folder to after folder (적용된거 이동)
    	File uploadFolder = new File(uploadPath + File.separator + serviceType + File.separator + deployId );
    	logger.info("uploadFolder : " + uploadFolder.getAbsolutePath() + " , "+uploadFolder.exists() );
    	if (uploadFolder.exists()){
    		
    		if(additionalTransformSync.size() > 0) {
    			// 추가 reload Transform 존재 할 시에는 reload 폴더 생성 후 추가한 Transform의 정보를 기록
        		StringBuilder reloadedTransformList = new StringBuilder();  
        		additionalTransformSync.forEach(tName -> reloadedTransformList.append(tName).append(System.getProperty("line.separator")));
        		
        		File reloadFile = new File(path + File.separator + "additionalReload.txt");
        		FileUtils.writeStringToFile(reloadFile, reloadedTransformList.toString());
    		}
    		
    		boolean result = uploadFolder.renameTo(afterFolder);
    		if(result) {
    			logger.info("rename folder success : "+uploadFolder.getAbsolutePath()+" to " + afterFolder.getAbsolutePath());
    		} else {
    			logger.error("rename folder error : "+uploadFolder.getAbsolutePath()+" to " + afterFolder.getAbsolutePath());
    		}
    	} else {
    		logger.error("upload folder does not exist : " + uploadFolder.getAbsolutePath());
    	}
	}
	
	private List<String> filterDuplicateTransformList(ArrayList<String> transformSync, List<String> additionalTransformSync) {
		return additionalTransformSync.stream().filter(addi -> transformSync.stream().noneMatch(Predicate.isEqual(addi))).collect(Collectors.toList());
	}
	
	/**
	 * path : /dat/eaiData/{serviceType}/{deployId}/{appCode}/
	 * 1. 배포 id 추출
	 * 2. 배포 id 기준으로 목록 수집
	 * 3. 배포 id 기준으로 layout, transform, interface 로딩
	 * 
	 * @throws Exception
	 */
	private void runOnlSync(String path, DataSourceType dt) throws Exception {
		HashMap<String,ArrayList<String>> interfaceSync = new HashMap<String,ArrayList<String>>();
		ArrayList<String> layoutSync = new ArrayList<String>();
		ArrayList<String> transformSync = new ArrayList<String>();
		//layout 식별
		File layoutDir = new File(path + File.separator + FileWriteUtil.LAYOUT);
		if (layoutDir.exists()){
			//DB 적재하는 부분
			layoutSync = cmService.insertLayoutFile(dt,layoutDir);
		}
		
		//변환 식별
		File transformDir = new File(path + File.separator + FileWriteUtil.TRANSFORM);
		if (transformDir.exists()){
			//DB 적재하는 부분
			transformSync = cmService.insertTransformFile(dt,transformDir);
		}
		
		//인터페이스 식별
		File interfaceDir = new File(path + File.separator + FileWriteUtil.INTERFACE);
		if (interfaceDir.exists()){
			//DB 적재하는 부분
			interfaceSync =  cmService.insertInterfaceFile(dt,interfaceDir);
		}
		
		
		//메모리 동기화
		//레이아웃
		if (layoutSync != null && layoutSync.size() > 0){
			cmService.layoutMemorySync(layoutSync);
		}
		//변환
		if (transformSync != null && transformSync.size() > 0){
			cmService.transformMemorySync(transformSync);
		}
		//인터페이스
		if (interfaceSync != null && interfaceSync.size() > 0){
			cmService.interfaceMemorySync(interfaceSync.get(FileWriteUtil.INTERFACE),interfaceSync.get(FileWriteUtil.INTERFACE+"EXT"));
		}
	}
	
	/**
	 * path : /dat/eaiData/{serviceType}/{deployId}/{appCode}/
	 * 1. 배포 id 추출
	 * 2. 배포 id 기준으로 목록 수집
	 * 3. 배포 id 기준으로 layout, transform, interface 로딩
	 * 
	 * @throws Exception
	 */
	private void runEbtSync(String path, DataSourceType dt ) throws Exception {
		HashMap<String,ArrayList<String>> interfaceSync = new HashMap<String,ArrayList<String>>();
		//인터페이스 식별
		File interfaceDir = new File(path + File.separator + FileWriteUtil.INTERFACE);
		if (interfaceDir.exists()){
			//DB 적재하는 부분
			interfaceSync =  cmBatService.insertInterfaceFile(dt,interfaceDir);
		}
	}
}
