package com.eactive.eai.custom.statistics.scheduler;

import java.util.List;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.eactive.eai.common.util.Logger;
import com.eactive.eai.custom.statistics.collector.ApiStatsCollector;
import com.eactive.eai.custom.statistics.config.ApiStatsConfig;
import com.eactive.eai.custom.statistics.persistence.ApiStatsHourLogger;
import com.eactive.eai.custom.statistics.persistence.ApiStatsMinuteLogger;
import com.eactive.eai.data.entity.custom.statistics.ApiStatsHour;
import com.eactive.eai.data.entity.custom.statistics.ApiStatsMinute;

import lombok.RequiredArgsConstructor;

/**
 * API 통계 스케줄러
 * - 분단위: 매 분 5초에 실행하여 집계된 통계를 DB에 저장
 * - 시간별: 매 시간 5분에 실행하여 집계된 통계를 DB에 저장
 */
@Component
@RequiredArgsConstructor
public class ApiStatsScheduler {
	
	private static Logger log = Logger.getLogger(Logger.LOGGER_DEFAULT);

	private final ApiStatsCollector collector;
	private final ApiStatsMinuteLogger minuteLogger;
	private final ApiStatsHourLogger hourLogger;
	private final ApiStatsConfig apiStatsConfig;

	// 이전 설정값 추적 (설정 변경 시 flush용)
	private volatile boolean previousMinuteEnabled = true;
	private volatile boolean previousHourEnabled = true;

	/**
	 * 초기화 시 설정값 로깅
	 */
	@PostConstruct
	public void init() {
		log.info("ApiStatsScheduler initialized");
		apiStatsConfig.logCurrentConfig();
	}

	/**
	 * 매 분마다 실행 (매 분 10초에 실행)
	 * - 이전 분의 완료된 통계를 DB에 저장
	 * - 10초 여유를 두어 이전 분 데이터가 완전히 수집되도록 함
	 */
	@Scheduled(cron = "10 * * * * ?")
	public void saveMinuteStats() {
		boolean currentEnabled = apiStatsConfig.isMinuteEnabled();

		// 설정이 enabled → disabled로 변경된 경우: 남은 데이터 모두 flush
		if (previousMinuteEnabled && !currentEnabled) {
			log.info("Minute stats disabled, flushing all remaining data...");
			flushAllMinuteStats();
			previousMinuteEnabled = currentEnabled;
			return;
		}

		// 현재 상태 업데이트
		previousMinuteEnabled = currentEnabled;

		// 분단위 통계 수집 비활성화 체크
		if (!currentEnabled) {
			log.debug("API minute stats collection is disabled, skipping save");
			return;
		}

		try {
			log.debug("Starting minute stats flush...");

			// 완료된 분의 통계 데이터 가져오기
			List<ApiStatsMinute> statsList = collector.flushCompletedMinutes();

			if (statsList.isEmpty()) {
				log.debug("No stats to save");
				return;
			}

			// DB에 저장
			int savedCount = 0;
			int errorCount = 0;

			for (ApiStatsMinute stats : statsList) {
				try {
					minuteLogger.log(stats);
					savedCount++;

					if (log.isDebugEnabled()) {
						log.debug("Saved minute stats: {} - total={}, success={}, p95={}ms",
							stats.getApiName(),
							stats.getTotalCnt(),
							stats.getSuccessCnt(),
							stats.getP95RespTime());
					}

				} catch (Exception e) {
					errorCount++;
					log.error("Failed to save minute stats: {}", stats, e);
				}
			}

			log.info("Minute stats saved: success={}, error={}, active_minute_aggregators={}",
				savedCount, errorCount, collector.getActiveMinuteAggregatorCount());

		} catch (Exception e) {
			log.error("Failed to flush minute stats", e);
		}
	}

