import { useEffect, useMemo, useRef, useState } from 'react';
import { fetchChnPrmShipPositions, fetchRecentPositions } from '../../entities/aisTarget/api/fetchPositions';
import type { AisTarget } from '../../entities/aisTarget/model/types';

export type AisPollingStatus = 'idle' | 'loading' | 'ready' | 'error';

export type AisPollingSnapshot = {
  status: AisPollingStatus;
  error: string | null;
  lastFetchAt: string | null;
  lastFetchMinutes: number | null;
  lastMessage: string | null;
  total: number;
  lastUpserted: number;
  lastInserted: number;
  lastDeleted: number;
};

export interface AisPollingOptions {
  chnprmship?: {
    initialMinutes?: number;
    pollMinutes?: number;
    intervalMs?: number;
    retentionMinutes?: number;
  };
  recent?: {
    initialMinutes?: number;
    pollMinutes?: number;
    intervalMs?: number;
    retentionMinutes?: number;
  };
  bbox?: string;
  enabled?: boolean;
}

/* ── Store helpers ── */

function upsertByMmsi(store: Map<number, AisTarget>, rows: AisTarget[]) {
  let inserted = 0;
  let upserted = 0;

  for (const r of rows) {
    if (!r || typeof r.mmsi !== 'number') continue;

    const prev = store.get(r.mmsi);
    if (!prev) {
      store.set(r.mmsi, r);
      inserted += 1;
      upserted += 1;
      continue;
    }

    // 소스 우선순위: chnprmship > recent
    // recent 데이터는 기존 chnprmship 데이터를 절대 덮어쓰지 않음
    if (prev.source === 'chnprmship' && r.source === 'recent') continue;

    // 동일 소스: 더 최신 timestamp만 업데이트
    const prevTs = Date.parse(prev.messageTimestamp || '');
    const nextTs = Date.parse(r.messageTimestamp || '');
    if (prev.source === r.source) {
      if (Number.isFinite(prevTs) && Number.isFinite(nextTs) && nextTs <= prevTs) continue;
    }
    // 다른 소스 (recent→chnprmship): chnprmship이 항상 승리

    store.set(r.mmsi, r);
    upserted += 1;
  }

  return { inserted, upserted };
}

function parseBbox(raw: string | undefined) {
  if (!raw) return null;
  const parts = raw
    .split(',')
    .map((s) => s.trim())
    .filter(Boolean);
  if (parts.length !== 4) return null;
  const [lonMin, latMin, lonMax, latMax] = parts.map((p) => Number(p));
  const ok =
    Number.isFinite(lonMin) &&
    Number.isFinite(latMin) &&
    Number.isFinite(lonMax) &&
    Number.isFinite(latMax) &&
    lonMin >= -180 &&
    lonMax <= 180 &&
    latMin >= -90 &&
    latMax <= 90 &&
    lonMin < lonMax &&
    latMin < latMax;
  if (!ok) return null;
  return { lonMin, latMin, lonMax, latMax };
}

function pruneStore(
  store: Map<number, AisTarget>,
  chnprmRetention: number,
  recentRetention: number,
  bboxRaw: string | undefined,
) {
  const now = Date.now();
  const bbox = parseBbox(bboxRaw);
  let deleted = 0;

  for (const [mmsi, t] of store.entries()) {
    const ts = Date.parse(t.messageTimestamp || '');
    const retention = t.source === 'chnprmship' ? chnprmRetention : recentRetention;
    const cutoffMs = now - retention * 60_000;

    if (Number.isFinite(ts) && ts < cutoffMs) {
      store.delete(mmsi);
      deleted += 1;
      continue;
    }

    if (bbox) {
      const lat = t.lat;
      const lon = t.lon;
      if (typeof lat !== 'number' || typeof lon !== 'number') {
        store.delete(mmsi);
        deleted += 1;
        continue;
      }
      if (lon < bbox.lonMin || lon > bbox.lonMax || lat < bbox.latMin || lat > bbox.latMax) {
        store.delete(mmsi);
        deleted += 1;
        continue;
      }
    }
  }

  return deleted;
}

