package com.eactive.eai.rms.onl.dashboard.modern.service;

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.onl.common.service.EaiServerInfoService;
import com.eactive.eai.rms.onl.dashboard.service.Dashboard2RepositoryService;
import com.eactive.eai.rms.onl.server.repository.TransactionRepositoryService;
import com.eactive.eai.rms.onl.vo.TransactionTrackingVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 거래현황 집계 서비스
 * 각 인터페이스별 10초 단위 처리량을 계산하여 저장합니다.
 * 시계열 데이터를 유지하여 트렌드 분석에 사용합니다.
 */
@Slf4j
@Service
public class TransactionSummaryService {

    @Autowired
    @Qualifier("dashboard2RepositoryService")
    private Dashboard2RepositoryService dashboard2Repository;

    @Autowired
    @Qualifier("transactionRepositoryService")
    private TransactionRepositoryService transactionRepositoryService;

    @Autowired
    private EaiServerInfoService eaiServerInfoService;

    // 인터페이스별 처리량 정보를 저장하는 맵
    // 키: 서비스명, 값: 시계열 데이터
    private final ConcurrentHashMap<String, TimeSeriesData> timeSeriesMap = new ConcurrentHashMap<>();

    // 서비스별 마지막 누적 값을 저장하는 맵
    private final ConcurrentHashMap<String, Map<String, Integer>> lastAccumulatedValuesMap = new ConcurrentHashMap<>();

    // 스케줄러
    private ScheduledExecutorService scheduler;

    // 시계열 데이터 저장 기간 (초)
    private static final int TIME_SERIES_RETENTION_SECONDS = 120;

    // 시계열 데이터 샘플링 간격 (초)
    private static final int TIME_SERIES_SAMPLING_SECONDS = 10;

    // 시계열 데이터를 담는 내부 클래스
    public static class TimeSeriesData {
        private List<Long> timestamps = new ArrayList<>();      // 타임스탬프 목록
        private List<Integer> success = new ArrayList<>();      // 성공 건수
        private List<Integer> error = new ArrayList<>();        // 오류 건수
        private List<Double> avgTime = new ArrayList<>();       // 평균 소요시간

        // 시계열 데이터 추가
        public void addDataPoint(long timestamp, int successCount, int errorCount, double avgProcessTime) {
            timestamps.add(timestamp);
            success.add(successCount);
            error.add(errorCount);
            avgTime.add(avgProcessTime);

            // 오래된 데이터 정리 (최대 저장 기간을 초과하는 데이터 제거)
            long cutoffTime = System.currentTimeMillis() - (TIME_SERIES_RETENTION_SECONDS * 1000);
            while (!timestamps.isEmpty() && timestamps.get(0) < cutoffTime) {
                timestamps.remove(0);
                success.remove(0);
                error.remove(0);
                avgTime.remove(0);
            }
        }

        // Getter 메서드들
        public List<Long> getTimestamps() {
            return timestamps;
        }

        public List<Integer> getSuccess() {
            return success;
        }

        public List<Integer> getError() {
            return error;
        }

        public List<Double> getAvgTime() {
            return avgTime;
        }

        // 특정 기간 동안의 합계 계산
        public Map<String, Integer> calculateTotalCounts() {
            int totalSuccess = 0;
            int totalError = 0;

            for (int i = 0; i < success.size(); i++) {
                totalSuccess += success.get(i);
                totalError += error.get(i);
            }

            Map<String, Integer> totals = new HashMap<>();
            totals.put("success", totalSuccess);
            totals.put("error", totalError);
            return totals;
        }
    }

    /**
     * 초기화 메서드
     * 서비스 로드 시 스케줄러를 시작합니다.
     */
    @PostConstruct
    public void init() {
        log.info("거래현황 집계 서비스 초기화");

        // 스케줄러 생성
        scheduler = Executors.newScheduledThreadPool(1);

        // 시계열 데이터 수집 작업 추가 (10초마다 실행)
        scheduler.scheduleWithFixedDelay(this::collectTimeSeriesData, 0, TIME_SERIES_SAMPLING_SECONDS, TimeUnit.SECONDS);

        log.info("거래현황 집계 스케줄러 시작됨");

        // 전체 데이터용 TOTAL 엔트리 추가
        timeSeriesMap.putIfAbsent("TOTAL", new TimeSeriesData());
    }