	/**
	 * 매 시간마다 실행 (매 시간 10분에 실행)
	 * - 이전 시간의 완료된 통계를 DB에 저장
	 * - 10분 여유를 두어 이전 시간 데이터가 완전히 수집되도록 함
	 */
	@Scheduled(cron = "0 10 * * * ?")
	public void saveHourStats() {
		boolean currentEnabled = apiStatsConfig.isHourEnabled();

		// 설정이 enabled → disabled로 변경된 경우: 남은 데이터 모두 flush
		if (previousHourEnabled && !currentEnabled) {
			log.info("Hour stats disabled, flushing all remaining data...");
			flushAllHourStats();
			previousHourEnabled = currentEnabled;
			return;
		}

		// 현재 상태 업데이트
		previousHourEnabled = currentEnabled;

		// 시간별 통계 수집 비활성화 체크
		if (!currentEnabled) {
			log.debug("API hour stats collection is disabled, skipping save");
			return;
		}

		try {
			log.debug("Starting hour stats flush...");

			// 완료된 시간의 통계 데이터 가져오기
			List<ApiStatsHour> statsList = collector.flushCompletedHours();

			if (statsList.isEmpty()) {
				log.debug("No hour stats to save");
				return;
			}

			// DB에 저장
			int savedCount = 0;
			int errorCount = 0;

			for (ApiStatsHour stats : statsList) {
				try {
					hourLogger.log(stats);
					savedCount++;

					if (log.isDebugEnabled()) {
						log.debug("Saved hour stats: {} - total={}, success={}, p95={}ms",
							stats.getApiName(),
							stats.getTotalCnt(),
							stats.getSuccessCnt(),
							stats.getP95RespTime());
					}

				} catch (Exception e) {
					errorCount++;
					log.error("Failed to save hour stats: {}", stats, e);
				}
			}

			log.info("Hour stats saved: success={}, error={}, active_hour_aggregators={}",
				savedCount, errorCount, collector.getActiveHourAggregatorCount());

		} catch (Exception e) {
			log.error("Failed to flush hour stats", e);
		}
	}

	/**
	 * 분단위 통계 모두 flush (설정 변경 시)
	 */
	private void flushAllMinuteStats() {
		try {
			List<ApiStatsMinute> statsList = collector.flushAllMinutes();

			if (statsList.isEmpty()) {
				log.info("No minute stats to flush");
				return;
			}

			int savedCount = 0;
			int errorCount = 0;

			for (ApiStatsMinute stats : statsList) {
				try {
					minuteLogger.log(stats);
					savedCount++;
				} catch (Exception e) {
					errorCount++;
					log.error("Failed to save minute stats during config change: {}", stats, e);
				}
			}

			log.info("Flushed all minute stats due to config change: success={}, error={}", savedCount, errorCount);

		} catch (Exception e) {
			log.error("Failed to flush all minute stats", e);
		}
	}

	/**
	 * 시간별 통계 모두 flush (설정 변경 시)
	 */
	private void flushAllHourStats() {
		try {
			List<ApiStatsHour> statsList = collector.flushAllHours();

			if (statsList.isEmpty()) {
				log.info("No hour stats to flush");
				return;
			}

			int savedCount = 0;
			int errorCount = 0;

			for (ApiStatsHour stats : statsList) {
				try {
					hourLogger.log(stats);
					savedCount++;
				} catch (Exception e) {
					errorCount++;
					log.error("Failed to save hour stats during config change: {}", stats, e);
				}
			}

			log.info("Flushed all hour stats due to config change: success={}, error={}", savedCount, errorCount);

		} catch (Exception e) {
			log.error("Failed to flush all hour stats", e);
		}
	}

	/**
	 * 애플리케이션 종료 시 남은 통계 저장
	 */
	@PreDestroy
	public void shutdown() {
		try {
			log.info("Shutting down ApiStatsScheduler, flushing remaining stats...");

			// 분단위 통계 저장
			List<ApiStatsMinute> minuteStatsList = collector.flushAllMinutes();
			if (!minuteStatsList.isEmpty()) {
				for (ApiStatsMinute stats : minuteStatsList) {
					try {
						minuteLogger.log(stats);
					} catch (Exception e) {
						log.error("Failed to save minute stats during shutdown: {}", stats, e);
					}
				}
				log.info("Saved {} remaining minute stats during shutdown", minuteStatsList.size());
			}

			// 시간별 통계 저장
			List<ApiStatsHour> hourStatsList = collector.flushAllHours();
			if (!hourStatsList.isEmpty()) {
				for (ApiStatsHour stats : hourStatsList) {
					try {
						hourLogger.log(stats);
					} catch (Exception e) {
						log.error("Failed to save hour stats during shutdown: {}", stats, e);
					}
				}
				log.info("Saved {} remaining hour stats during shutdown", hourStatsList.size());
			}

		} catch (Exception e) {
			log.error("Error during shutdown", e);
		}
	}
}
