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

import com.eactive.eai.rms.common.context.MonitoringContext;
import com.eactive.eai.rms.common.util.CommonUtil;
import com.eactive.eai.rms.onl.apim.obp.ObpGwMetricManService;
import com.eactive.eai.rms.onl.common.util.DateUtil;
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;

/**
 * ObpGwMetric Hour Job - Quartz Job
 * API Gateway 로그 데이터를 1시간 단위로 집계하여 ObpGwMetric 테이블에 저장
 *
 * <p>Cron Schedule 권장:</p>
 * <ul>
 *   <li>기본: 0 5 * * * ? (매시 05분 1회 실행)</li>
 *   <li>보정 (15분): 0 5,20,35,50 * * * ? (15분 간격 4회 실행)</li>
 *   <li>보정 (10분): 0 5,15,25,35,45,55 * * * ? (10분 간격 6회 실행)</li>
 * </ul>
 *
 * <p>Job 파라미터 (JobDataMap):</p>
 * <ul>
 *   <li><b>aggregation.hour.range</b>: 집계 범위 시간 (단위: 시간, 기본값: 1)
 *       <ul>
 *         <li>1: 직전 1시간만 집계</li>
 *         <li>3: 직전 3시간 각각 집계</li>
 *         <li>5: 직전 5시간 각각 집계</li>
 *       </ul>
 *   </li>
 * </ul>
 *
 * <p>예시 (Quartz 스케줄 등록 시):</p>
 * <pre>
 * JobDataMap jobDataMap = new JobDataMap();
 * jobDataMap.put("aggregation.hour.range", "3");
 * </pre>
 */
@DisallowConcurrentExecution
public class ObpGwMetricHourJob implements Job {

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

    /** Job 파라미터 키: 집계 범위 시간 */
    public static final String PARAM_AGGREGATION_HOUR_RANGE = "aggregation.hour.range";

    /** 기본 집계 범위 시간 (시간 단위) */
    public static final int DEFAULT_AGGREGATION_HOUR_RANGE = 1;

    private transient MonitoringContext monitoringContext;

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        log.info("*** START ObpGwMetricHourJob run({})", CommonUtil.getToday("yyyy-MM-dd HH:mm"));

        // Job 파라미터 로깅
        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
        logJobParameters(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");
        ObpGwMetricManService obpGwMetricManService = appContext.getBean(ObpGwMetricManService.class);

        // 집계 범위 시간 파라미터 파싱
        int aggregationHourRange = parseAggregationHourRange(jobDataMap);

        try {
            obpGwMetricManService.executeHourlyAggregation(aggregationHourRange);
        } catch (Exception e) {
            log.error("ObpGwMetricHourJob execution failed", e);
            throw new JobExecutionException(e);
        }

        log.info("*** END ObpGwMetricHourJob 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 int parseAggregationHourRange(JobDataMap jobDataMap) {
        if (jobDataMap == null) {
            return DEFAULT_AGGREGATION_HOUR_RANGE;
        }

        try {
            String value = jobDataMap.getString(PARAM_AGGREGATION_HOUR_RANGE);
            if (value != null && !value.trim().isEmpty()) {
                int parsedValue = Integer.parseInt(value.trim());
                if (parsedValue < 1) {
                    log.warn("aggregation.hour.range 값이 1 미만입니다. 기본값({}) 사용: input={}",
                            DEFAULT_AGGREGATION_HOUR_RANGE, parsedValue);
                    return DEFAULT_AGGREGATION_HOUR_RANGE;
                }
                // 개발 모드(D)가 아닌 경우에만 24시간 제한 적용
                if (parsedValue > 24 && !isDevMode()) {
                    log.warn("aggregation.hour.range 값이 24 초과입니다. 24로 제한: input={}", parsedValue);
                    return 24;
                }
                return parsedValue;
            }
        } catch (NumberFormatException e) {
            log.warn("aggregation.hour.range 파싱 실패, 기본값({}) 사용: {}",
                    DEFAULT_AGGREGATION_HOUR_RANGE, e.getMessage());
        }

        return DEFAULT_AGGREGATION_HOUR_RANGE;
    }

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