    /**
     * 종료 메서드
     * 서비스 종료 시 스케줄러를 정리합니다.
     */
    @PreDestroy
    public void destroy() {
        if (scheduler != null && !scheduler.isShutdown()) {
            scheduler.shutdown();
            log.info("거래현황 집계 스케줄러 종료됨");
        }
    }

    /**
     * 시계열 데이터 수집
     * 10초마다 실행되어 각 서비스별 처리 건수를 수집합니다.
     */
    public void collectTimeSeriesData() {
        try {
            // 수집 시간
            long currentTime = System.currentTimeMillis();

            // 서비스별 현재 누적 데이터
            Map<String, Integer> currentSuccessByService = new HashMap<>();
            Map<String, Integer> currentErrorByService = new HashMap<>();
            Map<String, Double> currentProcessingTimeByService = new HashMap<>();

            // 전체 합계
            int totalSuccess = 0;
            int totalError = 0;
            double totalProcessingTime = 0.0;
            int totalCount = 0;

            // 모든 온라인 데이터소스 타입에 대해 처리
            List<DataSourceType> servers = DataSourceTypeManager.getOnlineDataSourceTypes();
            log.debug("온라인 DataSource 개수: {}", servers.size());
            for (DataSourceType ds : servers) {
                log.debug("  - DataSource: {}", ds.getName());
            }

            for (DataSourceType server : servers) {
                try {
                    // 서버 컨텍스트 설정
                    log.debug("DataSource 전환: {}", server.getName());
                    DataSourceContextHolder.setDataSourceType(server);

                    // 서버에 대한 인스턴스 목록 조회
                    List<Map<String, String>> serverList = eaiServerInfoService.getEaiServerIpListDB();
                    log.debug("  - 서버 목록 개수: {}", serverList != null ? serverList.size() : 0);

                    if (serverList == null || serverList.isEmpty()) {
                        log.debug("  - {} DataSource에 대한 서버 목록이 없습니다. 건너뜁니다.", server.getName());
                        continue;
                    }

                    for (Map<String, String> serverInfo : serverList) {
                        // 트랜잭션 정보 조회를 위한 VO 생성
                        TransactionTrackingVo origin = new TransactionTrackingVo();
                        origin.setHostName(serverInfo.get("HOSTNAME"));
                        origin.setInstanceName(serverInfo.get("EAISVRINSTNM"));

                        // 트랜잭션 정보 조회
                        TransactionTrackingVo vo = transactionRepositoryService.getTrackingVo(origin);

                    if (vo != null) {
                        // 현재 누적 값 계산
                        int currentSuccess = vo.getTotalProc();
                        int currentError = vo.getTotalError();
                        double timeTotal = vo.getTimeTotal();

                        // 서비스별 데이터 저장 (서버명_인스턴스명 형식으로 키 생성)
                        String serviceKey = origin.getHostName() + "_" + origin.getInstanceName();
                        currentSuccessByService.put(serviceKey, currentSuccess);
                        currentErrorByService.put(serviceKey, currentError);
                        currentProcessingTimeByService.put(serviceKey, timeTotal);

                        // 전체 합계에 추가
                        totalSuccess += currentSuccess;
                        totalError += currentError;
                        totalProcessingTime += timeTotal;
                        totalCount++;
                    }
                }
                } catch (Exception e) {
                    log.error("DataSource {} 처리 중 오류 발생: {}", server.getName(), e.getMessage(), e);
                } finally {
                    // DataSource 컨텍스트 클리어
                    DataSourceContextHolder.clearDataSourceType();
                }
            }

            // 각 서비스 타입별 시계열 데이터 업데이트
            for (String serviceType : currentSuccessByService.keySet()) {
                // 현재 누적 값
                int currentSuccess = currentSuccessByService.get(serviceType);
                int currentError = currentErrorByService.get(serviceType);
                double processingTime = currentProcessingTimeByService.get(serviceType);

                // 마지막 누적 값 가져오기
                Map<String, Integer> lastValues = lastAccumulatedValuesMap.getOrDefault(serviceType, new HashMap<>());

                // 차이 계산
                int deltaSuccess = currentSuccess - lastValues.getOrDefault("success", 0);
                int deltaError = currentError - lastValues.getOrDefault("error", 0);

                // 음수 값 방지 (카운터가 리셋된 경우)
                deltaSuccess = Math.max(0, deltaSuccess);
                deltaError = Math.max(0, deltaError);

                // 평균 처리시간 계산
                double avgProcessingTime = (deltaSuccess + deltaError > 0) ? processingTime / (deltaSuccess + deltaError) : 0.0;

                // 시계열 데이터 추가
                TimeSeriesData timeSeriesData = timeSeriesMap.computeIfAbsent(serviceType, k -> new TimeSeriesData());
                timeSeriesData.addDataPoint(currentTime, deltaSuccess, deltaError, avgProcessingTime);

                // 마지막 누적 값 업데이트
                Map<String, Integer> newLastValues = new HashMap<>();
                newLastValues.put("success", currentSuccess);
                newLastValues.put("error", currentError);
                lastAccumulatedValuesMap.put(serviceType, newLastValues);
            }

            // TOTAL 시계열 데이터 업데이트
            TimeSeriesData totalTimeSeriesData = timeSeriesMap.get("TOTAL");
            if (totalTimeSeriesData != null && totalCount > 0) {
                // 마지막 누적 값 가져오기
                Map<String, Integer> lastTotalValues = lastAccumulatedValuesMap.getOrDefault("TOTAL", new HashMap<>());

                // 차이 계산
                int deltaSuccess = totalSuccess - lastTotalValues.getOrDefault("success", 0);
                int deltaError = totalError - lastTotalValues.getOrDefault("error", 0);

                // 음수 값 방지
                deltaSuccess = Math.max(0, deltaSuccess);
                deltaError = Math.max(0, deltaError);

                // 평균 처리시간 계산
                double avgProcessingTime = (deltaSuccess + deltaError > 0) ? totalProcessingTime / (deltaSuccess + deltaError) : 0.0;

                totalTimeSeriesData.addDataPoint(currentTime, deltaSuccess, deltaError, avgProcessingTime);

                // 마지막 누적 값 업데이트
                Map<String, Integer> newLastTotalValues = new HashMap<>();
                newLastTotalValues.put("success", totalSuccess);
                newLastTotalValues.put("error", totalError);
                lastAccumulatedValuesMap.put("TOTAL", newLastTotalValues);
            }

        } catch (Exception e) {
            log.error("시계열 데이터 수집 중 오류 발생: {}", e.getMessage(), e);
        }
    }

