import { useState, useEffect, useMemo, useCallback } from 'react';
import { Source, Layer } from 'react-map-gl/maplibre';
import type { GeoJSON } from 'geojson';
import type { Ship, VesselAnalysisDto } from '../../types';
import { fetchFleetCompanies } from '../../services/vesselAnalysis';
import type { FleetCompany } from '../../services/vesselAnalysis';

interface Props {
  ships: Ship[];
  analysisMap: Map<string, VesselAnalysisDto>;
  clusters: Map<number, string[]>;
  onShipSelect?: (mmsi: string) => void;
  onFleetZoom?: (bounds: { minLat: number; maxLat: number; minLng: number; maxLng: number }) => void;
}

// 두 벡터의 외적 (2D) — Graham scan에서 왼쪽 회전 여부 판별
function cross(o: [number, number], a: [number, number], b: [number, number]): number {
  return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
}

// Graham scan 기반 볼록 껍질 (반시계 방향)
function convexHull(points: [number, number][]): [number, number][] {
  const n = points.length;
  if (n < 2) return points.slice();
  const sorted = points.slice().sort((a, b) => a[0] - b[0] || a[1] - b[1]);
  const lower: [number, number][] = [];
  for (const p of sorted) {
    while (lower.length >= 2 && cross(lower[lower.length - 2], lower[lower.length - 1], p) <= 0) {
      lower.pop();
    }
    lower.push(p);
  }
  const upper: [number, number][] = [];
  for (let i = sorted.length - 1; i >= 0; i--) {
    const p = sorted[i];
    while (upper.length >= 2 && cross(upper[upper.length - 2], upper[upper.length - 1], p) <= 0) {
      upper.pop();
    }
    upper.push(p);
  }
  // lower + upper (첫/끝 중복 제거)
  lower.pop();
  upper.pop();
  return lower.concat(upper);
}

// 중심에서 각 꼭짓점 방향으로 padding 확장
function padPolygon(hull: [number, number][], padding: number): [number, number][] {
  if (hull.length === 0) return hull;
  const cx = hull.reduce((s, p) => s + p[0], 0) / hull.length;
  const cy = hull.reduce((s, p) => s + p[1], 0) / hull.length;
  return hull.map(([x, y]) => {
    const dx = x - cx;
    const dy = y - cy;
    const len = Math.sqrt(dx * dx + dy * dy);
    if (len === 0) return [x + padding, y + padding] as [number, number];
    const scale = (len + padding) / len;
    return [cx + dx * scale, cy + dy * scale] as [number, number];
  });
}

// cluster_id 해시 → HSL 색상
function clusterColor(id: number): string {
  const h = (id * 137) % 360;
  return `hsl(${h}, 80%, 55%)`;
}

// HSL 문자열 → hex 근사 (MapLibre는 hsl() 지원하므로 직접 사용 가능)
// GeoJSON feature에 color 속성으로 주입
interface ClusterPolygonFeature {
  type: 'Feature';
  id: number;
  properties: { clusterId: number; color: string };
  geometry: { type: 'Polygon'; coordinates: [number, number][][] };
}

interface ClusterLineFeature {
  type: 'Feature';
  id: number;
  properties: { clusterId: number; color: string };
  geometry: { type: 'LineString'; coordinates: [number, number][] };
}

type ClusterFeature = ClusterPolygonFeature | ClusterLineFeature;

