import { useEffect, useState, useMemo, useRef, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { Card, CardContent } from '@shared/components/ui/card';
import { Badge } from '@shared/components/ui/badge';
import { PageContainer, PageHeader } from '@shared/components/layout';
import { DataTable, type DataColumn } from '@shared/components/common/DataTable';
import { Anchor, AlertTriangle, Loader2, Filter, X } from 'lucide-react';
import type { MapboxOverlay } from '@deck.gl/mapbox';
import {
  BaseMap, createStaticLayers,
  createGeoJsonLayer, createGearPolygonLayer,
  createShipIconLayer, createGearIconLayer,
  type MapHandle,
  type ShipIconData, type GearIconData,
} from '@lib/map';
import { fetchGroups, type GearGroupItem } from '@/services/vesselAnalysisApi';
import { formatDate } from '@shared/utils/dateFormat';
import { getPermitStatusIntent, getPermitStatusLabel, getGearJudgmentIntent } from '@shared/constants/permissionStatuses';
import { getAlertLevelHex } from '@shared/constants/alertLevels';
import { useSettingsStore } from '@stores/settingsStore';
import { getZoneCodeIntent, getZoneCodeLabel, getZoneAllowedGears } from '@shared/constants/zoneCodes';
import { getGearViolationIntent } from '@shared/constants/gearViolationCodes';
import { getGearGroupTypeLabel } from '@shared/constants/gearGroupTypes';
import { GearDetailPanel } from './components/GearDetailPanel';
import { GearReplayController } from './components/GearReplayController';
import { useGearReplayStore } from '@stores/gearReplayStore';
import { useGearReplayLayers } from '@/hooks/useGearReplayLayers';
import fishingZonesGeoJson from '@lib/map/data/fishing-zones-wgs84.json';

/* SFR-10: 불법 어망·어구 탐지 및 관리 */

type Gear = {
  id: string;
  groupKey: string;
  type: string;         // 어구 그룹 유형 (구역 내/외)
  owner: string;        // 모선 MMSI 또는 그룹 라벨
  zone: string;         // 수역 코드
  status: string;
  permit: string;
  installed: string;
  lastSignal: string;
  risk: string;
  lat: number;
  lng: number;
  parentStatus: string;
  parentMmsi: string;
  confidence: string;
  memberCount: number;
  members: Array<{ mmsi: string; name?: string; lat?: number; lon?: number; role?: string; isParent?: boolean }>;
  // 폴리곤 원본
  polygon: unknown;
  // G코드 위반 정보
  gCodes: string[];
  gearViolationScore: number;
  gearViolationEvidence: Record<string, Record<string, unknown>>;
  pairTrawlDetected: boolean;
  pairTrawlPairMmsi: string;
  allowedGears: string[];
  topScore: number; // 최대 후보 일치율 (0~1)
};

const RISK_HEX: Record<string, string> = {
  '고위험': getAlertLevelHex('CRITICAL'),
  '중위험': getAlertLevelHex('MEDIUM'),
  '안전': '#22c55e',
};

function deriveRisk(g: GearGroupItem): string {
  if (g.resolution?.status === 'REVIEW_REQUIRED') return '고위험';
  if (g.resolution?.status === 'UNRESOLVED') return '중위험';
  return '안전';
}

function deriveStatus(g: GearGroupItem): string {
  if (g.resolution?.status === 'REVIEW_REQUIRED') return '불법 의심';
  if (g.resolution?.status === 'UNRESOLVED') return '확인 중';
  if (g.resolution?.status === 'MANUAL_CONFIRMED') return '정상';
  return '확인 중';
}

/** 그룹 유형에서 수역 코드 추론 (backend가 zoneCode를 미제공하므로 groupType 기반) */
function deriveZone(g: GearGroupItem): string {
  if (g.groupType === 'GEAR_OUT_ZONE') return 'EEZ_OR_BEYOND';
  // GEAR_IN_ZONE: 위치 기반 추론 — 위도/경도로 대략적 수역 판별
  const lat = g.centerLat;
  const lon = g.centerLon;
  if (lat > 37.0 && lon > 129.0) return 'ZONE_I';    // 동해
  if (lat < 34.0 && lon > 127.0) return 'ZONE_II';   // 남해
  if (lat < 35.5 && lon < 127.0) return 'ZONE_III';  // 서남해
  if (lat >= 35.5 && lon < 126.5) return 'ZONE_IV';  // 서해
  return 'ZONE_III'; // 기본값
}

function mapGroupToGear(g: GearGroupItem, idx: number, t: (k: string, opts?: { defaultValue?: string }) => string, lang: 'ko' | 'en'): Gear {
  const risk = deriveRisk(g);
  const status = deriveStatus(g);
  const zone = deriveZone(g);
  // 그룹명: 항상 groupLabel/groupKey 사용 (모선 후보 MMSI는 별도 칼럼)
  const owner = g.groupLabel || g.groupKey;
  return {
    id: `G-${String(idx + 1).padStart(3, '0')}`,
    groupKey: g.groupKey,
    type: getGearGroupTypeLabel(g.groupType, t, lang),
    owner,
    zone,
    status,
    permit: 'NONE',
    installed: formatDate(g.snapshotTime),
    lastSignal: g.snapshotTime ? new Date(g.snapshotTime).toLocaleTimeString('ko-KR') : '-',
    risk,
    lat: g.centerLat,
    lng: g.centerLon,
    parentStatus: g.resolution?.status ?? '-',
    parentMmsi: g.resolution?.selectedParentMmsi ?? '-',
    confidence: (g.candidateCount ?? 0) > 0 ? `${g.candidateCount}건` : '-',
    memberCount: g.memberCount ?? 0,
    members: g.members ?? [],
    polygon: g.polygon,
    gCodes: [],
    gearViolationScore: 0,
    gearViolationEvidence: {},
    pairTrawlDetected: false,
    pairTrawlPairMmsi: '',
    allowedGears: getZoneAllowedGears(zone),
    topScore: Math.max(g.liveTopScore ?? 0, g.resolution?.topScore ?? 0),
  };
}

/** 필터 그룹 내 체크박스 목록 */
function FilterCheckGroup({ label, selected, onChange, options }: {
  label: string;
  selected: Set<string>;
  onChange: (v: Set<string>) => void;
  options: { value: string; label: string }[];
}) {
  const toggle = (v: string) => {
    const next = new Set(selected);
    if (next.has(v)) next.delete(v); else next.add(v);
    onChange(next);
  };
  return (
    <div className="space-y-1">
      <div className="text-[10px] text-hint font-medium">{label} {selected.size > 0 && <span className="text-primary">({selected.size})</span>}</div>
      <div className="flex flex-wrap gap-x-3 gap-y-1">
        {options.map(o => (
          <label key={o.value} className="flex items-center gap-1.5 text-[10px] text-label cursor-pointer hover:text-heading">
            <input type="checkbox" checked={selected.has(o.value)} onChange={() => toggle(o.value)}
              className="w-3 h-3 rounded border-border accent-primary" />
            {o.label}
          </label>
        ))}
      </div>
    </div>
  );
}

function ReplayOverlay() {
  const groupKey = useGearReplayStore(s => s.groupKey);
  if (!groupKey) return null;
  return <GearReplayController onClose={() => useGearReplayStore.getState().reset()} />;
}

export function GearDetection() {
  const { t } = useTranslation('detection');
  const { t: tc } = useTranslation('common');
  const lang = useSettingsStore((s) => s.language);

  const cols: DataColumn<Gear>[] = useMemo(() => [
    { key: 'id', label: 'ID', width: '70px', render: v => <span className="text-hint font-mono text-[10px]">{v as string}</span> },
    { key: 'type', label: '그룹 유형', width: '100px', sortable: true, render: v => <span className="text-heading font-medium text-[11px]">{v as string}</span> },
    { key: 'owner', label: '어구 그룹', sortable: true,
      render: v => <span className="text-cyan-400 font-mono text-[11px]">{v as string}</span> },
    { key: 'memberCount', label: '멤버', width: '50px', align: 'center',
      render: v => <span className="font-mono text-[10px] text-label">{v as number}척</span> },
    { key: 'zone', label: '설치 해역', width: '130px', sortable: true,
      render: (v: unknown) => (
        <Badge intent={getZoneCodeIntent(v as string)} size="sm">
          {getZoneCodeLabel(v as string, t, lang)}
        </Badge>
      ) },
    { key: 'permit', label: '허가', width: '70px', align: 'center',
      render: v => <Badge intent={getPermitStatusIntent(v as string)} size="sm">{getPermitStatusLabel(v as string, tc, lang)}</Badge> },
    { key: 'status', label: '판정', width: '80px', align: 'center', sortable: true,
      render: v => <Badge intent={getGearJudgmentIntent(v as string)} size="sm">{v as string}</Badge> },
    { key: 'gCodes', label: 'G코드', width: '100px',
      render: (_: unknown, row: Gear) => row.gCodes.length > 0 ? (
        <div className="flex flex-wrap gap-0.5">
          {row.gCodes.map(code => (
            <Badge key={code} intent={getGearViolationIntent(code)} size="sm">{code}</Badge>
          ))}
        </div>
      ) : <span className="text-hint text-[10px]">-</span> },
    { key: 'risk', label: '위험도', width: '65px', align: 'center', sortable: true,
      render: v => { const r = v as string; const c = r === '고위험' ? 'text-red-400' : r === '중위험' ? 'text-yellow-400' : 'text-green-400'; return <span className={`text-[10px] font-bold ${c}`}>{r}</span>; } },
    { key: 'parentStatus', label: '모선 상태', width: '90px', sortable: true,
      render: v => {
        const s = v as string;
        const intent = s === 'DIRECT_PARENT_MATCH' ? 'success' : s === 'AUTO_PROMOTED' ? 'info' : s === 'REVIEW_REQUIRED' ? 'warning' : 'muted';
        const label = s === 'DIRECT_PARENT_MATCH' ? '직접매칭' : s === 'AUTO_PROMOTED' ? '자동승격' : s === 'REVIEW_REQUIRED' ? '심사필요' : s === 'UNRESOLVED' ? '미결정' : s;
        return <Badge intent={intent} size="sm">{label}</Badge>;
      } },
    { key: 'parentMmsi', label: '추정 모선', width: '100px',
      render: v => { const m = v as string; return m !== '-' ? <span className="text-cyan-400 font-mono text-[10px]">{m}</span> : <span className="text-hint">-</span>; } },
    { key: 'topScore', label: '후보 일치', width: '75px', align: 'center', sortable: true,
      render: (v: unknown) => {
        const s = v as number;
        if (s <= 0) return <span className="text-hint text-[10px]">-</span>;
        const pct = Math.round(s * 100);
        const c = pct >= 72 ? 'text-green-400' : pct >= 50 ? 'text-yellow-400' : 'text-hint';
        return <span className={`font-mono text-[10px] font-bold ${c}`}>{pct}%</span>;
      } },
    { key: 'lastSignal', label: '최종 신호', width: '80px', render: v => <span className="text-muted-foreground text-[10px]">{v as string}</span> },
  ], [t, tc, lang]);

  const [groups, setGroups] = useState<GearGroupItem[]>([]);
  const [serviceAvailable, setServiceAvailable] = useState(true);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');
  const [selectedId, setSelectedId] = useState<string | null>(null);

  // ── 필터 상태 (다중 선택, localStorage 영속화) ──
  const [filterOpen, setFilterOpen] = useState(() => {
    try { return JSON.parse(localStorage.getItem('kcg-gear-filter-open') ?? 'false'); } catch { return false; }
  });
  const [filterZone, setFilterZone] = useState<Set<string>>(() => {
    try { return new Set(JSON.parse(localStorage.getItem('kcg-gear-fz') ?? '[]')); } catch { return new Set(); }
  });
  const [filterStatus, setFilterStatus] = useState<Set<string>>(() => {
    try { return new Set(JSON.parse(localStorage.getItem('kcg-gear-fs') ?? '[]')); } catch { return new Set(); }
  });
  const [filterRisk, setFilterRisk] = useState<Set<string>>(() => {
    try { return new Set(JSON.parse(localStorage.getItem('kcg-gear-fr') ?? '[]')); } catch { return new Set(); }
  });
  const [filterParentStatus, setFilterParentStatus] = useState<Set<string>>(() => {
    try { return new Set(JSON.parse(localStorage.getItem('kcg-gear-fps') ?? '[]')); } catch { return new Set(); }
  });
  const [filterPermit, setFilterPermit] = useState<Set<string>>(() => {
    try { return new Set(JSON.parse(localStorage.getItem('kcg-gear-fp') ?? '[]')); } catch { return new Set(); }
  });
  const [filterMemberMin, setFilterMemberMin] = useState(() => {
    try { return JSON.parse(localStorage.getItem('kcg-gear-fmn') ?? '2'); } catch { return 2; }
  });
  const [filterMemberMax, setFilterMemberMax] = useState(() => {
    try { const v = localStorage.getItem('kcg-gear-fmx'); return v ? JSON.parse(v) : Infinity; } catch { return Infinity; }
  });
  const checkFilterCount = filterZone.size + filterStatus.size + filterRisk.size + filterParentStatus.size + filterPermit.size;

  // localStorage 동기화
  useEffect(() => {
    localStorage.setItem('kcg-gear-filter-open', JSON.stringify(filterOpen));
    localStorage.setItem('kcg-gear-fz', JSON.stringify([...filterZone]));
    localStorage.setItem('kcg-gear-fs', JSON.stringify([...filterStatus]));
    localStorage.setItem('kcg-gear-fr', JSON.stringify([...filterRisk]));
    localStorage.setItem('kcg-gear-fps', JSON.stringify([...filterParentStatus]));
    localStorage.setItem('kcg-gear-fp', JSON.stringify([...filterPermit]));
    localStorage.setItem('kcg-gear-fmn', JSON.stringify(filterMemberMin));
    if (filterMemberMax !== Infinity) localStorage.setItem('kcg-gear-fmx', JSON.stringify(filterMemberMax));
    else localStorage.removeItem('kcg-gear-fmx');
  }, [filterOpen, filterZone, filterStatus, filterRisk, filterParentStatus, filterPermit, filterMemberMin, filterMemberMax]);

  const loadData = useCallback(async () => {
    setLoading(true);
    setError('');
    try {
      const res = await fetchGroups('GEAR');
      setServiceAvailable(res.serviceAvailable);
      setGroups(res.items);
    } catch (e: unknown) {
      setError(e instanceof Error ? e.message : '데이터를 불러올 수 없습니다');
      setServiceAvailable(false);
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => { loadData(); }, [loadData]);

  const DATA: Gear[] = useMemo(
    () => groups.map((g, i) => mapGroupToGear(g, i, t, lang)),
    [groups, t, lang],
  );

  // 필터 옵션 (고유값 추출)
  const filterOptions = useMemo(() => ({
    zones: [...new Set(DATA.map(d => d.zone))].sort(),
    statuses: [...new Set(DATA.map(d => d.status))],
    risks: [...new Set(DATA.map(d => d.risk))],
    parentStatuses: [...new Set(DATA.map(d => d.parentStatus))],
    permits: [...new Set(DATA.map(d => d.permit))],
    maxMember: DATA.reduce((max, d) => Math.max(max, d.memberCount), 0),
  }), [DATA]);

  const hasActiveFilter = checkFilterCount > 0 || (filterMemberMin > 2 || (filterMemberMax !== Infinity && filterMemberMax < filterOptions.maxMember));

  // 데이터 로드 후 멤버 슬라이더 최대값 초기화
  useEffect(() => {
    if (filterOptions.maxMember > 0 && filterMemberMax === Infinity) {
      setFilterMemberMax(filterOptions.maxMember);
    }
  }, [filterOptions.maxMember, filterMemberMax]);

  // ── 필터 적용 ──
  const filteredData = useMemo(() => {
    const effMax = filterMemberMax === Infinity ? filterOptions.maxMember : filterMemberMax;
    return DATA.filter(d =>
      (filterZone.size === 0 || filterZone.has(d.zone)) &&
      (filterStatus.size === 0 || filterStatus.has(d.status)) &&
      (filterRisk.size === 0 || filterRisk.has(d.risk)) &&
      (filterParentStatus.size === 0 || filterParentStatus.has(d.parentStatus)) &&
      (filterPermit.size === 0 || filterPermit.has(d.permit)) &&
      d.memberCount >= filterMemberMin && d.memberCount <= effMax,
    );
  }, [DATA, filterZone, filterStatus, filterRisk, filterParentStatus, filterPermit, filterMemberMin, filterMemberMax, filterOptions.maxMember]);

  const selectedGear = useMemo(
    () => DATA.find(g => g.id === selectedId) ?? null,
    [DATA, selectedId],
  );

  const mapRef = useRef<MapHandle>(null);

  // overlay Proxy ref — mapRef.current.overlay를 항상 최신으로 참조
  // iran 패턴: 리플레이 훅이 overlay.setProps() 직접 호출
  const overlayRef = useMemo<React.RefObject<MapboxOverlay | null>>(() => ({
    get current() { return mapRef.current?.overlay ?? null; },
  }), []);

  const replayGroupKey = useGearReplayStore(s => s.groupKey);
  const isReplayActive = !!replayGroupKey;

  const buildLayers = useCallback(() => {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const layers: any[] = [
      // 1. 정적 레이어 (EEZ + NLL)
      ...createStaticLayers(),

      // 2. 특정해역 I~IV 폴리곤 (항상 표시)
      createGeoJsonLayer('fishing-zones', fishingZonesGeoJson, '#6366f1', {
        fillOpacity: 15,
        lineWidth: 2,
        pickable: false,
      }),
    ];

    if (isReplayActive) {
      // ── 리플레이 모드: 정적 레이어만 (리플레이 훅이 나머지 직접 관리) ──
      // 비선택 어구 그룹 중심 (흐린 마름모) — 위치 참조용
      layers.push(createGearIconLayer(
        'gear-dim',
        DATA.filter(g => g.groupKey !== replayGroupKey).map(g => ({
          lat: g.lat, lon: g.lng, color: '#475569', size: 10,
        } as GearIconData)),
      ));
    } else if (selectedId) {
      // ── 선택 모드 (리플레이 비활성) ──
      const sel = DATA.find(g => g.id === selectedId);

      // 선택된 어구 그룹 폴리곤 강조
      if (sel?.polygon) {
        layers.push(createGearPolygonLayer('gear-polygon-selected', [{
          polygon: sel.polygon,
          color: '#f59e0b',
          label: `${sel.id} ${sel.type}`,
          risk: sel.risk,
        }]));
      }

      // 멤버 아이콘: 선박(PARENT)=삼각형+COG, 어구(GEAR)=마름모
      if (sel && sel.members.length > 0) {
        const ships = sel.members.filter(m => m.lat != null && m.lon != null && (m.isParent || m.role === 'PARENT'));
        const gears = sel.members.filter(m => m.lat != null && m.lon != null && !m.isParent && m.role !== 'PARENT');

        if (ships.length > 0) {
          layers.push(createShipIconLayer('sel-ships', ships.map(m => ({
            lat: m.lat!, lon: m.lon!,
            cog: (m as Record<string, unknown>).cog as number | undefined,
            color: '#06b6d4', size: 28,
            label: `${m.mmsi} ${m.name ?? ''}`,
            isParent: true,
          } as ShipIconData))));
        }

        if (gears.length > 0) {
          layers.push(createGearIconLayer('sel-gears', gears.map(m => ({
            lat: m.lat!, lon: m.lon!,
            color: '#f59e0b', size: 18,
            label: `${m.mmsi} ${m.name ?? ''}`,
          } as GearIconData))));
        }
      }

      // 비선택 어구 그룹 중심 (흐린 마름모)
      layers.push(createGearIconLayer(
        'gear-dim',
        DATA.filter(g => g.id !== selectedId).map(g => ({
          lat: g.lat, lon: g.lng, color: '#475569', size: 10,
        } as GearIconData)),
      ));
    } else {
      // ── 기본 모드: 모든 어구 폴리곤 + 아이콘 ──
      layers.push(createGearPolygonLayer(
        'gear-polygons',
        DATA.filter(g => g.polygon != null).map(g => ({
          polygon: g.polygon,
          color: RISK_HEX[g.risk] || '#64748b',
          label: `${g.id} ${g.type}`,
          risk: g.risk,
        })),
      ));

      // 어구 그룹 중심 마름모 아이콘
      layers.push(createGearIconLayer(
        'gear-center-icons',
        DATA.map(g => ({
          lat: g.lat, lon: g.lng,
          color: RISK_HEX[g.risk] || '#64748b',
          size: g.risk === '고위험' ? 20 : 14,
          label: `${g.id} ${g.owner}`,
        } as GearIconData)),
      ));
    }

    return layers;
  }, [DATA, selectedId, isReplayActive, replayGroupKey]);

  // 리플레이 비활성 시만 useMapLayers가 overlay 제어
  // 리플레이 활성 시 useGearReplayLayers가 overlay 독점 (iran 패턴: 단일 렌더링 경로)
  useEffect(() => {
    if (isReplayActive) return; // replay hook이 overlay 독점
    const raf = requestAnimationFrame(() => {
      mapRef.current?.overlay?.setProps({ layers: buildLayers() });
    });
    return () => cancelAnimationFrame(raf);
  }, [buildLayers, isReplayActive]);

  useGearReplayLayers(overlayRef, buildLayers);

  // 수역별 통계
  const zoneStats = useMemo(() => {
    const stats: Record<string, number> = {};
    filteredData.forEach(d => { stats[d.zone] = (stats[d.zone] || 0) + 1; });
    return stats;
  }, [filteredData]);

  return (
    <>
    <GearDetailPanel gear={selectedGear} onClose={() => setSelectedId(null)} />
    <PageContainer>
      <PageHeader
        icon={Anchor}
        iconColor="text-orange-400"
        title={t('gearDetection.title')}
        description={t('gearDetection.desc')}
      />

      {!serviceAvailable && (
        <div className="flex items-center gap-2 px-4 py-3 rounded-lg border border-yellow-500/30 bg-yellow-500/5 text-yellow-400 text-xs">
          <AlertTriangle className="w-4 h-4 shrink-0" />
          <span>iran 분석 서비스 미연결 - 실시간 어구 데이터를 불러올 수 없습니다</span>
        </div>
      )}

      {error && <div className="text-xs text-red-400">에러: {error}</div>}

      {loading && (
        <div className="flex items-center justify-center py-8 text-muted-foreground">
          <Loader2 className="w-5 h-5 animate-spin" />
        </div>
      )}

      {/* 요약 배지 */}
      <div className="flex gap-2 flex-wrap">
        {[
          { l: '전체 어구 그룹', v: filteredData.length, c: 'text-heading' },
          { l: '불법 의심', v: filteredData.filter(d => d.status.includes('불법')).length, c: 'text-red-400' },
          { l: '확인 중', v: filteredData.filter(d => d.status === '확인 중').length, c: 'text-yellow-400' },
          { l: '정상', v: filteredData.filter(d => d.status === '정상').length, c: 'text-green-400' },
        ].map(k => (
          <div key={k.l} className="flex-1 min-w-[100px] flex items-center gap-2 px-3 py-2 rounded-xl border border-border bg-card">
            <span className={`text-base font-bold ${k.c}`}>{k.v}</span><span className="text-[9px] text-hint">{k.l}</span>
          </div>
        ))}
      </div>

      {/* 수역별 분포 */}
      <div className="flex gap-1.5 flex-wrap">
        {Object.entries(zoneStats).sort(([,a],[,b]) => b - a).map(([zone, cnt]) => (
          <Badge key={zone} intent={getZoneCodeIntent(zone)} size="sm">
            {getZoneCodeLabel(zone, t, lang)} {cnt}건
          </Badge>
        ))}
      </div>

      {/* 필터 토글 버튼 */}
      <div className="flex items-center gap-2">
        <button type="button" onClick={() => setFilterOpen(!filterOpen)} aria-label="필터 설정"
          className={`flex items-center gap-1.5 px-3 py-1.5 text-[11px] border rounded-lg transition-colors ${
            hasActiveFilter
              ? 'bg-primary/10 border-primary/40 text-heading'
              : 'bg-surface-raised border-border text-label hover:border-primary/50'
          }`}>
          <Filter className="w-3.5 h-3.5" />
          세부 필터
          {hasActiveFilter && (
            <span className="bg-primary text-on-vivid rounded-full px-1.5 py-0.5 text-[9px] font-bold leading-none">
              {filterZone.size + filterStatus.size + filterRisk.size + filterParentStatus.size + filterPermit.size}
            </span>
          )}
        </button>
        {hasActiveFilter && (
          <>
            <span className="text-[10px] text-hint">{filteredData.length}/{DATA.length}건</span>
            <button type="button" aria-label="필터 초기화"
              onClick={() => { setFilterZone(new Set()); setFilterStatus(new Set()); setFilterRisk(new Set()); setFilterParentStatus(new Set()); setFilterPermit(new Set()); setFilterMemberMin(2); setFilterMemberMax(filterOptions.maxMember || Infinity); }}
              className="flex items-center gap-1 px-2 py-1 text-[10px] text-hint hover:text-label rounded hover:bg-surface-raised">
              <X className="w-3 h-3" /> 초기화
            </button>
          </>
        )}
      </div>

      {/* 필터 패널 (접기/펼치기) */}
      {filterOpen && (
        <div className="bg-surface-raised border border-border rounded-lg p-3 space-y-3">
          <FilterCheckGroup label="설치 해역" selected={filterZone} onChange={setFilterZone}
            options={filterOptions.zones.map(z => ({ value: z, label: getZoneCodeLabel(z, t, lang) }))} />
          <FilterCheckGroup label="판정" selected={filterStatus} onChange={setFilterStatus}
            options={filterOptions.statuses.map(s => ({ value: s, label: s }))} />
          <FilterCheckGroup label="위험도" selected={filterRisk} onChange={setFilterRisk}
            options={filterOptions.risks.map(r => ({ value: r, label: r }))} />
          <FilterCheckGroup label="모선 상태" selected={filterParentStatus} onChange={setFilterParentStatus}
            options={filterOptions.parentStatuses.map(s => ({
              value: s,
              label: s === 'DIRECT_PARENT_MATCH' ? '직접매칭' : s === 'AUTO_PROMOTED' ? '자동승격'
                : s === 'REVIEW_REQUIRED' ? '심사필요' : s === 'UNRESOLVED' ? '미결정' : s,
            }))} />
          <FilterCheckGroup label="허가" selected={filterPermit} onChange={setFilterPermit}
            options={filterOptions.permits.map(p => ({ value: p, label: getPermitStatusLabel(p, tc, lang) }))} />

          {/* 멤버 수 범위 슬라이더 */}
          {filterOptions.maxMember > 2 && (
            <div className="space-y-1">
              <div className="text-[10px] text-hint font-medium">
                멤버 수 <span className="text-label font-bold">{filterMemberMin}~{filterMemberMax === Infinity ? filterOptions.maxMember : filterMemberMax}척</span>
              </div>
              <div className="flex items-center gap-3">
                <span className="text-[9px] text-hint w-6 text-right">{filterMemberMin}</span>
                <input type="range" min={2} max={filterOptions.maxMember}
                  value={filterMemberMin} onChange={e => setFilterMemberMin(Number(e.target.value))}
                  aria-label="최소 멤버 수"
                  className="flex-1 h-1 accent-primary cursor-pointer" />
                <input type="range" min={2} max={filterOptions.maxMember}
                  value={filterMemberMax === Infinity ? filterOptions.maxMember : filterMemberMax}
                  onChange={e => setFilterMemberMax(Number(e.target.value))}
                  aria-label="최대 멤버 수"
                  className="flex-1 h-1 accent-primary cursor-pointer" />
                <span className="text-[9px] text-hint w-6">{filterMemberMax === Infinity ? filterOptions.maxMember : filterMemberMax}</span>
              </div>
            </div>
          )}

          {/* 패널 내 초기화 */}
          <div className="pt-2 border-t border-border flex items-center justify-between">
            <span className="text-[10px] text-hint">{filteredData.length}/{DATA.length}건 표시</span>
            <button type="button" aria-label="필터 초기화"
              onClick={() => { setFilterZone(new Set()); setFilterStatus(new Set()); setFilterRisk(new Set()); setFilterParentStatus(new Set()); setFilterPermit(new Set()); setFilterMemberMin(2); setFilterMemberMax(filterOptions.maxMember || Infinity); }}
              className="flex items-center gap-1 px-2 py-1 text-[10px] text-hint hover:text-label rounded hover:bg-surface-overlay">
              <X className="w-3 h-3" /> 전체 초기화
            </button>
          </div>
        </div>
      )}

      <DataTable
        data={filteredData}
        columns={cols}
        pageSize={10}
        searchPlaceholder="그룹유형, 모선, 해역 검색..."
        searchKeys={['type', 'owner', 'zone', 'groupKey']}
        exportFilename="어구탐지"
        onRowClick={(row: Gear) => {
          const newId = row.id === selectedId ? null : row.id;
          setSelectedId(newId);
          // 선택 시 지도 중심 이동
          if (newId) {
            const gear = DATA.find(g => g.id === newId);
            if (gear && mapRef.current?.map) {
              mapRef.current.map.flyTo({
                center: [gear.lng, gear.lat],
                zoom: 10,
                duration: 1000,
              });
            }
          }
        }}
      />

      {/* 어구 탐지 위치 지도 */}
      <Card>
        <CardContent className="p-0 relative">
          <BaseMap ref={mapRef} center={[34.5, 126.0]} zoom={7} height={500} className="rounded-lg overflow-hidden" />
          {/* 범례 */}
          <div className="absolute bottom-3 left-3 z-[1000] bg-background/90 backdrop-blur-sm border border-border rounded-lg px-3 py-2">
            <div className="text-[9px] text-muted-foreground font-bold mb-1.5">범례</div>
            <div className="space-y-1">
              <div className="flex items-center gap-1.5"><div className="w-2.5 h-2.5 rounded-full bg-red-500" /><span className="text-[8px] text-muted-foreground">고위험 어구 그룹</span></div>
              <div className="flex items-center gap-1.5"><div className="w-2.5 h-2.5 rounded-full bg-yellow-500" /><span className="text-[8px] text-muted-foreground">중위험 (확인 중)</span></div>
              <div className="flex items-center gap-1.5"><div className="w-2.5 h-2.5 rounded-full bg-green-500" /><span className="text-[8px] text-muted-foreground">안전 (정상)</span></div>
            </div>
            <div className="mt-1.5 pt-1.5 border-t border-border space-y-1">
              <div className="text-[8px] text-muted-foreground font-bold">특정해역</div>
              <div className="flex items-center gap-1.5"><div className="w-3 h-2 rounded-sm bg-purple-500/30 border border-purple-500/60" /><span className="text-[8px] text-muted-foreground">해역 I (동해)</span></div>
              <div className="flex items-center gap-1.5"><div className="w-3 h-2 rounded-sm bg-blue-500/30 border border-blue-500/60" /><span className="text-[8px] text-muted-foreground">해역 II (남해)</span></div>
              <div className="flex items-center gap-1.5"><div className="w-3 h-2 rounded-sm bg-cyan-500/30 border border-cyan-500/60" /><span className="text-[8px] text-muted-foreground">해역 III (서남해)</span></div>
              <div className="flex items-center gap-1.5"><div className="w-3 h-2 rounded-sm bg-amber-500/30 border border-amber-500/60" /><span className="text-[8px] text-muted-foreground">해역 IV (서해)</span></div>
            </div>
            <div className="flex items-center gap-3 mt-1.5 pt-1.5 border-t border-border">
              <div className="flex items-center gap-1"><div className="w-3 h-0 border-t border-dashed border-red-500/50" /><span className="text-[7px] text-hint">EEZ</span></div>
              <div className="flex items-center gap-1"><div className="w-3 h-0 border-t border-dashed border-orange-500/60" /><span className="text-[7px] text-hint">NLL</span></div>
            </div>
          </div>
          <div className="absolute top-3 left-3 z-[1000] flex items-center gap-2 bg-background/90 backdrop-blur-sm border border-border rounded-lg px-3 py-1.5">
            <Anchor className="w-3.5 h-3.5 text-orange-400" />
            <span className="text-[10px] text-orange-400 font-bold">{DATA.length}건</span>
            <span className="text-[9px] text-hint">어구 그룹</span>
          </div>
          {/* 리플레이 컨트롤러 (활성 시 표시) */}
          <ReplayOverlay />
        </CardContent>
      </Card>
    </PageContainer>
    </>
  );
}
