import { useEffect, useMemo, useRef, useState } from "react";
import { useAisTargetPolling } from "../../features/aisPolling/useAisTargetPolling";
import { Map3DSettingsToggles } from "../../features/map3dSettings/Map3DSettingsToggles";
import type { MapToggleState } from "../../features/mapToggles/MapToggles";
import { MapToggles } from "../../features/mapToggles/MapToggles";
import { TypeFilterGrid } from "../../features/typeFilter/TypeFilterGrid";
import { useLegacyVessels } from "../../entities/legacyVessel/api/useLegacyVessels";
import { buildLegacyVesselIndex } from "../../entities/legacyVessel/lib";
import type { LegacyVesselIndex } from "../../entities/legacyVessel/lib";
import type { LegacyVesselDataset } from "../../entities/legacyVessel/model/types";
import { useZones } from "../../entities/zone/api/useZones";
import type { VesselTypeCode } from "../../entities/vessel/model/types";
import { AisInfoPanel } from "../../widgets/aisInfo/AisInfoPanel";
import { AisTargetList } from "../../widgets/aisTargetList/AisTargetList";
import { AlarmsPanel } from "../../widgets/alarms/AlarmsPanel";
import { MapLegend } from "../../widgets/legend/MapLegend";
import { Map3D, type BaseMapId, type Map3DSettings, type MapProjectionId } from "../../widgets/map3d/Map3D";
import { RelationsPanel } from "../../widgets/relations/RelationsPanel";
import { SpeedProfilePanel } from "../../widgets/speed/SpeedProfilePanel";
import { Topbar } from "../../widgets/topbar/Topbar";
import { VesselInfoPanel } from "../../widgets/info/VesselInfoPanel";
import { VesselList } from "../../widgets/vesselList/VesselList";
import {
  buildLegacyHitMap,
  computeCountsByType,
  computeFcLinks,
  computeFleetCircles,
  computeLegacyAlarms,
  computePairLinks,
  deriveLegacyVessels,
  filterByShipCodes,
} from "../../features/legacyDashboard/model/derive";
import { VESSEL_TYPE_ORDER } from "../../entities/vessel/model/meta";

const AIS_API_BASE = (import.meta.env.VITE_API_URL || "/snp-api").replace(/\/$/, "");
const AIS_CENTER = {
  lon: 126.95,
  lat: 35.95,
  radiusMeters: 2_000_000,
};

function fmtLocal(iso: string | null) {
  if (!iso) return "-";
  const d = new Date(iso);
  if (Number.isNaN(d.getTime())) return iso;
  return d.toLocaleString("ko-KR", { hour12: false });
}

type Bbox = [number, number, number, number]; // [lonMin, latMin, lonMax, latMax]

function inBbox(lon: number, lat: number, bbox: Bbox) {
  const [lonMin, latMin, lonMax, latMax] = bbox;
  if (lat < latMin || lat > latMax) return false;
  if (lonMin <= lonMax) return lon >= lonMin && lon <= lonMax;
  return lon >= lonMin || lon <= lonMax;
}

function fmtBbox(b: Bbox | null) {
  if (!b) return "-";
  return `${b[0].toFixed(4)},${b[1].toFixed(4)},${b[2].toFixed(4)},${b[3].toFixed(4)}`;
}

function useLegacyIndex(data: LegacyVesselDataset | null): LegacyVesselIndex | null {
  return useMemo(() => (data ? buildLegacyVesselIndex(data.vessels) : null), [data]);
}

