package gc.mda.kcg.domain.analysis;

import gc.mda.kcg.domain.fleet.GroupPolygonService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Service
@RequiredArgsConstructor
public class VesselAnalysisService {

    private final VesselAnalysisResultRepository repository;
    private final GroupPolygonService groupPolygonService;

    private static final long CACHE_TTL_MS = 2 * 60 * 60_000L; // 2시간

    /** mmsi → 최신 분석 결과 (인메모리 캐시) */
    private final Map<String, VesselAnalysisResult> cache = new ConcurrentHashMap<>();
    private volatile Instant lastFetchTime = null;
    private volatile long lastUpdatedAt = 0;

    /**
     * 최근 2시간 분석 결과 + 집계 통계.
     * - 첫 호출(warmup): 2시간 전체 조회 → 캐시 구축
     * - 이후: lastFetchTime 이후 증분만 조회 → 캐시 병합
     * - 2시간 초과 항목은 evict
     * - 값 갱신 시 TTL 타이머 초기화
     */
    public Map<String, Object> getLatestResultsWithStats() {
        Instant now = Instant.now();

        if (lastFetchTime == null || (System.currentTimeMillis() - lastUpdatedAt) > CACHE_TTL_MS) {
            // warmup: 2시간 전체 조회
            Instant since = now.minus(2, ChronoUnit.HOURS);
            List<VesselAnalysisResult> rows = repository.findByAnalyzedAtAfter(since);
            cache.clear();
            for (VesselAnalysisResult r : rows) {
                cache.merge(r.getMmsi(), r, (old, cur) ->
                        cur.getAnalyzedAt().isAfter(old.getAnalyzedAt()) ? cur : old);
            }
            lastFetchTime = now;
            lastUpdatedAt = System.currentTimeMillis();
            log.info("vessel analysis cache warmup: {} vessels from DB", cache.size());
        } else {
            // 증분: lastFetchTime 이후만 조회
            List<VesselAnalysisResult> rows = repository.findByAnalyzedAtAfter(lastFetchTime);
            if (!rows.isEmpty()) {
                for (VesselAnalysisResult r : rows) {
                    cache.merge(r.getMmsi(), r, (old, cur) ->
                            cur.getAnalyzedAt().isAfter(old.getAnalyzedAt()) ? cur : old);
                }
                lastUpdatedAt = System.currentTimeMillis();
                log.debug("vessel analysis incremental merge: {} new rows", rows.size());
            }
            lastFetchTime = now;
        }

        // 2시간 초과 항목 evict
        Instant cutoff = now.minus(2, ChronoUnit.HOURS);
        cache.entrySet().removeIf(e -> e.getValue().getAnalyzedAt().isBefore(cutoff));

        // 집계 통계
        int dark = 0, spoofing = 0, critical = 0, high = 0, medium = 0, low = 0;
        Set<Integer> clusterIds = new HashSet<>();
        for (VesselAnalysisResult r : cache.values()) {
            if (Boolean.TRUE.equals(r.getIsDark())) dark++;
            if (r.getSpoofingScore() != null && r.getSpoofingScore() > 0.5) spoofing++;
            String level = r.getRiskLevel();
            if (level != null) {
                switch (level) {
                    case "CRITICAL" -> critical++;
                    case "HIGH" -> high++;
                    case "MEDIUM" -> medium++;
                    default -> low++;
                }
            } else {
                low++;
            }
            if (r.getClusterId() != null && r.getClusterId() >= 0) {
                clusterIds.add(r.getClusterId());
            }
        }

        Map<String, Integer> gearStats = groupPolygonService.getGearStats();

        Map<String, Object> stats = new LinkedHashMap<>();
        stats.put("total", cache.size());
        stats.put("dark", dark);
        stats.put("spoofing", spoofing);
        stats.put("critical", critical);
        stats.put("high", high);
        stats.put("medium", medium);
        stats.put("low", low);
        stats.put("clusterCount", clusterIds.size());
        stats.put("gearGroups", gearStats.get("gearGroups"));
        stats.put("gearCount", gearStats.get("gearCount"));

        List<VesselAnalysisDto> results = cache.values().stream()
                .sorted(Comparator.comparingInt(VesselAnalysisResult::getRiskScore).reversed())
                .map(VesselAnalysisDto::from)
                .toList();

        return Map.of(
                "count", results.size(),
                "items", results,
                "stats", stats
        );
    }
}
