package com.eactive.eai.rms.ext.djb.job;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;

import com.eactive.eai.rms.common.context.MonitoringContext;
import com.eactive.eai.rms.common.datasource.DataSourceContextHolder;
import com.eactive.eai.rms.common.datasource.DataSourceTypeManager;
import com.eactive.eai.rms.common.util.CommonUtil;
import com.eactive.eai.rms.ext.djb.apistatus.ApiStatusService;
import com.eactive.eai.rms.onl.common.util.DateUtil;

/**
 *  Job - Quartz Job
 * API_STATS_MINUTE 데이터를 1분마다 조회하여 Api 상태를 판단하여 API_STATUS 테이블을 insert/update 한다.
 * & api 상태가 변경된 경우, 알림 테이블에 저장한다
 *
 * <p>Cron Schedule 권장:</p>
 * <ul>
 *   <li>기본: 0 * * * * ? (매분 1회 실행)</li>
 * </ul>
 *
 * <p>Job 파라미터 (JobDataMap):</p>
 * <ul>
 *   <li>
 *   	<b>api.status.error.range_minute</b>: API 장애기준 판단 시간간격 (단위: 분, 기본값: 1)
 *   	<b>api.status.error.rate</b>: API 장애기준 오류건수 비율(%) (단위: 시간, 기본값: 100)
 *   	<b>api.status.delay.range_minute</b>: API 지연기준 판단 시간간격 (단위: 분, 기본값: 1)
 *   	<b>api.status.delay.avg_resp_time</b>: API 지연기준 평균응답시간 (단위: 밀리세컨드, 기본값: 10000)
 *   </li>
 * </ul>
 *
 * </pre>
 */
@DisallowConcurrentExecution
public class ApiStatusMonitorJob implements Job {

    private static final Logger log = LoggerFactory.getLogger(ApiStatusMonitorJob.class);

    /** Job 파라미터 키: API 장애기준 시간간격(분) */
    public static final String KEY_API_STATUS_ERROR_RANGE_MINUTE = "api.status.error.range_minute";

    /** Job 파라미터 키: API 장애기준 오류건수 비율(%) */
    public static final String KEY_API_STATUS_ERROR_RATE = "api.status.error.rate";
    
    /** Job 파라미터 키: API 지연기준 시간간격(분) */
    public static final String KEY_API_STATUS_DELAY_RANGE_MINUTE = "api.status.delay.range_minute";
    
    /** Job 파라미터 키: API 지연기준 평균응답시간(ms) */
    public static final String KEY_API_STATUS_DELAY_AVG_RESP_TIME = "api.status.delay.avg_resp_time";
    
    public static final String DEFAULT_ERROR_RANGE_MINUTE = "1";
    public static final String DEFAULT_ERROR_RATE  = "100";
    public static final String DEFAULT_DELAY_RANGE_MINUTE = "1";
    public static final String DEFAULT_DELAY_AVG_RESP_TIME = "10000";

    private transient MonitoringContext monitoringContext;

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();

        // execute.instances 가 지정된 경우 해당 인스턴스에서만 실행
        String allowedInstances = jobDataMap.getString("execute.instances");
        if (StringUtils.isNotEmpty(allowedInstances)) {
            Set<String> instanceSet = Arrays.stream(allowedInstances.split(","))
                    .map(s -> s.trim().toLowerCase())
                    .collect(Collectors.toSet());
            String currentInstance = StringUtils.defaultString(System.getProperty("inst.Name")).trim().toLowerCase();
            if (!instanceSet.contains(currentInstance)) {
                log.debug("Job 실행 인스턴스 제한 - 현재: {}, 허용: {}", currentInstance, allowedInstances);
                return;
            }
        }

        log.info("*** START ApiStatusUpdateJob run({})", CommonUtil.getToday("yyyy-MM-dd HH:mm"));

        // Job 파라미터 로깅
        logJobParameters(jobDataMap);
        
        HashMap<String, String> param = this.checkParameters(jobDataMap);
        

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

