package gc.mda.signal_batch.batch.reader;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import gc.mda.signal_batch.domain.vessel.model.AisTargetEntity;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 중국 허가선박 전용 캐시
 *
 * - 대상 MMSI(~1,400척)만 별도 관리
 * - TTL: expireAfterWrite (마지막 put 이후 N일 경과 시 만료)
 * - key: MMSI (String)
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class ChnPrmShipCacheManager {

    private final ChnPrmShipProperties properties;
    private Cache<String, AisTargetEntity> cache;

    @PostConstruct
    public void init() {
        this.cache = Caffeine.newBuilder()
                .maximumSize(properties.getMaxSize())
                .expireAfterWrite(properties.getTtlDays(), TimeUnit.DAYS)
                .recordStats()
                .build();

        log.info("ChnPrmShip 캐시 초기화 - TTL: {}일, 최대 크기: {}, 대상 MMSI: {}건",
                properties.getTtlDays(), properties.getMaxSize(), properties.getMmsiSet().size());
    }

    /**
     * 대상 MMSI에 해당하는 항목만 필터링하여 캐시에 저장
     */
    public int putIfTarget(List<AisTargetEntity> items) {
        if (items == null || items.isEmpty()) {
            return 0;
        }

        int updated = 0;
        for (AisTargetEntity item : items) {
            if (!properties.isTarget(item.getMmsi())) {
                continue;
            }

            AisTargetEntity existing = cache.getIfPresent(item.getMmsi());
            if (existing == null || isNewerOrEqual(item, existing)) {
                cache.put(item.getMmsi(), item);
                updated++;
            }
        }

        if (updated > 0) {
            log.debug("ChnPrmShip 캐시 업데이트 - 입력: {}, 대상 저장: {}, 현재 크기: {}",
                    items.size(), updated, cache.estimatedSize());
        }
        return updated;
    }

    /**
     * 시간 범위 내 캐시 데이터 조회
     */
    public List<AisTargetEntity> getByTimeRange(int minutes) {
        OffsetDateTime threshold = OffsetDateTime.now(ZoneOffset.UTC).minusMinutes(minutes);

        return cache.asMap().values().stream()
                .filter(entity -> entity.getMessageTimestamp() != null)
                .filter(entity -> entity.getMessageTimestamp().isAfter(threshold))
                .collect(Collectors.toList());
    }

    /**
     * 워밍업용 직접 저장 (시간 비교 없이)
     */
    public void putAll(List<AisTargetEntity> entities) {
        if (entities == null || entities.isEmpty()) {
            return;
        }
        for (AisTargetEntity entity : entities) {
            if (entity != null && entity.getMmsi() != null) {
                cache.put(entity.getMmsi(), entity);
            }
        }
    }

    public long size() {
        return cache.estimatedSize();
    }

    public Map<String, Object> getStats() {
        var stats = cache.stats();
        return Map.of(
                "estimatedSize", cache.estimatedSize(),
                "maxSize", properties.getMaxSize(),
                "ttlDays", properties.getTtlDays(),
                "targetMmsiCount", properties.getMmsiSet().size(),
                "hitCount", stats.hitCount(),
                "missCount", stats.missCount(),
                "hitRate", String.format("%.2f%%", stats.hitRate() * 100)
        );
    }

    private boolean isNewerOrEqual(AisTargetEntity candidate, AisTargetEntity existing) {
        if (candidate.getMessageTimestamp() == null) return false;
        if (existing.getMessageTimestamp() == null) return true;
        return !candidate.getMessageTimestamp().isBefore(existing.getMessageTimestamp());
    }
}