    /**
     * 시계열 데이터 조회
     * @param seconds 조회할 초 단위 기간 (최대 120초)
     * @return 시계열 데이터
     */
    public TimeSeriesData getTimeSeriesData(int seconds) {
        // 기간 제한 (1~120초)
        seconds = Math.min(Math.max(seconds, 1), TIME_SERIES_RETENTION_SECONDS);

        // TOTAL 데이터 사용
        TimeSeriesData originalData = timeSeriesMap.get("TOTAL");

        // 데이터가 없으면 빈 데이터 반환
        if (originalData == null) {
            return new TimeSeriesData();
        }

        // 기간에 맞게 데이터 필터링
        TimeSeriesData filteredData = new TimeSeriesData();
        long cutoffTime = System.currentTimeMillis() - (seconds * 1000);

        List<Long> originalTimestamps = originalData.getTimestamps();
        List<Integer> originalSuccess = originalData.getSuccess();
        List<Integer> originalError = originalData.getError();
        List<Double> originalAvgTime = originalData.getAvgTime();

        // 최근 데이터부터 시작하여 지정된 기간 내의 데이터만 필터링
        for (int i = originalTimestamps.size() - 1; i >= 0; i--) {
            if (originalTimestamps.get(i) >= cutoffTime) {
                filteredData.timestamps.add(0, originalTimestamps.get(i));
                filteredData.success.add(0, originalSuccess.get(i));
                filteredData.error.add(0, originalError.get(i));
                filteredData.avgTime.add(0, originalAvgTime.get(i));
            } else {
                // 시간 범위를 벗어나면 종료
                break;
            }
        }

        return filteredData;
    }
}