package com.eactive.eai.rms.onl.loader.service;

import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.SchedulerException;
import org.springframework.context.ApplicationContext;

import com.eactive.eai.common.util.DatetimeUtil;
import com.eactive.eai.rms.bat.loader.service.TransactionLoaderServiceImpl;
import com.eactive.eai.rms.common.datasource.DataSourceTypeManager;
import com.eactive.eai.rms.onl.common.util.DateUtil;
import com.eactive.eai.rms.onl.loader.vo.RuleDeployJobVo;

public class RuleDeployJob implements Job {
	private static final Log logger = LogFactory.getLog(RuleDeployJob.class);

	private static final int FAILED_MAX_CNT = 10;
	private static final int DEFAULT_DELETE_HISTORY_DAYS = 365;
	private static final int MINIMUM_DELETE_HISTORY_DAYS = 90;

	private RuleDeployJobService ruleDeployJobService;
	private TransactionLoaderService loaderService;
	private TransactionLoaderServiceImpl batLoaderService;

	/**
	 * DJ01 테이블에서 현재시간보다 이전인 데이터 작업 대상이다.<br>
	 * 목록조회후 status working으로 업데이트하고 작업 수행한다.(다른 스레드 중복작업 방지)<br>
	 * 실패하면 failedCnt ++ 한다.<br>
	 * failCnt가 특정 횟수 넘어가면 status를 failed로 처리한다.(더이상 수행 안함)<br>
	 */
	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException {
		logger.info("START RULE_DEPLOY_JOB...");

		ApplicationContext appContext = null;
		try {
			appContext = (ApplicationContext) context.getScheduler().getContext().get("applicationContext");
		} catch (SchedulerException e) {
			logger.error("applicationContext get module error", e);
			return;
		}

		ruleDeployJobService = (RuleDeployJobService) appContext.getBean("ruleDeployJobService");

		try {
			// 1. Job 목록 확보(최대 100개)
			RuleDeployJobVo searchJobVo = new RuleDeployJobVo();
			searchJobVo.setStatus("standby");
			searchJobVo.setScheduledDateTime(DatetimeUtil.getCurrentDateTime());
			List<RuleDeployJobVo> jobList = ruleDeployJobService.selectJobAll(searchJobVo, 1, 100);
			if (jobList == null || jobList.size() == 0) {
				logger.info("...NO HAVE RULE_DEPLOY_JOB...");
				logger.info("...RULE_DEPLOY_JOB END");
				return;
			}

			// 2. 작업 수행
			batLoaderService = (TransactionLoaderServiceImpl) appContext.getBean("batLoaderService");
			loaderService = (TransactionLoaderService) appContext.getBean("loaderService");
			int failedJobCnt = 0;
			for (RuleDeployJobVo ruleDeployJobVo : jobList) {
				if (!doRun(ruleDeployJobVo)) {
					failedJobCnt++;
				}
			}

			if (failedJobCnt > 0) {
				throw new Exception(failedJobCnt + "times failed");
			}
		} catch (Exception e) {
			logger.error(e.getMessage());
			logger.info("...RULE_DEPLOY_JOB END");
			throw new JobExecutionException(e);
		}

		// 오래된 히스토리 삭제
		cleansingHistoryData(context);
		logger.info("...RULE_DEPLOY_JOB END");
	}

	private boolean doRun(RuleDeployJobVo ruleDeployJobVo) {
		logger.info(ruleDeployJobVo.getJobId() + ": Start");

		try {
			// 다른 쓰레드에 의한 중복작업 방지를 위해 다시 체크
			RuleDeployJobVo jobVo = ruleDeployJobService.selectOne(ruleDeployJobVo.getJobId());
			if (!StringUtils.equals(jobVo.getStatus(), "standby")) {
				logger.info(jobVo.getJobId() + ": Other Thread run..");
				return true;
			}

			// 다른 쓰레드에 의한 중복작업 방지를 위해 상태 업데이트
			jobVo.setStatus("working");
			jobVo.setLastJobTime(DatetimeUtil.getCurrentDateTime());
			ruleDeployJobService.update(jobVo);
			
			if (StringUtils.equals(jobVo.getServiceType(), DataSourceTypeManager.BAT)) {
				if (!batLoaderService.ruleDataSaveAndLoading(jobVo)) {
					throw new Exception();
				}
			} else {
				if (!loaderService.ruleDataSaveAndLoading(jobVo)) {
					throw new Exception();
				}
			}

			jobVo.setStatus("finished");
			jobVo.setFailedMsg("");
			ruleDeployJobService.update(jobVo);

			logger.info(jobVo.getJobId() + ": Finished");
			return true;
		} catch (Exception e) {
			processFailJob(ruleDeployJobVo, StringUtils.left(e.getMessage(), 500));

			logger.error(e.getMessage());
			logger.info(ruleDeployJobVo.getJobId() + ": Failed");
			return false;
		}
	}

	private void processFailJob(RuleDeployJobVo jobVo, String failedMsg) {
		try {
			jobVo.setLastJobTime(DatetimeUtil.getCurrentDateTime());
			jobVo.setFailedCnt(jobVo.getFailedCnt() + 1);
			if (jobVo.getFailedCnt() < FAILED_MAX_CNT) {
				jobVo.setStatus("standby");
			} else {
				jobVo.setStatus("failed");
			}
			jobVo.setFailedMsg(String.format("%s: %s", DatetimeUtil.getTimeStampString(), failedMsg));
			ruleDeployJobService.update(jobVo);
		} catch (Exception e) {
			logger.error(e.getMessage());
		}
	}

	private void cleansingHistoryData(JobExecutionContext context) {
		try {
			int beforeDays = DEFAULT_DELETE_HISTORY_DAYS; // 1년
			try {
				beforeDays = Integer
						.parseInt(context.getJobDetail().getJobDataMap().getString("delete.history.before.days"));
			} catch (Exception e) {
			}

			if (beforeDays < MINIMUM_DELETE_HISTORY_DAYS) {
				beforeDays = MINIMUM_DELETE_HISTORY_DAYS;
			}

			ruleDeployJobService.deleteHistory(DateUtil.getDateTime("yyyyMMddhhmmssSS", -beforeDays));
		} catch (Exception e) {
			logger.error(e.getMessage());
		}
	}
}
