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

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
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.ApiStatsMonth;
import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsMonthService;
import com.eactive.eai.rms.data.entity.onl.kjb.statistics.ApiStatsDayService;
import com.eactive.eai.rms.data.entity.onl.kjb.statistics.QApiStatsDay;
import com.eactive.eai.rms.data.entity.onl.kjb.statistics.QApiStatsMonth;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.jpa.impl.JPAQueryFactory;

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

	private ApiStatsDayService apiStatsDayService;
	private ApiStatsMonthService apiStatsMonthService;

	@Autowired
	public void setApiStatsDayService(ApiStatsDayService apiStatsDayService) {
		this.apiStatsDayService = apiStatsDayService;
	}

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

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

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

		LocalDate startDate = targetMonth.atDay(1);
		LocalDate endDate = targetMonth.atEndOfMonth();

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

		List<ApiStatsMonth> aggregatedData = aggregateDailyToMonthly(startDate, endDate, targetMonth);

		// 1. 기존 데이터 삭제 (QueryDSL 사용)
		JPAQueryFactory queryFactory = apiStatsMonthService.getJPAQueryFactory();
		QApiStatsMonth qm = QApiStatsMonth.apiStatsMonth;
		String targetMonthStr = targetMonth.format(DateTimeFormatter.ofPattern("yyyyMM"));
		long deletedCount = queryFactory
			.delete(qm)
			.where(qm.statTime.eq(targetMonthStr))
			.execute();
		log.info("기존 데이터 삭제: {} 건", deletedCount);

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

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

		return savedCount;
	}

	/**
	 * 일별 데이터를 월별로 집계
	 */
	private List<ApiStatsMonth> aggregateDailyToMonthly(LocalDate startDate, LocalDate endDate, YearMonth targetMonth) {
		QApiStatsDay qd = QApiStatsDay.apiStatsDay;
		JPAQueryFactory queryFactory = apiStatsDayService.getJPAQueryFactory();

		String targetMonthStr = targetMonth.format(DateTimeFormatter.ofPattern("yyyyMM"));

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

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

		return aggregatedData;
	}
}
