package com.eactive.ext.kjb.statistics.job;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Year;
import java.util.List;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

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.data.entity.onl.kjb.statistics.ApiStatsYear;
import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsYearService;
import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsMonthService;
import com.eactive.eai.rms.data.entity.onl.kjb.statistics.QApiStatsMonth;
import com.eactive.eai.rms.data.entity.onl.kjb.statistics.QApiStatsYear;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.jpa.impl.JPAQueryFactory;

/**
 * 월별 통계를 연별 통계로 집계하는 배치 작업
 * API_STATS_MONTH → API_STATS_YEAR
 * 실행 주기: 매년 1월 1일 00:30 (전년 데이터 집계)
 */
@Component
public class MonthlyToYearlyAggregationJob implements Job {
	private static final Logger log = LoggerFactory.getLogger(MonthlyToYearlyAggregationJob.class);

	private ApiStatsMonthService apiStatsMonthService;
	private ApiStatsYearService apiStatsYearService;

	@Autowired
	public void setApiStatsMonthService(ApiStatsMonthService apiStatsMonthService) {
		this.apiStatsMonthService = apiStatsMonthService;
	}

	@Autowired
	public void setApiStatsYearService(ApiStatsYearService apiStatsYearService) {
		this.apiStatsYearService = apiStatsYearService;
	}

	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException {
		
   		ApplicationContext appContext = null;
   		final MonthlyToYearlyAggregationJob selfJob;
   		try {
			appContext = (ApplicationContext) context.getScheduler().getContext().get("applicationContext");
			selfJob = appContext.getBean(MonthlyToYearlyAggregationJob.class);
   		} catch (SchedulerException e) {
   			log.error("applicationContext get module error",e);
   			return ;
   		}
		
		DataSourceType dataType = DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.APIGW);
		DataSourceContextHolder.setDataSourceType(dataType);
		try {
			Year targetYear = Year.now().minusYears(1);
			selfJob.executeManual(targetYear);
		} catch (Exception e) {
			throw new JobExecutionException(e);
		} finally {
			DataSourceContextHolder.clearDataSourceType();
		}
	}

	/**
	 * 수동 실행 메서드 (연도 지정)
	 */
	@Transactional
	public int executeManual(Year targetYear) {
		long startTime = System.currentTimeMillis();
		log.info("=== 월별→연별 통계 집계 작업 시작 ===");

		String startMonth = targetYear.toString() + "01";
		String endMonth = targetYear.toString() + "12";

		log.info("집계 대상 기간: {} ~ {}", startMonth, endMonth);

		List<ApiStatsYear> aggregatedData = aggregateMonthlyToYearly(startMonth, endMonth, targetYear);

		// 1. 기존 데이터 삭제 (QueryDSL 사용)
		JPAQueryFactory queryFactory = apiStatsYearService.getJPAQueryFactory();
		QApiStatsYear qy = QApiStatsYear.apiStatsYear;
		String targetYearStr = targetYear.toString();
		long deletedCount = queryFactory
			.delete(qy)
			.where(qy.statTime.eq(targetYearStr))
			.execute();
		log.info("기존 데이터 삭제: {} 건", deletedCount);

		// 2. 배치 INSERT
		apiStatsYearService.saveAll(aggregatedData);
		int savedCount = aggregatedData.size();

		long elapsedTime = System.currentTimeMillis() - startTime;
		log.info("=== 월별→연별 통계 집계 작업 완료 === (처리 건수: {}, 소요 시간: {}ms)", savedCount, elapsedTime);

		return savedCount;
	}

	/**
	 * 월별 데이터를 연별로 집계
	 */
	private List<ApiStatsYear> aggregateMonthlyToYearly(String startMonth, String endMonth, Year targetYear) {
		QApiStatsMonth qm = QApiStatsMonth.apiStatsMonth;
		JPAQueryFactory queryFactory = apiStatsMonthService.getJPAQueryFactory();

		String targetYearStr = targetYear.toString();

		List<ApiStatsYear> aggregatedData = queryFactory
				.select(Projections.bean(ApiStatsYear.class,
						qm.apiName,
						qm.gwInstanceId,
						qm.bizDivCode,
						qm.clientId,
						qm.inboundAdapter,
						qm.outboundAdapter,
						qm.totalCnt.sum().as("totalCnt"),
						qm.successCnt.sum().as("successCnt"),
						qm.timeoutCnt.sum().as("timeoutCnt"),
						qm.systemErrCnt.sum().as("systemErrCnt"),
						qm.bizErrCnt.sum().as("bizErrCnt"),
						qm.seq900TimeoutCnt.sum().as("seq900TimeoutCnt"),
						qm.seq900SystemErrCnt.sum().as("seq900SystemErrCnt"),
						qm.seq900BizErrCnt.sum().as("seq900BizErrCnt"),
						Expressions.cases()
				.when(qm.totalCnt.sum().gt(0))
				.then(qm.avgRespTime.multiply(qm.totalCnt).sum()
						.divide(qm.totalCnt.sum()).castToNum(BigDecimal.class))
				.otherwise((BigDecimal) null)
				.as("avgRespTime"),
						qm.minRespTime.min().castToNum(BigDecimal.class).as("minRespTime"),
						qm.maxRespTime.max().castToNum(BigDecimal.class).as("maxRespTime"),
						Expressions.cases()
				.when(qm.successCnt.sum().gt(0))
				.then(qm.p50RespTime.multiply(qm.successCnt).sum()
						.divide(qm.successCnt.sum()).castToNum(BigDecimal.class))
				.otherwise((BigDecimal) null)
				.as("p50RespTime"),
						Expressions.cases()
				.when(qm.successCnt.sum().gt(0))
				.then(qm.p95RespTime.multiply(qm.successCnt).sum()
						.divide(qm.successCnt.sum()).castToNum(BigDecimal.class))
				.otherwise((BigDecimal) null)
				.as("p95RespTime")))
				.from(qm)
				.where(qm.statTime.goe(startMonth)
						.and(qm.statTime.loe(endMonth)))
				.groupBy(qm.apiName, qm.gwInstanceId, qm.bizDivCode, qm.clientId, qm.inboundAdapter, qm.outboundAdapter)
				.fetch();

		for (ApiStatsYear statsYear : aggregatedData) {
			statsYear.setStatTime(targetYearStr);
			if (statsYear.getAvgRespTime() != null) {
				statsYear.setAvgRespTime(statsYear.getAvgRespTime().setScale(3, RoundingMode.HALF_UP));
			}
			if (statsYear.getP50RespTime() != null) {
				statsYear.setP50RespTime(statsYear.getP50RespTime().setScale(3, RoundingMode.HALF_UP));
			}
			if (statsYear.getP95RespTime() != null) {
				statsYear.setP95RespTime(statsYear.getP95RespTime().setScale(3, RoundingMode.HALF_UP));
			}
			if (statsYear.getMinRespTime() == null) {
				statsYear.setMinRespTime(BigDecimal.ZERO);
			}
			if (statsYear.getMaxRespTime() == null) {
				statsYear.setMaxRespTime(BigDecimal.ZERO);
			}
		}

		return aggregatedData;
	}
}