export function FleetClusterLayer({ ships, analysisMap, clusters, onShipSelect, onFleetZoom }: Props) {
  const [companies, setCompanies] = useState<Map<number, FleetCompany>>(new Map());
  const [expanded, setExpanded] = useState(true);
  const [expandedFleet, setExpandedFleet] = useState<number | null>(null);
  const [hoveredFleetId, setHoveredFleetId] = useState<number | null>(null);
  const [expandedGearGroup, setExpandedGearGroup] = useState<string | null>(null);

  useEffect(() => {
    fetchFleetCompanies().then(setCompanies).catch(() => {});
  }, []);

  // 선박명 → mmsi 맵 (어구 매칭용)
  const gearsByParent = useMemo(() => {
    const map = new Map<string, Ship[]>(); // parent_mmsi → gears
    const gearPattern = /^(.+?)_\d+_\d*$/;
    const parentNames = new Map<string, string>(); // name → mmsi
    for (const s of ships) {
      if (s.name && !gearPattern.test(s.name)) {
        parentNames.set(s.name.trim(), s.mmsi);
      }
    }
    for (const s of ships) {
      const m = s.name?.match(gearPattern);
      if (!m) continue;
      const parentMmsi = parentNames.get(m[1].trim());
      if (parentMmsi) {
        const arr = map.get(parentMmsi) ?? [];
        arr.push(s);
        map.set(parentMmsi, arr);
      }
    }
    return map;
  }, [ships]);

  // ships map (mmsi → Ship)
  const shipMap = useMemo(() => {
    const m = new Map<string, Ship>();
    for (const s of ships) m.set(s.mmsi, s);
    return m;
  }, [ships]);

  // 비허가 어구 클러스터: parentName → { parent: Ship | null, gears: Ship[] }
  const gearGroupMap = useMemo(() => {
    const gearPattern = /^(.+?)_\d+_\d+_?$/;
    const nameToShip = new Map<string, Ship>();
    for (const s of ships) {
      const nm = (s.name || '').trim();
      if (nm && !gearPattern.test(nm)) {
        nameToShip.set(nm, s);
      }
    }
    const map = new Map<string, { parent: Ship | null; gears: Ship[] }>();
    for (const s of ships) {
      const m = (s.name || '').match(gearPattern);
      if (!m) continue;
      const parentName = m[1].trim();
      const entry = map.get(parentName) ?? { parent: nameToShip.get(parentName) ?? null, gears: [] };
      entry.gears.push(s);
      map.set(parentName, entry);
    }
    return map;
  }, [ships]);

  // 비허가 어구 클러스터 GeoJSON
  const gearClusterGeoJson = useMemo((): GeoJSON => {
    const features: GeoJSON.Feature[] = [];
    for (const [parentName, { parent, gears }] of gearGroupMap) {
      const points: [number, number][] = gears.map(g => [g.lng, g.lat]);
      if (parent) points.push([parent.lng, parent.lat]);
      if (points.length < 3) continue;
      const hull = convexHull(points);
      const padded = padPolygon(hull, 0.01);
      padded.push(padded[0]);
      features.push({
        type: 'Feature',
        properties: { name: parentName, gearCount: gears.length },
        geometry: { type: 'Polygon', coordinates: [padded] },
      });
    }
    return { type: 'FeatureCollection', features };
  }, [gearGroupMap]);

  // 어구 그룹 목록 (어구 수 내림차순)
  const gearGroupList = useMemo(() => {
    return Array.from(gearGroupMap.entries())
      .map(([name, { parent, gears }]) => ({ name, parent, gears }))
      .sort((a, b) => b.gears.length - a.gears.length);
  }, [gearGroupMap]);

  const handleGearGroupZoom = useCallback((parentName: string) => {
    const entry = gearGroupMap.get(parentName);
    if (!entry) return;
    const all: Ship[] = [...entry.gears];
    if (entry.parent) all.push(entry.parent);
    if (all.length === 0) return;
    let minLat = Infinity, maxLat = -Infinity, minLng = Infinity, maxLng = -Infinity;
    for (const s of all) {
      if (s.lat < minLat) minLat = s.lat;
      if (s.lat > maxLat) maxLat = s.lat;
      if (s.lng < minLng) minLng = s.lng;
      if (s.lng > maxLng) maxLng = s.lng;
    }
    if (minLat === Infinity) return;
    onFleetZoom?.({ minLat, maxLat, minLng, maxLng });
  }, [gearGroupMap, onFleetZoom]);

  // GeoJSON 피처 생성
  const polygonFeatures = useMemo((): ClusterFeature[] => {
    const features: ClusterFeature[] = [];
    for (const [clusterId, mmsiList] of clusters) {
      const points: [number, number][] = [];
      for (const mmsi of mmsiList) {
        const ship = shipMap.get(mmsi);
        if (ship) points.push([ship.lng, ship.lat]);
      }
      if (points.length < 2) continue;

      const color = clusterColor(clusterId);

      if (points.length === 2) {
        features.push({
          type: 'Feature',
          id: clusterId,
          properties: { clusterId, color },
          geometry: { type: 'LineString', coordinates: points },
        });
        continue;
      }

      const hull = convexHull(points);
      const padded = padPolygon(hull, 0.02);
      // 폴리곤 닫기
      const ring = [...padded, padded[0]];
      features.push({
        type: 'Feature',
        id: clusterId,
        properties: { clusterId, color },
        geometry: { type: 'Polygon', coordinates: [ring] },
      });
    }
    return features;
  }, [clusters, shipMap]);

  const polygonGeoJSON = useMemo((): GeoJSON => ({
    type: 'FeatureCollection',
    features: polygonFeatures.filter(f => f.geometry.type === 'Polygon'),
  }), [polygonFeatures]);

  const lineGeoJSON = useMemo((): GeoJSON => ({
    type: 'FeatureCollection',
    features: polygonFeatures.filter(f => f.geometry.type === 'LineString'),
  }), [polygonFeatures]);

  // 호버 하이라이트용 단일 폴리곤
  const hoveredGeoJSON = useMemo((): GeoJSON => {
    if (hoveredFleetId === null) return { type: 'FeatureCollection', features: [] };
    const f = polygonFeatures.find(p => p.properties.clusterId === hoveredFleetId && p.geometry.type === 'Polygon');
    if (!f) return { type: 'FeatureCollection', features: [] };
    return { type: 'FeatureCollection', features: [f] };
  }, [hoveredFleetId, polygonFeatures]);

  const handleFleetZoom = useCallback((clusterId: number) => {
    const mmsiList = clusters.get(clusterId) ?? [];
    if (mmsiList.length === 0) return;
    let minLat = Infinity, maxLat = -Infinity, minLng = Infinity, maxLng = -Infinity;
    for (const mmsi of mmsiList) {
      const ship = shipMap.get(mmsi);
      if (!ship) continue;
      if (ship.lat < minLat) minLat = ship.lat;
      if (ship.lat > maxLat) maxLat = ship.lat;
      if (ship.lng < minLng) minLng = ship.lng;
      if (ship.lng > maxLng) maxLng = ship.lng;
    }
    if (minLat === Infinity) return;
    onFleetZoom?.({ minLat, maxLat, minLng, maxLng });
  }, [clusters, shipMap, onFleetZoom]);

  const fleetList = useMemo(() => {
    return Array.from(clusters.entries())
      .map(([id, mmsiList]) => ({ id, mmsiList }))
      .sort((a, b) => b.mmsiList.length - a.mmsiList.length);
  }, [clusters]);

  // 패널 스타일 (AnalysisStatsPanel 패턴)
  const panelStyle: React.CSSProperties = {
    position: 'absolute',
    bottom: 60,
    left: 10,
    zIndex: 10,
    minWidth: 220,
    maxWidth: 300,
    backgroundColor: 'rgba(12, 24, 37, 0.92)',
    border: '1px solid rgba(99, 179, 237, 0.25)',
    borderRadius: 8,
    color: '#e2e8f0',
    fontFamily: 'monospace, sans-serif',
    fontSize: 11,
    boxShadow: '0 4px 16px rgba(0, 0, 0, 0.5)',
    overflow: 'hidden',
    display: 'flex',
    flexDirection: 'column',
    pointerEvents: 'auto',
  };

  const headerStyle: React.CSSProperties = {
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'space-between',
    padding: '6px 10px',
    borderBottom: expanded ? '1px solid rgba(99, 179, 237, 0.15)' : 'none',
    cursor: 'default',
    userSelect: 'none',
    flexShrink: 0,
  };

  const toggleButtonStyle: React.CSSProperties = {
    background: 'none',
    border: 'none',
    color: '#94a3b8',
    cursor: 'pointer',
    fontSize: 10,
    padding: '0 2px',
    lineHeight: 1,
  };

  return (
    <>
      {/* 선단 폴리곤 레이어 */}
      <Source id="fleet-cluster-fill" type="geojson" data={polygonGeoJSON}>
        <Layer
          id="fleet-cluster-fill-layer"
          type="fill"
          paint={{
            'fill-color': ['get', 'color'],
            'fill-opacity': 0.1,
          }}
        />
        <Layer
          id="fleet-cluster-line-layer"
          type="line"
          paint={{
            'line-color': ['get', 'color'],
            'line-opacity': 0.5,
            'line-width': 1.5,
          }}
        />
      </Source>

      {/* 2척 선단 라인 */}
      <Source id="fleet-cluster-line" type="geojson" data={lineGeoJSON}>
        <Layer
          id="fleet-cluster-line-only"
          type="line"
          paint={{
            'line-color': ['get', 'color'],
            'line-opacity': 0.5,
            'line-width': 1.5,
            'line-dasharray': [4, 2],
          }}
        />
      </Source>

      {/* 호버 하이라이트 (별도 Source) */}
      <Source id="fleet-cluster-hovered" type="geojson" data={hoveredGeoJSON}>
        <Layer
          id="fleet-cluster-hovered-fill"
          type="fill"
          paint={{
            'fill-color': ['get', 'color'],
            'fill-opacity': 0.25,
          }}
        />
      </Source>

      {/* 비허가 어구 클러스터 폴리곤 */}
      <Source id="gear-clusters" type="geojson" data={gearClusterGeoJson}>
        <Layer
          id="gear-cluster-fill-layer"
          type="fill"
          paint={{
            'fill-color': 'rgba(249, 115, 22, 0.08)',
          }}
        />
        <Layer
          id="gear-cluster-line-layer"
          type="line"
          paint={{
            'line-color': '#f97316',
            'line-opacity': 0.7,
            'line-width': 1.5,
            'line-dasharray': [4, 2],
          }}
        />
      </Source>

      {/* 선단 목록 패널 */}
      <div style={panelStyle}>
        <div style={headerStyle}>
          <span style={{ fontWeight: 700, color: '#63b3ed', letterSpacing: 0.5 }}>
            선단 현황 ({fleetList.length}개)
          </span>
          <button
            style={toggleButtonStyle}
            onClick={() => setExpanded(prev => !prev)}
            aria-label={expanded ? '패널 접기' : '패널 펼치기'}
          >
            {expanded ? '▲' : '▼'}
          </button>
        </div>

        {expanded && (
          <div style={{ maxHeight: 500, overflowY: 'auto', padding: '4px 0' }}>
            {fleetList.length === 0 ? (
              <div style={{ color: '#64748b', textAlign: 'center', padding: '8px 10px' }}>
                선단 데이터 없음
              </div>
            ) : (
              fleetList.map(({ id, mmsiList }) => {
                const company = companies.get(id);
                const companyName = company?.nameCn ?? `선단 #${id}`;
                const color = clusterColor(id);
                const isOpen = expandedFleet === id;
                const isHovered = hoveredFleetId === id;

                const mainVessels = mmsiList.filter(mmsi => {
                  const dto = analysisMap.get(mmsi);
                  return dto?.algorithms.fleetRole.role === 'LEADER' || dto?.algorithms.fleetRole.role === 'MEMBER';
                });
                const gearCount = mmsiList.reduce((acc, mmsi) => acc + (gearsByParent.get(mmsi)?.length ?? 0), 0);

                return (
                  <div key={id}>
                    {/* 선단 행 */}
                    <div
                      onMouseEnter={() => setHoveredFleetId(id)}
                      onMouseLeave={() => setHoveredFleetId(null)}
                      style={{
                        display: 'flex',
                        alignItems: 'center',
                        gap: 4,
                        padding: '4px 10px',
                        cursor: 'pointer',
                        backgroundColor: isHovered ? 'rgba(255,255,255,0.06)' : 'transparent',
                        borderLeft: isOpen ? `2px solid ${color}` : '2px solid transparent',
                        transition: 'background-color 0.1s',
                      }}
                    >
                      {/* 펼침 토글 */}
                      <span
                        onClick={() => setExpandedFleet(prev => (prev === id ? null : id))}
                        style={{ color: '#94a3b8', fontSize: 9, flexShrink: 0, cursor: 'pointer' }}
                      >
                        {isOpen ? '▾' : '▸'}
                      </span>
                      {/* 색상 인디케이터 */}
                      <span style={{
                        width: 8, height: 8, borderRadius: '50%',
                        backgroundColor: color, flexShrink: 0,
                      }} />
                      {/* 회사명 */}
                      <span
                        onClick={() => setExpandedFleet(prev => (prev === id ? null : id))}
                        style={{
                          flex: 1,
                          color: '#e2e8f0',
                          overflow: 'hidden',
                          textOverflow: 'ellipsis',
                          whiteSpace: 'nowrap',
                          cursor: 'pointer',
                        }}
                        title={company ? `${company.nameCn} / ${company.nameEn}` : `선단 #${id}`}
                      >
                        {companyName}
                      </span>
                      {/* 선박 수 */}
                      <span style={{ color: '#64748b', fontSize: 10, flexShrink: 0 }}>
                        ({mmsiList.length}척)
                      </span>
                      {/* zoom 버튼 */}
                      <button
                        onClick={e => { e.stopPropagation(); handleFleetZoom(id); }}
                        style={{
                          background: 'none',
                          border: '1px solid rgba(99,179,237,0.3)',
                          borderRadius: 3,
                          color: '#63b3ed',
                          fontSize: 9,
                          cursor: 'pointer',
                          padding: '1px 4px',
                          flexShrink: 0,
                        }}
                        title="이 선단으로 지도 이동"
                      >
                        zoom
                      </button>
                    </div>

                    {/* 선단 상세 */}
                    {isOpen && (
                      <div style={{
                        paddingLeft: 22,
                        paddingRight: 10,
                        paddingBottom: 6,
                        fontSize: 10,
                        color: '#94a3b8',
                        borderLeft: `2px solid ${color}33`,
                        marginLeft: 10,
                      }}>
                        {/* 선박 목록 */}
                        <div style={{ color: '#64748b', fontSize: 9, marginBottom: 3 }}>선박:</div>
                        {(mainVessels.length > 0 ? mainVessels : mmsiList).map(mmsi => {
                          const ship = shipMap.get(mmsi);
                          const dto = analysisMap.get(mmsi);
                          const role = dto?.algorithms.fleetRole.role ?? 'MEMBER';
                          const displayName = ship?.name || mmsi;
                          return (
                            <div
                              key={mmsi}
                              style={{
                                display: 'flex',
                                alignItems: 'center',
                                gap: 4,
                                marginBottom: 2,
                              }}
                            >
                              <span style={{ flex: 1, color: '#cbd5e1', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                                {displayName}
                              </span>
                              <span style={{ color: role === 'LEADER' ? '#fbbf24' : '#64748b', fontSize: 9, flexShrink: 0 }}>
                                ({role === 'LEADER' ? 'MAIN' : 'SUB'})
                              </span>
                              <button
                                onClick={() => onShipSelect?.(mmsi)}
                                style={{
                                  background: 'none',
                                  border: 'none',
                                  color: '#63b3ed',
                                  fontSize: 10,
                                  cursor: 'pointer',
                                  padding: '0 2px',
                                  flexShrink: 0,
                                }}
                                title="선박으로 이동"
                                aria-label={`${displayName} 선박으로 이동`}
                              >
                                ▶
                              </button>
                            </div>
                          );
                        })}

                        {/* 어구 목록 */}
                        {gearCount > 0 && (
                          <>
                            <div style={{ color: '#64748b', fontSize: 9, marginTop: 4, marginBottom: 2 }}>
                              어구: {gearCount}개
                            </div>
                            {mmsiList.flatMap(mmsi => gearsByParent.get(mmsi) ?? []).map(gear => (
                              <div key={gear.mmsi} style={{ color: '#475569', fontSize: 9, marginBottom: 1 }}>
                                {gear.name || gear.mmsi}
                              </div>
                            ))}
                          </>
                        )}
                      </div>
                    )}
                  </div>
                );
              })
            )}

            {/* 비허가 어구 그룹 섹션 */}
            {gearGroupList.length > 0 && (
              <>
                <div style={{
                  borderTop: '1px solid rgba(249,115,22,0.25)',
                  margin: '6px 10px',
                }} />
                <div style={{
                  padding: '2px 10px 4px',
                  fontSize: 10,
                  color: '#f97316',
                  fontWeight: 700,
                  letterSpacing: 0.3,
                }}>
                  비허가 어구 그룹 ({gearGroupList.length}개)
                </div>
                {gearGroupList.map(({ name, parent, gears }) => {
                  const isOpen = expandedGearGroup === name;
                  return (
                    <div key={name}>
                      <div
                        style={{
                          display: 'flex',
                          alignItems: 'center',
                          gap: 4,
                          padding: '3px 10px',
                          cursor: 'pointer',
                          borderLeft: isOpen ? '2px solid #f97316' : '2px solid transparent',
                          transition: 'background-color 0.1s',
                        }}
                        onMouseEnter={e => { (e.currentTarget as HTMLDivElement).style.backgroundColor = 'rgba(255,255,255,0.04)'; }}
                        onMouseLeave={e => { (e.currentTarget as HTMLDivElement).style.backgroundColor = 'transparent'; }}
                      >
                        <span
                          onClick={() => setExpandedGearGroup(prev => (prev === name ? null : name))}
                          style={{ color: '#94a3b8', fontSize: 9, flexShrink: 0, cursor: 'pointer' }}
                        >
                          {isOpen ? '▾' : '▸'}
                        </span>
                        <span style={{
                          width: 8, height: 8, borderRadius: '50%',
                          backgroundColor: '#f97316', flexShrink: 0,
                        }} />
                        <span
                          onClick={() => setExpandedGearGroup(prev => (prev === name ? null : name))}
                          style={{
                            flex: 1,
                            color: '#e2e8f0',
                            overflow: 'hidden',
                            textOverflow: 'ellipsis',
                            whiteSpace: 'nowrap',
                            cursor: 'pointer',
                          }}
                          title={name}
                        >
                          {name}
                        </span>
                        <span style={{ color: '#64748b', fontSize: 10, flexShrink: 0 }}>
                          ({gears.length}개)
                        </span>
                        <button
                          type="button"
                          onClick={e => { e.stopPropagation(); handleGearGroupZoom(name); }}
                          style={{
                            background: 'none',
                            border: '1px solid rgba(249,115,22,0.4)',
                            borderRadius: 3,
                            color: '#f97316',
                            fontSize: 9,
                            cursor: 'pointer',
                            padding: '1px 4px',
                            flexShrink: 0,
                          }}
                          title="이 어구 그룹으로 지도 이동"
                        >
                          zoom
                        </button>
                      </div>

                      {isOpen && (
                        <div style={{
                          paddingLeft: 24,
                          paddingRight: 10,
                          paddingBottom: 4,
                          fontSize: 9,
                          color: '#94a3b8',
                          borderLeft: '2px solid rgba(249,115,22,0.2)',
                          marginLeft: 10,
                        }}>
                          {parent && (
                            <div style={{ color: '#fbbf24', marginBottom: 2 }}>
                              모선: {parent.name || parent.mmsi}
                            </div>
                          )}
                          <div style={{ color: '#64748b', marginBottom: 2 }}>어구 목록:</div>
                          {gears.map(g => (
                            <div key={g.mmsi} style={{
                              display: 'flex',
                              alignItems: 'center',
                              gap: 4,
                              marginBottom: 1,
                            }}>
                              <span style={{ flex: 1, color: '#475569', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                                {g.name || g.mmsi}
                              </span>
                              <button
                                type="button"
                                onClick={() => onShipSelect?.(g.mmsi)}
                                style={{
                                  background: 'none',
                                  border: 'none',
                                  color: '#f97316',
                                  fontSize: 10,
                                  cursor: 'pointer',
                                  padding: '0 2px',
                                  flexShrink: 0,
                                }}
                                title="어구 위치로 이동"
                                aria-label={`${g.name || g.mmsi} 위치로 이동`}
                              >
                                ▶
                              </button>
                            </div>
                          ))}
                        </div>
                      )}
                    </div>
                  );
                })}
              </>
            )}
          </div>
        )}
      </div>
    </>
  );
}

export default FleetClusterLayer;
