package gc.mda.signal_batch.batch.job;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.*;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Profile;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Slf4j
@Component
@Profile("!query")  // query 프로파일에서는 배치 스케줄러 비활성화
@RequiredArgsConstructor
@ConditionalOnProperty(name = "vessel.batch.scheduler.enabled", havingValue = "true", matchIfMissing = true)
public class VesselBatchScheduler {
    
    private volatile boolean trackAggregationRunning = false;

    @Autowired
    @Qualifier("asyncJobLauncher")
    private JobLauncher jobLauncher;

    @Autowired
    @Qualifier("vesselTrackAggregationJob")
    private Job vesselTrackAggregationJob;

    @Autowired(required = false)
    @Qualifier("hourlyAggregationJob")
    private Job hourlyAggregationJob;

    @Autowired(required = false)
    @Qualifier("dailyAggregationJob")
    private Job dailyAggregationJob;

    @Autowired(required = false)
    @Qualifier("aisTargetImportJob")
    private Job aisTargetImportJob;

    @Value("${vessel.batch.scheduler.enabled:true}")
    private boolean schedulerEnabled;

    @Value("${vessel.batch.abnormal-detection.enabled:true}")
    private boolean abnormalDetectionEnabled;

    /**
     * S&P AIS API 수집 (매 1분 15초)
     * 캐시에 최신 위치 저장 → 5분 집계 Job에서 활용
     */
    @Scheduled(cron = "15 * * * * *")
    public void runAisTargetImport() {
        if (!schedulerEnabled || aisTargetImportJob == null) {
            return;
        }

        try {
            LocalDateTime now = LocalDateTime.now();
            JobParameters params = new JobParametersBuilder()
                    .addString("executionTime", now.toString())
                    .toJobParameters();

            JobExecution execution = jobLauncher.run(aisTargetImportJob, params);
            log.debug("[AIS Import] 실행 ID: {}", execution.getId());
        } catch (JobExecutionAlreadyRunningException e) {
            log.warn("[AIS Import] 이전 Job 실행 중, 스킵");
        } catch (Exception e) {
            log.error("[AIS Import] Job 실행 실패", e);
        }
    }

//
    /**
     * 5분 단위 궤적 집계 처리 (4분 지연으로 위치 집계 이후 실행)
     * 매 5분마다 실행 (4, 9, 14, 19, 24, 29, 34, 39, 44, 49, 54, 59분)
     */
    @Scheduled(cron = "0 4,9,14,19,24,29,34,39,44,49,54,59 * * * *")
    public void runTrackAggregation() {
        log.info("=== runTrackAggregation() called by thread: {} ===", Thread.currentThread().getName());
        
        if (!schedulerEnabled) {
            log.debug("Scheduler is disabled");
            return;
        }
        
        // 실행 가드: 이미 실행 중이면 스킵
        if (trackAggregationRunning) {
            log.warn("Track aggregation is already running, skipping this execution - thread: {}", Thread.currentThread().getName());
            return;
        }
        
        trackAggregationRunning = true;
        try {
            // 4분 전 데이터를 처리 (위치 집계 완료 후)
            LocalDateTime now = LocalDateTime.now();
            LocalDateTime endTime = now.minusMinutes(4); // 4분 지연 (캐시 기반이므로 고정)
            LocalDateTime startTime = endTime.minusMinutes(5);

            // 5분 버킷 계산
            LocalDateTime timeBucket = startTime
                    .withSecond(0)
                    .withNano(0)
                    .minusMinutes(startTime.getMinute() % 5);

            log.info("Starting track aggregation for period: {} to {} (bucket: {})",
                    startTime, endTime, timeBucket);

            // Timestamp 형식에 맞게 포매팅
            DateTimeFormatter timestampFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

            JobParameters params = new JobParametersBuilder()
                    .addString("startTime", startTime.withNano(0).toString())
                    .addString("endTime", endTime.withNano(0).toString())
                    .addString("timeBucket", timeBucket.format(timestampFormatter))
                    .addString("jobType", "TRACK_INCREMENTAL")
                    // executionTime, nanoTime 제거 - timeBucket만으로 고유성 보장
                    .toJobParameters();

            log.info("=== ABOUT TO CALL jobLauncher.run() - Thread: {} ===", Thread.currentThread().getName());
            JobExecution execution = jobLauncher.run(vesselTrackAggregationJob, params);
            log.info("=== COMPLETED jobLauncher.run() - Thread: {} - Execution ID: {} ===", 
                    Thread.currentThread().getName(), execution.getId());

            log.info("Track aggregation started with execution ID: {}", execution.getId());

        } catch (JobExecutionAlreadyRunningException e) {
            log.warn("Previous track job is still running, skipping this execution");
        } catch (Exception e) {
            log.error("Failed to start track aggregation", e);
            // 중복 키 오류인 경우 경고로만 처리
            if (e.getMessage().contains("중복된 키") || e.getMessage().contains("duplicate key")) {
                log.warn("Duplicate key detected, job may have already processed this time bucket");
            }
        } finally {
            trackAggregationRunning = false;
        }
    }