/* ── Hook ── */

export function useAisTargetPolling(opts: AisPollingOptions = {}) {
  const cp = opts.chnprmship ?? {};
  const rc = opts.recent ?? {};
  const cpInit = cp.initialMinutes ?? 120;
  const cpPoll = cp.pollMinutes ?? 2;
  const cpInterval = cp.intervalMs ?? 60_000;
  const cpRetention = cp.retentionMinutes ?? 120;
  const rcInit = rc.initialMinutes ?? 15;
  const rcPoll = rc.pollMinutes ?? 12;
  const rcInterval = rc.intervalMs ?? 600_000;
  const rcRetention = rc.retentionMinutes ?? 72;
  const bbox = opts.bbox;
  const enabled = opts.enabled ?? true;

  const storeRef = useRef<Map<number, AisTarget>>(new Map());
  const generationRef = useRef(0);

  const [rev, setRev] = useState(0);
  const [snapshot, setSnapshot] = useState<AisPollingSnapshot>({
    status: 'idle',
    error: null,
    lastFetchAt: null,
    lastFetchMinutes: null,
    lastMessage: null,
    total: 0,
    lastUpserted: 0,
    lastInserted: 0,
    lastDeleted: 0,
  });

  useEffect(() => {
    if (!enabled) return;

    let cancelled = false;
    const controller = new AbortController();
    const generation = ++generationRef.current;

    function applyData(data: AisTarget[], minutes: number) {
      if (cancelled || generation !== generationRef.current) return;

      const { inserted, upserted } = upsertByMmsi(storeRef.current, data);
      const deleted = pruneStore(storeRef.current, cpRetention, rcRetention, bbox);
      const total = storeRef.current.size;

      setSnapshot({
        status: 'ready',
        error: null,
        lastFetchAt: new Date().toISOString(),
        lastFetchMinutes: minutes,
        lastMessage: null,
        total,
        lastUpserted: upserted,
        lastInserted: inserted,
        lastDeleted: deleted,
      });
      setRev((r) => r + 1);
    }

    async function pollChnprm(minutes: number) {
      try {
        const data = await fetchChnPrmShipPositions({ minutes }, controller.signal);
        applyData(data, minutes);
      } catch (e) {
        if (cancelled || generation !== generationRef.current) return;
        setSnapshot((s) => ({
          ...s,
          error: e instanceof Error ? e.message : String(e),
        }));
      }
    }

    async function pollRecent(minutes: number) {
      try {
        const data = await fetchRecentPositions({ minutes }, controller.signal);
        applyData(data, minutes);
      } catch (e) {
        if (cancelled || generation !== generationRef.current) return;
        setSnapshot((s) => ({
          ...s,
          error: e instanceof Error ? e.message : String(e),
        }));
      }
    }

    // 초기화: 스토어 리셋 + 두 API 병렬 호출
    storeRef.current = new Map();
    setSnapshot({
      status: 'loading',
      error: null,
      lastFetchAt: null,
      lastFetchMinutes: null,
      lastMessage: null,
      total: 0,
      lastUpserted: 0,
      lastInserted: 0,
      lastDeleted: 0,
    });
    setRev((r) => r + 1);

    void Promise.all([pollChnprm(cpInit), pollRecent(rcInit)]);

    // 이중 타이머: ChnPrmShip 1분, 전체 선박 10분
    const cpTimer = window.setInterval(() => void pollChnprm(cpPoll), cpInterval);
    const rcTimer = window.setInterval(() => void pollRecent(rcPoll), rcInterval);

    return () => {
      cancelled = true;
      controller.abort();
      window.clearInterval(cpTimer);
      window.clearInterval(rcTimer);
    };
  }, [cpInit, cpPoll, cpInterval, cpRetention, rcInit, rcPoll, rcInterval, rcRetention, bbox, enabled]);

  const targets = useMemo(() => {
    void rev;
    return Array.from(storeRef.current.values());
  }, [rev]);

  return { targets, snapshot };
}
