import { useEffect, useRef, useState } from 'react';
import type { MutableRefObject } from 'react';
import { useSettingsStore } from '../../store/settingsStore';

/**
 * 지도 기반 나침반 (노스업) — OSM 타일 위에 '현재 위치(중심 고정 점)' + '드론 시야(반투명 역삼각형)'.
 *   - 노스업: 지도는 북쪽 위로 고정. 드론이 움직이면 지도가 팬(현재 위치가 항상 중앙).
 *   - 시야 역삼각형(▽): 중심(드론)에서 진행/촬영 방향으로 벌어지는 반투명 부채꼴. yaw 로 회전.
 *   - 마우스 오버: 위젯 자체가 커져(스케일 아님, 타일 재배치) 더 넓은 영역을 선명하게 표시.
 *     휠로 줌 인/아웃. 벗어나면 기본 크기·기본 줌으로 복귀.
 *   - poseRef: 부모 RAF 가 매 프레임 { lat, lon, yaw(도, 0=N 시계+) } 갱신 → 내부 RAF 가 읽어
 *     역삼각형 회전(즉시) + 지도 팬(transform) + 임계 이동/줌·크기 변경 시 타일 재배치.
 *   - 타일은 서버 프록시(/api/tile) 경유(외부 직접 로드는 CSP img-src/COEP 로 차단).
 */

export interface CompassPose { lat: number; lon: number; yaw: number }

const BASE_D = 200;      // 기본 지름(px)
const HOVER_D = 400;     // 오버 시 지름(px, 2배) — 위젯 확대(스케일 아님 → 더 넓은 영역, 선명)
const TILE = 256;        // OSM 타일 크기
const MARGIN = TILE;     // 팬 중 빈틈 방지용 여유 타일 폭
const RELAYOUT_PX = 96;  // 중심이 이만큼 벗어나면 타일 재배치(팬 리셋)
const DEFAULT_ZOOM = 16;
const MIN_ZOOM = 12;
const MAX_ZOOM = 19;

/** 위경도 → 전역 픽셀(Web Mercator, zoom z). */
function project(lat: number, lon: number, z: number): { x: number; y: number } {
  const n = 2 ** z;
  const x = ((lon + 180) / 360) * n * TILE;
  const s = Math.min(0.9999, Math.max(-0.9999, Math.sin((lat * Math.PI) / 180)));
  const y = (0.5 - Math.log((1 + s) / (1 - s)) / (4 * Math.PI)) * n * TILE;
  return { x, y };
}