export function DashboardPage() {
  const { data: zones, error: zonesError } = useZones();
  const { data: legacyData, error: legacyError } = useLegacyVessels();
  const legacyIndex = useLegacyIndex(legacyData);

  const [viewBbox, setViewBbox] = useState<Bbox | null>(null);
  const [useViewportFilter, setUseViewportFilter] = useState(false);
  const [useApiBbox, setUseApiBbox] = useState(false);
  const [apiBbox, setApiBbox] = useState<string | undefined>(undefined);

  const { targets, snapshot } = useAisTargetPolling({
    initialMinutes: 60,
    bootstrapMinutes: 10,
    incrementalMinutes: 2,
    intervalMs: 60_000,
    retentionMinutes: 90,
    bbox: useApiBbox ? apiBbox : undefined,
    centerLon: useApiBbox ? undefined : AIS_CENTER.lon,
    centerLat: useApiBbox ? undefined : AIS_CENTER.lat,
    radiusMeters: useApiBbox ? undefined : AIS_CENTER.radiusMeters,
  });

  const [selectedMmsi, setSelectedMmsi] = useState<number | null>(null);
  const [typeEnabled, setTypeEnabled] = useState<Record<VesselTypeCode, boolean>>({
    PT: true,
    "PT-S": true,
    GN: true,
    OT: true,
    PS: true,
    FC: true,
  });
  const [showTargets, setShowTargets] = useState(true);
  const [showOthers, setShowOthers] = useState(false);

  const [baseMap, setBaseMap] = useState<BaseMapId>("enhanced");
  const [projection, setProjection] = useState<MapProjectionId>("mercator");

  const [overlays, setOverlays] = useState<MapToggleState>({
    pairLines: true,
    pairRange: true,
    fcLines: true,
    zones: true,
    fleetCircles: true,
  });

  const [settings, setSettings] = useState<Map3DSettings>({
    showShips: true,
    showDensity: false,
    showSeamark: false,
  });

  const [isProjectionLoading, setIsProjectionLoading] = useState(false);

  const [clock, setClock] = useState(() => new Date().toLocaleString("ko-KR", { hour12: false }));
  useEffect(() => {
    const id = window.setInterval(() => setClock(new Date().toLocaleString("ko-KR", { hour12: false })), 1000);
    return () => window.clearInterval(id);
  }, []);

  // Secret admin toggle: 7 clicks within 900ms on the logo.
  const [adminMode, setAdminMode] = useState(false);
  const clicksRef = useRef<number[]>([]);
  const onLogoClick = () => {
    const now = Date.now();
    clicksRef.current = clicksRef.current.filter((t) => now - t < 900);
    clicksRef.current.push(now);
    if (clicksRef.current.length >= 7) {
      clicksRef.current = [];
      setAdminMode((v) => !v);
    }
  };

  const targetsInScope = useMemo(() => {
    if (!useViewportFilter || !viewBbox) return targets;
    return targets.filter((t) => typeof t.lon === "number" && typeof t.lat === "number" && inBbox(t.lon, t.lat, viewBbox));
  }, [targets, useViewportFilter, viewBbox]);

  const legacyHits = useMemo(() => buildLegacyHitMap(targetsInScope, legacyIndex), [targetsInScope, legacyIndex]);
  const legacyVesselsAll = useMemo(() => deriveLegacyVessels({ targets: targetsInScope, legacyHits, zones }), [targetsInScope, legacyHits, zones]);
  const legacyCounts = useMemo(() => computeCountsByType(legacyVesselsAll), [legacyVesselsAll]);

  const legacyVesselsFiltered = useMemo(() => {
    if (!showTargets) return [];
    return filterByShipCodes(legacyVesselsAll, typeEnabled);
  }, [legacyVesselsAll, showTargets, typeEnabled]);

  const legacyMmsiForMap = useMemo(() => new Set(legacyVesselsFiltered.map((v) => v.mmsi)), [legacyVesselsFiltered]);

  const targetsForMap = useMemo(() => {
    const out = [];
    for (const t of targetsInScope) {
      const mmsi = t.mmsi;
      if (typeof mmsi !== "number") continue;
      const isLegacy = legacyHits.has(mmsi);
      if (isLegacy) {
        if (!showTargets) continue;
        if (!legacyMmsiForMap.has(mmsi)) continue;
        out.push(t);
        continue;
      }
      if (showOthers) out.push(t);
    }
    return out;
  }, [targetsInScope, legacyHits, showTargets, showOthers, legacyMmsiForMap]);

  const pairLinksAll = useMemo(() => computePairLinks(legacyVesselsAll), [legacyVesselsAll]);
  const fcLinksAll = useMemo(() => computeFcLinks(legacyVesselsAll), [legacyVesselsAll]);
  const alarms = useMemo(() => computeLegacyAlarms({ vessels: legacyVesselsAll, pairLinks: pairLinksAll, fcLinks: fcLinksAll }), [legacyVesselsAll, pairLinksAll, fcLinksAll]);

  const pairLinksForMap = useMemo(() => computePairLinks(legacyVesselsFiltered), [legacyVesselsFiltered]);
  const fcLinksForMap = useMemo(() => computeFcLinks(legacyVesselsFiltered), [legacyVesselsFiltered]);
  const fleetCirclesForMap = useMemo(() => computeFleetCircles(legacyVesselsFiltered), [legacyVesselsFiltered]);

  const selectedLegacyVessel = useMemo(() => {
    if (!selectedMmsi) return null;
    return legacyVesselsAll.find((v) => v.mmsi === selectedMmsi) ?? null;
  }, [legacyVesselsAll, selectedMmsi]);

  const selectedTarget = useMemo(() => {
    if (!selectedMmsi) return null;
    return targetsInScope.find((t) => t.mmsi === selectedMmsi) ?? null;
  }, [targetsInScope, selectedMmsi]);

  const selectedLegacyInfo = useMemo(() => {
    if (!selectedMmsi) return null;
    return legacyHits.get(selectedMmsi) ?? null;
  }, [selectedMmsi, legacyHits]);

  const fishingCount = legacyVesselsAll.filter((v) => v.state.isFishing).length;
  const transitCount = legacyVesselsAll.filter((v) => v.state.isTransit).length;

  const enabledTypes = useMemo(() => VESSEL_TYPE_ORDER.filter((c) => typeEnabled[c]), [typeEnabled]);
  const speedPanelType = (selectedLegacyVessel?.shipCode ?? (enabledTypes.length === 1 ? enabledTypes[0] : "PT")) as VesselTypeCode;

  return (
    <div className="app">
      <Topbar
        total={legacyVesselsAll.length}
        fishing={fishingCount}
        transit={transitCount}
        pairLinks={pairLinksAll.length}
        alarms={alarms.length}
        pollingStatus={snapshot.status}
        lastFetchMinutes={snapshot.lastFetchMinutes}
        clock={clock}
        adminMode={adminMode}
        onLogoClick={onLogoClick}
      />

      <div className="sidebar">
        <div className="sb">
          <div className="sb-t">업종 필터</div>
          <div className="tog">
            <div
              className={`tog-btn ${showTargets ? "on" : ""}`}
              onClick={() => {
                setShowTargets((v) => {
                  const next = !v;
                  if (!next) setSelectedMmsi((m) => (legacyHits.has(m ?? -1) ? null : m));
                  return next;
                });
              }}
              title="레거시(CN permit) 대상 선박 표시"
            >
              대상 선박
            </div>
            <div className={`tog-btn ${showOthers ? "on" : ""}`} onClick={() => setShowOthers((v) => !v)} title="대상 외 AIS 선박 표시">
              기타 AIS
            </div>
          </div>
          <TypeFilterGrid
            enabled={typeEnabled}
            totalCount={legacyVesselsAll.length}
            countsByType={legacyCounts}
            onToggle={(code) => {
              // When hiding the currently selected legacy vessel's type, clear selection.
              if (selectedLegacyVessel && selectedLegacyVessel.shipCode === code && typeEnabled[code]) setSelectedMmsi(null);
              setTypeEnabled((prev) => ({ ...prev, [code]: !prev[code] }));
            }}
            onToggleAll={() => {
              const allOn = VESSEL_TYPE_ORDER.every((c) => typeEnabled[c]);
              const nextVal = !allOn; // any-off -> true, all-on -> false
              if (!nextVal && selectedLegacyVessel) setSelectedMmsi(null);
              setTypeEnabled({
                PT: nextVal,
                "PT-S": nextVal,
                GN: nextVal,
                OT: nextVal,
                PS: nextVal,
                FC: nextVal,
              });
            }}
          />
        </div>

        <div className="sb">
          <div className="sb-t">지도 표시 설정</div>
          <MapToggles value={overlays} onToggle={(k) => setOverlays((prev) => ({ ...prev, [k]: !prev[k] }))} />
          <div style={{ fontSize: 9, fontWeight: 700, color: "var(--muted)", letterSpacing: 1.5, marginTop: 8, marginBottom: 6 }}>
            베이스맵
          </div>
          <div className="tog" style={{ flexWrap: "nowrap", alignItems: "center" }}>
            <div className={`tog-btn ${baseMap === "enhanced" ? "on" : ""}`} onClick={() => setBaseMap("enhanced")} title="현재 지도(해저지형/3D 표현 포함)">
              기본
            </div>
            <div className={`tog-btn ${baseMap === "legacy" ? "on" : ""}`} onClick={() => setBaseMap("legacy")} title="레거시 대시보드(Carto Dark) 베이스맵">
              레거시
            </div>

            <div style={{ flex: 1 }} />

            <div
              className={`tog-btn ${projection === "globe" ? "on" : ""}`}
              onClick={() => setProjection((p) => (p === "globe" ? "mercator" : "globe"))}
              title="지구본(globe) 투영: 드래그로 회전, 휠로 확대/축소"
            >
              지구본
            </div>
          </div>
          <div style={{ fontSize: 10, color: "var(--muted)" }}>지도 우하단 Attribution(라이센스) 표기 유지</div>
        </div>

        <div className="sb">
          <div className="sb-t">속도 프로파일</div>
          <SpeedProfilePanel selectedType={speedPanelType} />
        </div>

        <div className="sb" style={{ maxHeight: 260, display: "flex", flexDirection: "column", overflow: "hidden" }}>
          <div className="sb-t">
            선단 연관관계{" "}
            <span style={{ color: "var(--accent)", fontSize: 8 }}>
              {selectedLegacyVessel ? `${selectedLegacyVessel.permitNo} 연관` : "(선박 클릭 시 표시)"}
            </span>
          </div>
          <div style={{ overflowY: "auto", minHeight: 0 }}>
            <RelationsPanel
              selectedVessel={selectedLegacyVessel}
              vessels={legacyVesselsAll}
              fleetVessels={legacyVesselsFiltered}
              onSelectMmsi={setSelectedMmsi}
            />
          </div>
        </div>

        <div className="sb" style={{ flex: 1, minHeight: 0, display: "flex", flexDirection: "column" }}>
          <div className="sb-t">
            선박 목록{" "}
            <span style={{ color: "var(--accent)", fontSize: 8 }}>
              ({legacyVesselsFiltered.length}척)
            </span>
          </div>
          <VesselList vessels={legacyVesselsFiltered} selectedMmsi={selectedMmsi} onSelectMmsi={setSelectedMmsi} />
        </div>

        <div className="sb" style={{ maxHeight: 130, overflowY: "auto" }}>
          <div className="sb-t">실시간 경고</div>
          <AlarmsPanel alarms={alarms} onSelectMmsi={setSelectedMmsi} />
        </div>

        {adminMode ? (
          <>
            <div className="sb">
              <div className="sb-t">ADMIN · AIS Target Polling</div>
              <div style={{ fontSize: 11, lineHeight: 1.45 }}>
                <div style={{ color: "var(--muted)", fontSize: 10 }}>엔드포인트</div>
                <div style={{ wordBreak: "break-all" }}>{AIS_API_BASE}/api/ais-target/search</div>
                <div style={{ marginTop: 6, color: "var(--muted)", fontSize: 10 }}>상태</div>
                <div>
                  <b style={{ color: snapshot.status === "ready" ? "#22C55E" : snapshot.status === "error" ? "#EF4444" : "#F59E0B" }}>
                    {snapshot.status.toUpperCase()}
                  </b>
                  {snapshot.error ? <span style={{ marginLeft: 6, color: "#EF4444" }}>{snapshot.error}</span> : null}
                </div>
                <div style={{ marginTop: 6, color: "var(--muted)", fontSize: 10 }}>최근 fetch</div>
                <div>
                  {fmtLocal(snapshot.lastFetchAt)}{" "}
                  <span style={{ color: "var(--muted)", fontSize: 10 }}>
                    ({snapshot.lastFetchMinutes ?? "-"}m, inserted {snapshot.lastInserted}, upserted {snapshot.lastUpserted})
                  </span>
                </div>
                <div style={{ marginTop: 6, color: "var(--muted)", fontSize: 10 }}>메시지</div>
                <div style={{ color: "var(--text)", fontSize: 10 }}>{snapshot.lastMessage ?? "-"}</div>
              </div>
            </div>

            <div className="sb">
              <div className="sb-t">ADMIN · Legacy (CN Permit)</div>
              {legacyError ? (
                <div style={{ fontSize: 11, color: "#EF4444" }}>legacy load error: {legacyError}</div>
              ) : (
                <div style={{ fontSize: 11, lineHeight: 1.45 }}>
                  <div style={{ color: "var(--muted)", fontSize: 10 }}>데이터셋</div>
                  <div style={{ wordBreak: "break-all", fontSize: 10 }}>/data/legacy/chinese-permitted.v1.json</div>
                  <div style={{ marginTop: 6, color: "var(--muted)", fontSize: 10 }}>매칭(현재 scope)</div>
                  <div>
                    <b style={{ color: "#F59E0B" }}>{legacyVesselsAll.length}</b>{" "}
                    <span style={{ color: "var(--muted)", fontSize: 10 }}>/ {targetsInScope.length}</span>
                  </div>
                  <div style={{ marginTop: 6, color: "var(--muted)", fontSize: 10 }}>생성시각</div>
                  <div style={{ fontSize: 10, color: "var(--text)" }}>{legacyData?.generatedAt ? fmtLocal(legacyData.generatedAt) : "loading..."}</div>
                </div>
              )}
            </div>

            <div className="sb">
              <div className="sb-t">ADMIN · Viewport / BBox</div>
              <div style={{ fontSize: 11, lineHeight: 1.45 }}>
                <div style={{ color: "var(--muted)", fontSize: 10 }}>현재 View BBox</div>
                <div style={{ wordBreak: "break-all", fontSize: 10 }}>{fmtBbox(viewBbox)}</div>
                <div style={{ marginTop: 8, display: "flex", gap: 6, flexWrap: "wrap" }}>
                  <button
                    onClick={() => setUseViewportFilter((v) => !v)}
                    style={{
                      fontSize: 10,
                      padding: "4px 8px",
                      borderRadius: 6,
                      border: "1px solid var(--border)",
                      background: useViewportFilter ? "rgba(59,130,246,.18)" : "var(--card)",
                      color: "var(--text)",
                      cursor: "pointer",
                    }}
                    title="지도/리스트에 현재 화면 영역 내 선박만 표시(클라이언트 필터)"
                  >
                    Viewport filter {useViewportFilter ? "ON" : "OFF"}
                  </button>

                  <button
                    onClick={() => {
                      if (!viewBbox) return;
                      setUseApiBbox((v) => {
                        const next = !v;
                        if (next && viewBbox) setApiBbox(fmtBbox(viewBbox));
                        if (!next) setApiBbox(undefined);
                        return next;
                      });
                    }}
                    style={{
                      fontSize: 10,
                      padding: "4px 8px",
                      borderRadius: 6,
                      border: "1px solid var(--border)",
                      background: useApiBbox ? "rgba(245,158,11,.14)" : "var(--card)",
                      color: viewBbox ? "var(--text)" : "var(--muted)",
                      cursor: viewBbox ? "pointer" : "not-allowed",
                    }}
                    title="서버에서 bbox로 필터링해서 내려받기(페이로드 감소). 켜는 순간 현재 view bbox로 고정됨."
                    disabled={!viewBbox}
                  >
                    API bbox {useApiBbox ? "ON" : "OFF"}
                  </button>

                  <button
                    onClick={() => {
                      if (!viewBbox) return;
                      setApiBbox(fmtBbox(viewBbox));
                      setUseApiBbox(true);
                    }}
                    style={{
                      fontSize: 10,
                      padding: "4px 8px",
                      borderRadius: 6,
                      border: "1px solid var(--border)",
                      background: "var(--card)",
                      color: viewBbox ? "var(--text)" : "var(--muted)",
                      cursor: viewBbox ? "pointer" : "not-allowed",
                    }}
                    disabled={!viewBbox}
                    title="현재 view bbox로 API bbox를 갱신"
                  >
                    bbox=viewport
                  </button>
                </div>
                <div style={{ marginTop: 8, color: "var(--muted)", fontSize: 10 }}>
                  표시 선박: <b style={{ color: "var(--text)" }}>{targetsInScope.length}</b> / 스토어:{" "}
                  <b style={{ color: "var(--text)" }}>{snapshot.total}</b>
                </div>
              </div>
            </div>

            <div className="sb">
              <div className="sb-t">ADMIN · Map (Extras)</div>
              <Map3DSettingsToggles value={settings} onToggle={(k) => setSettings((prev) => ({ ...prev, [k]: !prev[k] }))} />
              <div style={{ fontSize: 10, color: "var(--muted)", marginTop: 6 }}>단일 WebGL 컨텍스트: MapboxOverlay(interleaved)</div>
            </div>

            <div className="sb" style={{ flex: 1, minHeight: 0, display: "flex", flexDirection: "column" }}>
              <div className="sb-t">ADMIN · AIS Targets (All)</div>
              <AisTargetList
                targets={targetsInScope}
                selectedMmsi={selectedMmsi}
                onSelectMmsi={setSelectedMmsi}
                legacyIndex={legacyIndex}
              />
            </div>

            <div className="sb" style={{ maxHeight: 130, overflowY: "auto" }}>
              <div className="sb-t">ADMIN · 수역 데이터</div>
              {zonesError ? (
                <div style={{ fontSize: 11, color: "#EF4444" }}>zones load error: {zonesError}</div>
              ) : (
                <div style={{ fontSize: 11, color: "var(--muted)" }}>
                  {zones ? `loaded (${zones.features.length} features)` : "loading..."}
                </div>
              )}
            </div>
          </>
        ) : null}
      </div>

      <div className="map-area">
        {isProjectionLoading ? (
          <div className="map-loader-overlay" role="status" aria-live="polite">
            <div className="map-loader-overlay__panel">
              <div className="map-loader-overlay__spinner" />
              <div className="map-loader-overlay__text">지도 모드 동기화 중...</div>
              <div className="map-loader-overlay__bar">
                <div className="map-loader-overlay__fill" />
              </div>
            </div>
          </div>
        ) : null}
        <Map3D
          targets={targetsForMap}
          zones={zones}
          selectedMmsi={selectedMmsi}
          settings={settings}
          baseMap={baseMap}
          projection={projection}
          overlays={overlays}
          onSelectMmsi={setSelectedMmsi}
          onViewBboxChange={setViewBbox}
          legacyHits={legacyHits}
          pairLinks={pairLinksForMap}
          fcLinks={fcLinksForMap}
          fleetCircles={fleetCirclesForMap}
          onProjectionLoadingChange={setIsProjectionLoading}
        />
        <MapLegend />
        {selectedLegacyVessel ? (
          <VesselInfoPanel vessel={selectedLegacyVessel} allVessels={legacyVesselsAll} onClose={() => setSelectedMmsi(null)} onSelectMmsi={setSelectedMmsi} />
        ) : selectedTarget ? (
          <AisInfoPanel target={selectedTarget} legacy={selectedLegacyInfo} onClose={() => setSelectedMmsi(null)} />
        ) : null}
      </div>
    </div>
  );
}
