package com.caliverse.admin.scheduler;

import com.caliverse.admin.dynamodb.service.DynamodbService;
import com.caliverse.admin.global.common.constants.AdminConstants;
import com.caliverse.admin.logs.Indicatordomain.StartEndTime;
import com.caliverse.admin.logs.logservice.LogServiceHelper;
import com.caliverse.admin.logs.logservice.indicators.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.time.LocalDate;

@Component
@Order(1)
@RequiredArgsConstructor
@Slf4j
public class OneTimeSchedule implements CommandLineRunner {
    @Autowired private IndicatorsDauService dauService;
    @Autowired private IndicatorsWauService wauService;
    @Autowired private IndicatorsMauService mauService;
    @Autowired private IndicatorsMcuService mcuService;
    @Autowired private IndicatorsNruService nruService;
    @Autowired private IndicatorsPlayTimeService playTimeService;
    @Autowired private IndicatorsDglcService dglcService;
    @Autowired private IndicatorsDBCapacityService capacityService;
    @Autowired private IndicatorsUgqCreateService ugqCreateService;
    @Autowired private IndicatorsMetaverseServerService metaverseServerService;
    @Autowired private IndicatorsUserCreateService userCreateService;
    @Autowired private IndicatorsUserLoginService userLoginService;
    @Autowired private IndicatorsCurrencyService currencyService;
    @Autowired private IndicatorsItemService itemService;
    @Autowired private IndicatorsSnapshotService snapshotService;

    @Autowired private DynamodbService dynamodbService;
    @Autowired private MigrationService migrationService;

    @Value("${batch.delay.between.dates}") // 날짜 간 지연시간
    private long delayBetweenDates;

    @Value("${batch.delay.between.services}") // 서비스 간 지연시간
    private long delayBetweenServices;

    @Value("${batch.chunk.size}") // 한 번에 처리할 일수
    private int chunkSize;

    @Value("${batch.delay.between.chunks}") // 청크 간 지연시간
    private long delayBetweenChunks;

    @Override
    public void run(String... args) throws Exception {
        log.info("=== OneTimeSchedule starting ===");
        try{

            LocalDate startDate = LocalDate.of(2024, 8, 28);
//            LocalDate startDate = LocalDate.of(2025, 7, 1);
//            LocalDate currentDate = LocalDate.of(2025, 5, 1);
            LocalDate currentDate = LocalDate.now();
//
            log.info("Processing dates from {} to {}", startDate, currentDate);

            LocalDate processDate = startDate;
            int processedDates = 0;

            migrationService.migrationHistory();

//            while (!processDate.isAfter(currentDate)) {
//                LocalDate chunkEndDate = processDate.plusDays(chunkSize - 1);
//                if (chunkEndDate.isAfter(currentDate)) {
//                    chunkEndDate = currentDate;
//                }
//
//                log.info("Processing chunk: {} to {}", processDate, chunkEndDate);
//
//                // 청크 단위로 처리
//                for (LocalDate date = processDate; !date.isAfter(chunkEndDate); date = date.plusDays(1)) {
//                    processDateWithLoadBalancing(date);
//                    processedDates++;
//
//                    // 날짜 간 지연
//                    if (!date.equals(chunkEndDate)) {
//                        Thread.sleep(delayBetweenDates);
//                    }
//                }
//
//                processDate = chunkEndDate.plusDays(1);
//
//                // 청크 간 큰 지연 (시스템 리소스 회복 시간)
//                if (!processDate.isAfter(currentDate)) {
//                    log.info("Chunk completed. Waiting {} ms before next chunk...", delayBetweenChunks);
//                    Thread.sleep(delayBetweenChunks);
//                }
//            }

            // local, dev 용
//            for (LocalDate date = startDate; !date.isAfter(currentDate); date = date.plusDays(1)) {
//                log.info("Processing date: {}", date);
//
//                StartEndTime dayStartEndTime = LogServiceHelper.getCurrentLogSearchEndTime(date, AdminConstants.STAT_DAY_NUM);
//                StartEndTime weekStartEndTime = LogServiceHelper.getCurrentLogSearchEndTime(date, AdminConstants.STAT_WEEK_NUM);
//                StartEndTime monthStartEndTime = LogServiceHelper.getCurrentLogSearchEndTime(date, AdminConstants.STAT_MONTH_NUM);
//
//                log.info("Date: {}, StartTime: {}, EndTime: {}", date, dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime());
//
//                snapshotService.collectSnapshot(dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime());
////                itemService.collectItem(dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime());
////                userCreateService.collectUserCreate(dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime());
////                userLoginService.collectUserLogin(dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime());
////                currencyService.collectCurrency(dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime());
//
////                metaverseServerService.collectMetaverseServerCount(dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime(), 13);
////                capacityService.collectDBCapacity(dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime());  //체크
////                dauService.collectDailyActiveUser(dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime());  //체크
////                wauService.collectWeeklyActiveUser(weekStartEndTime.getStartTime(), weekStartEndTime.getEndTime());   //체크
////                mauService.collectMonthlyActiveUser(monthStartEndTime.getStartTime(), monthStartEndTime.getEndTime());    //체크
////                mcuService.collectMaxCountUser(dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime());     //체크
////                nruService.collectCharacterCreateCount(dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime());     //체크
////                playTimeService.collectUserPlayTime(dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime());    //체크
////                dglcService.collectDailyGameLoginCount(dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime());     //체크
////                ugqCreateService.collectUGQCreateCount(dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime());     //체크
//            }

            log.info("=== OneTimeSchedule completed successfully ===");
        }catch (Exception e){
            log.error("OneTimeSchedule execution failed", e);
        }
    }