        monitoringContext = (MonitoringContext) appContext.getBean("monitoringContext");
        ApiStatusService apiStatusUpdateService = appContext.getBean(ApiStatusService.class);

        DataSourceContextHolder.setDataSourceType(
                DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.APIGW));
        try {
        	apiStatusUpdateService.updateApiStatus(param);
        	
        } catch (Exception e) {
            log.error("ApiStatusUpdateJob execution failed", e);
            throw new JobExecutionException(e);
        } finally {
            DataSourceContextHolder.clearDataSourceType();
        }

        log.info("*** END ApiStatusUpdateJob run({})", DateUtil.getDateTime("yyyy-MM-dd HH:mm"));
    }

    /**
     * Job 파라미터 로깅
     */
    private void logJobParameters(JobDataMap jobDataMap) {
        if (jobDataMap == null || jobDataMap.isEmpty()) {
            log.debug("Job 파라미터 없음 (기본값 사용)");
            return;
        }

        StringBuilder sb = new StringBuilder("Job 파라미터: ");
        for (String key : jobDataMap.getKeys()) {
            sb.append(key).append("=").append(jobDataMap.getString(key)).append(", ");
        }
        log.info(sb.toString());
    }

    /**
     * 집계 범위 시간 파라미터 파싱
     *
     * @param jobDataMap Job 파라미터 맵
     * @return 집계 범위 시간 (파싱 실패 시 기본값 반환)
     */
    private HashMap<String,String> checkParameters(JobDataMap jobDataMap) {
    	
    	HashMap<String, String> param = new HashMap<String, String>();
    	
        if (jobDataMap == null) {
        	param.put("errorRangeMinute", DEFAULT_ERROR_RANGE_MINUTE);	//1분동안
        	param.put("errorRate", DEFAULT_ERROR_RATE);					//에러가 100% 발생시 '장애'로 판단
        	param.put("delayRangeMinute", DEFAULT_DELAY_RANGE_MINUTE);	//1분동안
        	param.put("delayAvgRespTime", DEFAULT_DELAY_AVG_RESP_TIME);	//평균 응답속도가 10초 이상이면 '지연'으로 판단
            return param;
        }
        
        if (StringUtils.isEmpty(jobDataMap.getString(KEY_API_STATUS_ERROR_RANGE_MINUTE))) {
        	param.put("errorRangeMinute", DEFAULT_ERROR_RANGE_MINUTE);
        } else {
        	param.put("errorRangeMinute", jobDataMap.getString(KEY_API_STATUS_ERROR_RANGE_MINUTE));
        }
        
        if (StringUtils.isEmpty(jobDataMap.getString(KEY_API_STATUS_ERROR_RATE))) {
        	param.put("errorRate", DEFAULT_ERROR_RATE);
        } else {
        	param.put("errorRate", jobDataMap.getString(KEY_API_STATUS_ERROR_RATE));
        }
        
        if (StringUtils.isEmpty(jobDataMap.getString(KEY_API_STATUS_DELAY_RANGE_MINUTE))) {
        	param.put("delayRangeMinute", DEFAULT_DELAY_RANGE_MINUTE);
        } else {
        	param.put("delayRangeMinute", jobDataMap.getString(KEY_API_STATUS_DELAY_RANGE_MINUTE));
        }
        
        if (StringUtils.isEmpty(jobDataMap.getString(KEY_API_STATUS_DELAY_AVG_RESP_TIME))) {
        	param.put("delayAvgRespTime", DEFAULT_DELAY_AVG_RESP_TIME);
        } else {
        	param.put("delayAvgRespTime", jobDataMap.getString(KEY_API_STATUS_DELAY_AVG_RESP_TIME));
        }

        return param;
    }

    /**
     * 개발 모드 여부 확인
     * @return eai.systemmode=D 이면 true
     */
    private boolean isDevMode() {
        String systemMode = System.getProperty("eai.systemmode", "");
        return "D".equalsIgnoreCase(systemMode);
    }
}