export function MapCompass({ poseRef }: { poseRef: MutableRefObject<CompassPose> }) {
  const mapStyle = useSettingsStore((s) => s.compassMapStyle); // 'street' | 'sat'
  const setMapStyle = useSettingsStore((s) => s.setCompassMapStyle);
  const src = mapStyle === 'sat' ? 'sat' : 'osm';
  const [hovered, setHovered] = useState(false);
  const [zoom, setZoomState] = useState(DEFAULT_ZOOM);
  const zoomRef = useRef(zoom);
  useEffect(() => { zoomRef.current = zoom; }, [zoom]);
  const setZoom = (z: number): void => setZoomState(Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, Math.round(z))));

  const D = hovered ? HOVER_D : BASE_D;
  const HALF = D / 2;

  // 타일 배치 기준 중심(이 좌표에 맞춰 타일을 깔고, 현재위치와의 차이는 transform 으로 팬).
  const [tileCenter, setTileCenter] = useState<{ lat: number; lon: number } | null>(null);
  const arrowRef = useRef<HTMLDivElement>(null);
  const layerRef = useRef<HTMLDivElement>(null);
  const containerRef = useRef<HTMLDivElement>(null);
  const tileCenterRef = useRef<{ lat: number; lon: number } | null>(null);
  useEffect(() => { tileCenterRef.current = tileCenter; }, [tileCenter]);

  // 줌 변경 시 현재 위치 기준으로 타일 재배치(줌 전환 튐 방지).
  useEffect(() => {
    const p = poseRef.current;
    if (p && isFinite(p.lat) && isFinite(p.lon)) setTileCenter({ lat: p.lat, lon: p.lon });
  }, [zoom, poseRef]);

  // 휠 줌(페이지 스크롤 대신). native 리스너(passive:false)로 preventDefault.
  useEffect(() => {
    const el = containerRef.current;
    if (!el) return;
    const onWheel = (e: WheelEvent): void => {
      e.preventDefault();
      setZoom(zoomRef.current + (e.deltaY < 0 ? 1 : -1)); // 위로 스크롤 = 확대
    };
    el.addEventListener('wheel', onWheel, { passive: false });
    return () => el.removeEventListener('wheel', onWheel);
  }, []);

  useEffect(() => {
    let raf = 0;
    const tick = (): void => {
      raf = requestAnimationFrame(tick);
      const p = poseRef.current;
      if (!p || !isFinite(p.lat) || !isFinite(p.lon)) return;
      const z = zoomRef.current;
      if (arrowRef.current) arrowRef.current.style.transform = `rotate(${p.yaw}deg)`;
      const tc = tileCenterRef.current;
      if (!tc) { setTileCenter({ lat: p.lat, lon: p.lon }); return; }
      const cur = project(p.lat, p.lon, z);
      const base = project(tc.lat, tc.lon, z);
      const dx = cur.x - base.x, dy = cur.y - base.y;
      if (layerRef.current) layerRef.current.style.transform = `translate(${-dx}px, ${-dy}px)`;
      if (Math.hypot(dx, dy) > RELAYOUT_PX) setTileCenter({ lat: p.lat, lon: p.lon });
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [poseRef]);

  // 벗어나면 기본값 복귀(기본 크기 + 기본 줌).
  const onLeave = (): void => { setHovered(false); setZoomState(DEFAULT_ZOOM); };

  // 타일 배치 — 기준 중심(tileCenter)을 뷰포트 중앙에 두고, 뷰포트+여유를 덮는 타일 나열.
  const tiles: { key: string; url: string; left: number; top: number }[] = [];
  if (tileCenter) {
    const c = project(tileCenter.lat, tileCenter.lon, zoom);
    const n = 2 ** zoom;
    const minTx = Math.floor((c.x - HALF - MARGIN) / TILE);
    const maxTx = Math.floor((c.x + HALF + MARGIN) / TILE);
    const minTy = Math.floor((c.y - HALF - MARGIN) / TILE);
    const maxTy = Math.floor((c.y + HALF + MARGIN) / TILE);
    for (let tx = minTx; tx <= maxTx; tx++) {
      for (let ty = minTy; ty <= maxTy; ty++) {
        if (ty < 0 || ty >= n) continue;
        const wx = ((tx % n) + n) % n; // 경도 래핑
        tiles.push({
          key: `${src}_${zoom}_${tx}_${ty}`,
          url: `/api/tile/${src}/${zoom}/${wx}/${ty}`,
          left: tx * TILE - c.x + HALF,
          top: ty * TILE - c.y + HALF,
        });
      }
    }
  }

  return (
    <div
      ref={containerRef}
      onMouseEnter={() => setHovered(true)}
      onMouseLeave={onLeave}
      onClick={() => setMapStyle(mapStyle === 'sat' ? 'street' : 'sat')}
      title="클릭: 위성 ↔ 일반 전환"
      style={{ position: 'absolute', top: 14, right: 14, width: D, height: D, zIndex: hovered ? 40 : 30, pointerEvents: 'auto', cursor: 'pointer' }}
    >
      {/* 원형 클립 지도 */}
      <div
        style={{
          position: 'absolute', inset: 0, borderRadius: '50%', overflow: 'hidden',
          background: 'rgba(16,22,32,0.55)', border: '3px solid rgba(255,255,255,0.92)', boxSizing: 'border-box',
        }}
      >
        {/* 팬 레이어 — RAF 가 transform 으로 이동. 타일은 tileCenter 기준 배치. */}
        <div ref={layerRef} style={{ position: 'absolute', inset: 0, willChange: 'transform' }}>
          {tiles.map((t) => (
            <img
              key={t.key} src={t.url} width={TILE} height={TILE} alt="" draggable={false}
              style={{ position: 'absolute', left: t.left, top: t.top, maxWidth: 'none' }}
            />
          ))}
        </div>
        {!tileCenter && (
          <div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'rgba(255,255,255,0.7)', fontSize: 11 }}>
            지도 로딩…
          </div>
        )}
      </div>

      {/* 드론 시야(역삼각형 ▽) — 중심(드론)에서 촬영방향으로 벌어짐. yaw 로 회전.
          주황 그라데이션: 근거리(드론) 진하게 → 멀수록 투명. 보더 없음. */}
      <div ref={arrowRef} style={{ position: 'absolute', inset: 0, transformOrigin: '50% 50%' }}>
        <svg viewBox="0 0 100 100" width={D} height={D} style={{ position: 'absolute', inset: 0 }}>
          <defs>
            <linearGradient id="fovGrad" gradientUnits="userSpaceOnUse" x1="50" y1="50" x2="50" y2="13">
              <stop offset="0%" stopColor="#ff8800" stopOpacity="0.85" />
              <stop offset="100%" stopColor="#ff8800" stopOpacity="0" />
            </linearGradient>
          </defs>
          {/* 꼭짓점이 현재위치 점에 붙음(50). */}
          <polygon points="50,50 27,13 73,13" fill="url(#fovGrad)" />
        </svg>
      </div>

      {/* 현재 위치 점(중심) + 상단 N(노스업 고정, 크게·붉은·반투명) — SVG 라 위젯 크기에 맞춰 스케일. */}
      <svg viewBox="0 0 100 100" width={D} height={D} style={{ position: 'absolute', inset: 0 }}>
        {/* 현재 위치 점 — 빨강 단색, 보더 없음. */}
        <circle cx="50" cy="50" r="3.6" fill="rgba(226,35,26,0.8)" />
        <text
          x="50" y="13.5" textAnchor="middle" fontSize="15" fontWeight="800"
          fill="#e22319" textLength="9" lengthAdjust="spacingAndGlyphs"
        >N</text>
      </svg>
    </div>
  );
}
