import React, { useEffect, useRef, useState, useCallback } from 'react';
import {
  toCameraCoords,
  pixelFromCamera,
  DEFAULT_CAMERA_PARAMS,
} from '../../utils/geoProjection';
import { useGeoStore } from '../../store/geoStore';
import { useSettingsStore, isGradeVisible } from '../../store/settingsStore';
import type { GeoPoint } from '../../types/geo';

interface RoutePanelProps {
  currentTime: number;
  visible: boolean;
  onSeek: (time: number) => void;
  /** 상단 침범 방지: 컨테이너 top(px). 기본 위쪽(배너/카메라파라미터) 아래. */
  topPx?: number;
  /** 하단 침범 방지: 컨테이너 bottom(px). 기본 아래쪽(배속/토글/재생바) 위. */
  bottomPx?: number;
}

const VIDEO_FPS = 30000 / 1001;

const cleanTitle = (t: string) => t.replace(/\s*\([상하]\)\s*$/, '').trim();

function stationKm(title: string): number {
  const m = title.match(/(\d+)\s*[Kk+]\s*(\d+)/);
  if (!m) return -1;
  return parseInt(m[1]) * 1000 + parseInt(m[2]);
}

/** 미터값 → "158+160" (10m 단위, 재생바 배지와 동일 형식). */
function formatKm10(m: number): string {
  const r = Math.round(m / 10) * 10;
  return `${Math.floor(r / 1000)}+${String(r % 1000).padStart(3, '0')}`;
}

const CATEGORY_EMOJI: Record<string, string> = {
  '\uD130\uB110': '\uD83D\uDE87',
  '\uAD50\uB7C9': '\uD83C\uDF09',
  '\uC5ED\uC0AC': '\uD83D\uDE89',
  '\uC9C0\uC7A5\uBB3C': '\uD83C\uDFE2',
  '\uCE21\uC810': '\uD83D\uDCCD',
};

function poiKm(poi: GeoPoint, stations: GeoPoint[]): number {
  if (!stations.length) return -1;
  const sorted = [...stations]
    .map(st => ({
      st,
      d: (poi.lat - st.lat) ** 2 + (poi.lon - st.lon) ** 2,
    }))
    .sort((a, b) => a.d - b.d);
  const a = sorted[0], b = sorted[1];
  if (!b || b.d === 0) return stationKm(a.st.title);
  const ka = stationKm(a.st.title), kb = stationKm(b.st.title);
  if (ka < 0 || kb < 0) return ka >= 0 ? ka : kb;
  const t = a.d / (a.d + b.d);
  return Math.round(ka + (kb - ka) * t);
}