    private void processDateWithLoadBalancing(LocalDate date) throws InterruptedException {
        log.info("Processing date: {}", date);

        StartEndTime dayStartEndTime = LogServiceHelper.getCurrentLogSearchEndTime(date, AdminConstants.STAT_DAY_NUM);
        StartEndTime weekStartEndTime = LogServiceHelper.getCurrentLogSearchEndTime(date, AdminConstants.STAT_WEEK_NUM);
        StartEndTime monthStartEndTime = LogServiceHelper.getCurrentLogSearchEndTime(date, AdminConstants.STAT_MONTH_NUM);

        log.info("Date: {}, StartTime: {}, EndTime: {}", date, dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime());

        // 1. 가장 부하가 적은 서비스부터 실행
        // 아이템 DW
        executeWithDelay(() -> {
            try {
                itemService.collectItem(dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime());
            } catch (Exception e) {
                log.error("Error in itemService for date: {}", date, e);
            }
        }, "itemService");

        // 자산 DW
        executeWithDelay(() -> {
            try {
                snapshotService.collectSnapshot(dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime());
            } catch (Exception e) {
                log.error("Error in snapshotService for date: {}", date, e);
            }
        }, "snapshotService");

        // 유저 생성 DW
        executeWithDelay(() -> {
            try {
                userCreateService.collectUserCreate(dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime());
            } catch (Exception e) {
                log.error("Error in userCreateService for date: {}", date, e);
            }
        }, "userCreateService");

        // 유저 로그인 DW
        executeWithDelay(() -> {
            try {
                userLoginService.collectUserLogin(dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime());
            } catch (Exception e) {
                log.error("Error in userLoginService for date: {}", date, e);
            }
        }, "userLoginService");

        // 재화 DW
//        executeWithDelay(() -> {
//            try {
//                currencyService.collectCurrency(dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime());
//            } catch (Exception e) {
//                log.error("Error in currencyService for date: {}", date, e);
//            }
//        }, "currencyService");

        // 2. 중간 부하 서비스들
//        executeWithDelay(() -> {
//            try {
//                dauService.collectDailyActiveUser(dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime());
//            } catch (Exception e) {
//                log.error("Error in dauService for date: {}", date, e);
//            }
//        }, "dauService");

//        executeWithDelay(() -> {
//            try {
//                mcuService.collectMaxCountUser(dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime());
//            } catch (Exception e) {
//                log.error("Error in mcuService for date: {}", date, e);
//            }
//        }, "mcuService");

//        executeWithDelay(() -> {
//            try {
//                nruService.collectCharacterCreateCount(dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime());
//            } catch (Exception e) {
//                log.error("Error in nruService for date: {}", date, e);
//            }
//        }, "nruService");

        // 3. 높은 부하 서비스들 (더 긴 지연시간)
//        executeWithDelay(() -> {
//            try {
//                playTimeService.collectUserPlayTime(dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime());
//            } catch (Exception e) {
//                log.error("Error in playTimeService for date: {}", date, e);
//            }
//        }, "playTimeService");

//        executeWithDelay(() -> {
//            try {
//                currencyService.collectCurrency(dayStartEndTime.getStartTime(), dayStartEndTime.getEndTime());
//            } catch (Exception e) {
//                log.error("Error in currencyService for date: {}", date, e);
//            }
//        }, "currencyService");

        // 4. 주간/월간 데이터는 특정 조건에서만 실행 (부하 최소화)
//        if (shouldProcessWeeklyData(date)) {
//            executeWithDelay(() -> {
//                try {
//                    wauService.collectWeeklyActiveUser(weekStartEndTime.getStartTime(), weekStartEndTime.getEndTime());
//                } catch (Exception e) {
//                    log.error("Error in wauService for date: {}", date, e);
//                }
//            }, "wauService");
//        }
//
//        if (shouldProcessMonthlyData(date)) {
//            executeWithDelay(() -> {
//                try {
//                    mauService.collectMonthlyActiveUser(monthStartEndTime.getStartTime(), monthStartEndTime.getEndTime());
//                } catch (Exception e) {
//                    log.error("Error in mauService for date: {}", date, e);
//                }
//            }, "mauService");
//        }
    }

    private void executeWithDelay(Runnable task, String serviceName) throws InterruptedException {
        executeWithDelay(task, serviceName, delayBetweenServices);
    }

    private void executeWithDelay(Runnable task, String serviceName, long customDelay) throws InterruptedException {
        long startTime = System.currentTimeMillis();

        try {
            task.run();
            long endTime = System.currentTimeMillis();
            log.debug("{} completed in {} ms", serviceName, (endTime - startTime));
        } catch (Exception e) {
            log.error("Error executing {}", serviceName, e);
        }

        // 서비스별 커스텀 지연
        log.debug("Waiting {} ms after {}", customDelay, serviceName);
        Thread.sleep(customDelay);
    }

    private boolean shouldProcessWeeklyData(LocalDate date) {
        // 주의 마지막 날에만 주간 데이터 처리
        return date.getDayOfWeek().getValue() == 7; // 일요일
    }

    private boolean shouldProcessMonthlyData(LocalDate date) {
        // 월의 마지막 날에만 월간 데이터 처리
        return date.equals(date.withDayOfMonth(date.lengthOfMonth()));
    }
}