    /**
     * 향상된 1시간 집계 스케줄 - 매시 10분
     * 비정상 궤적 검출 기능 포함
     */
    @Scheduled(cron = "0 10 * * * *")
    public void runHourlyAggregation() {
        if (!schedulerEnabled || hourlyAggregationJob == null) {
            log.debug("Hourly aggregation job is not available");
            return;
        }

        try {
            LocalDateTime now = LocalDateTime.now();
            LocalDateTime startTime = now.minusHours(1).withMinute(0).withSecond(0).withNano(0);
            LocalDateTime endTime = startTime.plusHours(1);

            JobParameters params = new JobParametersBuilder()
                    .addString("startTime", startTime.toString())
                    .addString("endTime", endTime.toString())
                    .addString("timeBucket", "hourly")
                    .addString("executionTime", now.toString())
                    .addString("enableAbnormalDetection", String.valueOf(abnormalDetectionEnabled))
                    .toJobParameters();

            JobExecution execution = jobLauncher.run(hourlyAggregationJob, params);
            log.info("Started enhanced hourly aggregation job: {} for period {} to {} (abnormal detection: {})",
                    execution.getId(), startTime, endTime, abnormalDetectionEnabled);
        } catch (Exception e) {
            log.error("Failed to start enhanced hourly aggregation job", e);
        }
    }

    /**
     * 향상된 1일 집계 스케줄 - 매일 01:00
     * 비정상 궤적 검출 기능 포함
     */
    @Scheduled(cron = "0 0 1 * * *")
    public void runDailyAggregation() {
        if (!schedulerEnabled || dailyAggregationJob == null) {
            log.debug("Enhanced daily aggregation job is not available");
            return;
        }

        try {
            LocalDateTime now = LocalDateTime.now();
            LocalDateTime startTime = now.minusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
            LocalDateTime endTime = startTime.plusDays(1);

            JobParameters params = new JobParametersBuilder()
                    .addString("startTime", startTime.toString())
                    .addString("endTime", endTime.toString())
                    .addString("timeBucket", "daily")
                    .addString("executionTime", now.toString())
                    .addString("enableAbnormalDetection", String.valueOf(abnormalDetectionEnabled))
                    .toJobParameters();

            JobExecution execution = jobLauncher.run(dailyAggregationJob, params);
            log.info("Started enhanced daily aggregation job: {} for date {} (abnormal detection: {})",
                    execution.getId(), startTime.toLocalDate(), abnormalDetectionEnabled);
        } catch (Exception e) {
            log.error("Failed to start enhanced daily aggregation job", e);
        }
    }

    /**
     * 비정상 궤적 통계 집계 (매일 02:00)
     */
    @Scheduled(cron = "0 0 2 * * *")
    public void updateAbnormalTrackStatistics() {
        if (!schedulerEnabled || !abnormalDetectionEnabled) {
            return;
        }

        try {
            log.info("Updating abnormal track statistics...");
            // TODO: 통계 업데이트 로직 구현
        } catch (Exception e) {
            log.error("Failed to update abnormal track statistics", e);
        }
    }
    /**
     * 데이터 지연 모니터링 및 캐치업 작업 (10분마다)
     */
    @Scheduled(fixedDelay = 600000, initialDelay = 60000)
    public void monitorAndCatchUp() {
        if (!schedulerEnabled) {
            return;
        }

        try {
            // TODO: 데이터 지연 확인 로직 구현
            // 지연이 10분 이상이면 캐치업 작업 실행

        } catch (Exception e) {
            log.error("Failed to monitor data delay", e);
        }
    }
}