export default function RoutePanel({ currentTime, visible, onSeek, topPx = 90, bottomPx = 200 }: RoutePanelProps) {
  // 지리정보는 클라이언트 geoStore(폴더 선택 파싱 결과)에서 직접 읽는다.
  // 서버 /api/geo/* fetch 대체. 폴더 미선택 시 빈 배열 → idle 렌더.
  const loaded = useGeoStore(s => s.loaded);
  const stations = useGeoStore(s => s.stations);
  const pois = useGeoStore(s => s.pois);
  const structures = useGeoStore(s => s.structures);
  const gradeFilter = useSettingsStore(s => s.gradeFilter);
  const poiOverlapExclude = useSettingsStore(s => s.poiOverlapExclude);
  const droneFrames = useGeoStore(s => s.frames);
  const routeMeta = useGeoStore(s => s.routeMeta);
  const [currentKm, setCurrentKm] = useState(0);
  const [currentStationTitle, setCurrentStationTitle] = useState('');
  const [visibleRange, setVisibleRange] = useState<{ minKm: number; maxKm: number } | null>(null);
  const [routeStartTitle, setRouteStartTitle] = useState('');
  const [routeEndTitle, setRouteEndTitle] = useState('');
  const panelRef = useRef<HTMLDivElement>(null);
  const [dragYPct, setDragYPct] = useState(0);
  const [dragging, setDragging] = useState(false);

  // POI/측점/드론 프레임은 위 geoStore 셀렉터로 구독한다(폴더 데이터 도착 시 자동 재렌더).

  // 시점/종점: 역사(category=역사) POI 중 km 최소/최대
  useEffect(() => {
    if (!stations.length || !pois.length) return;
    const validSt = stations.filter(s => stationKm(s.title) >= 0);
    if (!validSt.length) return;
    const stationPois = pois.filter(p => p.category === '\uC5ED\uC0AC' || p.category === '\uCCA0\uB3C4\uC5ED'); // 역사
    if (!stationPois.length) return;
    let minKmPoi = stationPois[0], maxKmPoi = stationPois[0];
    let minK = poiKm(stationPois[0], validSt), maxK = minK;
    for (let i = 1; i < stationPois.length; i++) {
      const k = poiKm(stationPois[i], validSt);
      if (k >= 0 && k < minK) { minK = k; minKmPoi = stationPois[i]; }
      if (k >= 0 && k > maxK) { maxK = k; maxKmPoi = stationPois[i]; }
    }
    setRouteStartTitle(minKmPoi.title);
    setRouteEndTitle(maxKmPoi.title);
  }, [stations, pois]);

  // Update current km and visible range based on currentTime
  useEffect(() => {
    const frames = droneFrames;
    if (!frames.length || !stations.length) return;

    // Find closest frame by time
    const targetFrame = Math.round(currentTime * VIDEO_FPS);
    let closest = frames[0];
    let closestDist = Math.abs(closest.frame - targetFrame);
    for (let i = 1; i < frames.length; i++) {
      const d = Math.abs(frames[i].frame - targetFrame);
      if (d < closestDist) {
        closest = frames[i];
        closestDist = d;
      }
    }

    // Find nearest station to current drone position
    const validStations = stations.filter(s => stationKm(s.title) >= 0);
    if (!validStations.length) return;

    let nearestStation = validStations[0];
    let nearestDist = (closest.lat - nearestStation.lat) ** 2 + (closest.lon - nearestStation.lon) ** 2;
    for (let i = 1; i < validStations.length; i++) {
      const d = (closest.lat - validStations[i].lat) ** 2 + (closest.lon - validStations[i].lon) ** 2;
      if (d < nearestDist) {
        nearestStation = validStations[i];
        nearestDist = d;
      }
    }
    setCurrentStationTitle(nearestStation.title);

    // 현재 km = 측점 폴리라인 투영 연속 체이니지 (재생바 StationBar 와 동일 기준).
    const sorted = [...validStations].sort((a, b) => stationKm(a.title) - stationKm(b.title));
    const lat0 = sorted.reduce((s, p) => s + p.lat, 0) / sorted.length;
    const k = Math.cos((lat0 * Math.PI) / 180) * 111000;
    const pts = sorted.map(p => ({ x: p.lon * k, y: p.lat * 111000, km: stationKm(p.title) }));
    const cpx = closest.lon * k, cpy = closest.lat * 111000;
    let bestD = Infinity, bestKm = pts.length ? pts[0].km : 0;
    for (let i = 0; i < pts.length - 1; i++) {
      const a = pts[i], b = pts[i + 1];
      const dx = b.x - a.x, dy = b.y - a.y;
      const L2 = dx * dx + dy * dy;
      const t = L2 === 0 ? 0 : Math.max(0, Math.min(1, ((cpx - a.x) * dx + (cpy - a.y) * dy) / L2));
      const ex = a.x + dx * t, ey = a.y + dy * t;
      const dd = (cpx - ex) ** 2 + (cpy - ey) ** 2;
      if (dd < bestD) { bestD = dd; bestKm = a.km + (b.km - a.km) * t; }
    }
    setCurrentKm(bestKm);

    // Calculate visible range (green box)
    const allPoints = [...validStations, ...pois];
    const visibleKms: number[] = [];
    for (const pt of allPoints) {
      const cc = toCameraCoords(closest, pt.lat, pt.lon, pt.z, DEFAULT_CAMERA_PARAMS);
      if (cc.Zc <= 0) continue;
      const { pyRaw } = pixelFromCamera(cc, DEFAULT_CAMERA_PARAMS);
      if (pyRaw >= 0.0 && pyRaw <= 1.0) {
        const km = pt.type === 'station' ? stationKm(pt.title) : poiKm(pt, validStations);
        if (km >= 0) visibleKms.push(km);
      }
    }
    if (visibleKms.length >= 2) {
      setVisibleRange({
        minKm: Math.min(...visibleKms),
        maxKm: Math.max(...visibleKms),
      });
    } else {
      setVisibleRange(null);
    }
  }, [currentTime, droneFrames, stations, pois]);

  // Drag handling
  const handleMouseDown = useCallback((e: React.MouseEvent) => {
    e.preventDefault();
    setDragging(true);
  }, []);

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

    const handleMouseMove = (e: MouseEvent) => {
      if (!panelRef.current) return;
      const rect = panelRef.current.getBoundingClientRect();
      const y = Math.max(0, Math.min(1, (e.clientY - rect.top) / rect.height));
      setDragYPct(y * 100);
    };

    const handleMouseUp = (e: MouseEvent) => {
      setDragging(false);
      if (!panelRef.current) return;
      const rect = panelRef.current.getBoundingClientRect();
      const yPct = Math.max(0, Math.min(1, (e.clientY - rect.top) / rect.height));

      const validStations = stations.filter(s => stationKm(s.title) >= 0);
      if (validStations.length < 2) return;

      const allKms = [
        ...validStations.map(s => stationKm(s.title)),
        ...pois.map(p => poiKm(p, validStations)).filter(k => k >= 0),
      ];
      const minK = Math.min(...allKms);
      const maxK = Math.max(...allKms);
      const targetKm = maxK - yPct * (maxK - minK);

      // Find closest station to target km
      let bestStation = validStations[0];
      let bestDiff = Math.abs(stationKm(bestStation.title) - targetKm);
      for (let i = 1; i < validStations.length; i++) {
        const diff = Math.abs(stationKm(validStations[i].title) - targetKm);
        if (diff < bestDiff) {
          bestStation = validStations[i];
          bestDiff = diff;
        }
      }

      // Find closest drone frame to that station's lat/lon
      const frames = droneFrames;
      if (!frames.length) return;
      let bestFrame = frames[0];
      let bestFrameDist = (bestFrame.lat - bestStation.lat) ** 2 + (bestFrame.lon - bestStation.lon) ** 2;
      for (let i = 1; i < frames.length; i++) {
        const d = (frames[i].lat - bestStation.lat) ** 2 + (frames[i].lon - bestStation.lon) ** 2;
        if (d < bestFrameDist) {
          bestFrame = frames[i];
          bestFrameDist = d;
        }
      }
      onSeek(bestFrame.frame / VIDEO_FPS);
    };

    window.addEventListener('mousemove', handleMouseMove);
    window.addEventListener('mouseup', handleMouseUp);
    return () => {
      window.removeEventListener('mousemove', handleMouseMove);
      window.removeEventListener('mouseup', handleMouseUp);
    };
  }, [dragging, stations, pois, droneFrames, onSeek]);

  // Render guard — 폴더 미선택(!loaded) 또는 측점 없음이면 idle(미표시)
  if (!visible || !loaded || stations.length === 0) return null;
  const validStations = stations.filter(s => stationKm(s.title) >= 0);
  if (validStations.length < 2) return null;

  const allKms = [
    ...validStations.map(s => stationKm(s.title)),
    ...pois.map(p => poiKm(p, validStations)).filter(k => k >= 0),
  ];
  const minKm = Math.min(...allKms);
  const maxKm = Math.max(...allKms);
  const kmToY = (km: number) => (1 - (km - minKm) / (maxKm - minKm)) * 100;

  // 교량/터널만 표시 + 시설종별 필터(설정 스토어, 재생바와 동일 규칙).
  // \uACB9\uCE68\uC81C\uC678 ON \uC2DC \uAC19\uC740 \uC704\uCE58 \uD615\uC81C \uC911 \uC9C4\uD589\uBC29\uD5A5((\uD558)/(\uC0C1))\uC744 \uC6B0\uC120 \uB0A8\uAE30\uB3C4\uB85D dir-\uC6B0\uC120 \uC815\uB82C.
  const routeDir = routeMeta?.routeInfo?.direction?.includes('\uD558') ? '\uD558'
    : routeMeta?.routeInfo?.direction?.includes('\uC0C1') ? '\uC0C1' : null;
  const dirTag = routeDir === '\uC0C1' ? '(\uC0C1)' : routeDir === '\uD558' ? '(\uD558)' : '';
  // \uACBD\uB85C\uC0C1 \uC2DC\uC124\uBB3C\uB9CC: \uBC29\uD5A5\uD45C\uAE30((\uC0C1\u2026/(\uD558\u2026)\uAC00 \uC788\uC73C\uBA74 \uC601\uC0C1 \uBC29\uD5A5 \uC77C\uCE58\uD558\uB294 \uAC83\uB9CC(\uBC18\uB300 \uBC29\uD5A5=\uB2E4\uB978 \uC120\uB85C \uC81C\uC678).
  const oppCh = routeDir === '\uD558' ? '\uC0C1' : routeDir === '\uC0C1' ? '\uD558' : '';
  const isOppositeDir = (name: string): boolean => !!oppCh && new RegExp(`[(\uFF08]${oppCh}`).test(name);
  const baseStruct = (t: string): string => t.replace(/\s*[(\uFF08].*$/, '').trim();
  // \uAC19\uC740 \uC88C\uD45C(\uAC19\uC740 base \uBCC0\uD615)\uB294 \uACB9\uCE68\uC81C\uC678 \uC635\uC158\uACFC \uBB34\uAD00\uD558\uAC8C \uC9C4\uD589\uBC29\uD5A5 1\uAC1C\uB9CC(dir-\uC6B0\uC120 \uC815\uB82C \uD6C4 base\uBCC4 \uCCAB\uC9F8).
  const seenBase = new Set<string>();
  const filteredPois = structures
    .filter(s => (s.type === 'bridge' || s.type === 'tunnel') && typeof s.lat === 'number' && typeof s.lon === 'number' && isGradeVisible(s.grade, gradeFilter) && !isOppositeDir(s.name))
    .map(s => ({ title: s.name, category: s.type === 'tunnel' ? '\uD130\uB110' : '\uAD50\uB7C9', lat: s.lat as number, lon: s.lon as number, z: 0, type: 'poi' as const }))
    .sort((a, b) => (dirTag && a.title.includes(dirTag) ? 0 : 1) - (dirTag && b.title.includes(dirTag) ? 0 : 1))
    .filter(p => { const b = baseStruct(p.title); if (seenBase.has(b)) return false; seenBase.add(b); return true; });

  return (
    <div
      ref={panelRef}
      className="absolute w-28 border border-white/20 rounded-md z-30"
      style={{ left: 8, top: topPx, bottom: bottomPx, background: 'rgba(0,0,0,0.6)' }}
    >
      {/* Center vertical line */}
      <div
        className="absolute"
        style={{ left: 38, width: 2, top: 22, bottom: 22, background: 'rgba(255,255,255,0.5)' }}
      />

      {/* 높은 km 역명 — 상단 (대전조차장) */}
      <div className="absolute left-0 right-0 flex items-center gap-1" style={{ top: 4 }}>
        <div className="w-2 h-2 rounded-full bg-white/80 shrink-0" style={{ marginLeft: 29 }} />
        <span className="text-[11px] text-white/90 font-semibold truncate">{routeMeta?.routeInfo?.endStationName || cleanTitle(routeEndTitle)}</span>
      </div>

      {/* 낮은 km 역명 — 하단 (회덕) */}
      <div className="absolute left-0 right-0 flex items-center gap-1" style={{ bottom: 4 }}>
        <div className="w-2 h-2 rounded-full bg-white/80 shrink-0" style={{ marginLeft: 29 }} />
        <span className="text-[11px] text-white/90 font-semibold truncate">{routeMeta?.routeInfo?.startStationName || cleanTitle(routeStartTitle)}</span>
      </div>

      {/* 교량/터널 POIs — 겹침 방지: Y 간격 7% 미만이면 건너뜀 */}
      {(() => {
        const MIN_GAP = 9; // %
        const placed: number[] = [];
        return filteredPois.map((poi, i) => {
          const km = poiKm(poi, validStations);
          if (km < 0) return null;
          const y = kmToY(km);
          if (y < 5 || y > 95) return null;
          // 겹침제외 ON 일 때만 겹친 것(같은 위치 형제 포함) 제외 → 방향 우선(위 dir-정렬)으로 1개만.
          // OFF 면 모두 표시(겹쳐도). 영상 오버레이 토글과 동일 규칙.
          if (poiOverlapExclude) {
            if (placed.some(py => Math.abs(py - y) < MIN_GAP)) return null;
            placed.push(y);
          }
          return (
          <div
            key={`poi-${i}`}
            className="absolute flex items-center pointer-events-none"
            style={{ top: `${y}%`, transform: 'translateY(-50%)', left: 0, right: 0 }}
          >
            <div style={{ position: 'absolute', left: 30, width: 16, display: 'flex', justifyContent: 'center' }}>
              <div
                className="w-3 h-3 rounded-sm"
                style={{ background: poi.category === '\uD130\uB110' ? '#818cf8' : '#38bdf8' }}
              />
            </div>
            <div
              className="text-[10px] truncate font-medium"
              style={{
                position: 'absolute', left: 48, right: 2,
                color: poi.category === '\uD130\uB110' ? '#c7d2fe' : '#bae6fd',
              }}
            >
              {cleanTitle(poi.title)}
            </div>
          </div>
        );
        });
      })()}

      {/* Green visible range box */}
      {visibleRange && (
        <div
          className="absolute pointer-events-none"
          style={{
            left: 30,
            right: 4,
            top: `${kmToY(visibleRange.maxKm)}%`,
            bottom: `${100 - kmToY(visibleRange.minKm)}%`,
            border: '1px solid rgba(74,222,128,0.7)',
            background: 'rgba(74,222,128,0.08)',
          }}
        />
      )}

      {/* Orange current position marker */}
      <div
        className="absolute left-0 right-0 flex items-center cursor-grab z-10"
        style={{
          top: `${dragging ? dragYPct : kmToY(currentKm)}%`,
          transform: 'translateY(-50%)',
        }}
        onMouseDown={handleMouseDown}
      >
        <div
          style={{
            position: 'absolute',
            left: 30,
            width: 12,
            height: 12,
            borderRadius: '50%',
            background: '#f97316',
            border: '2px solid white',
            transform: 'translateX(-50%)',
          }}
        />
        <div
          style={{ position: 'absolute', left: 44 }}
          className="bg-orange-500 text-white text-[11px] font-bold px-1.5 py-0.5 rounded whitespace-nowrap"
        >
          {formatKm10(currentKm)}
        </div>
      </div>
    </div>
  );
}
