import { useEffect, useRef, useCallback } from 'react';
import { useMap } from '@vis.gl/react-maplibre';
import type { Map as MapLibreMap } from 'maplibre-gl';

interface CurrentVectorPoint {
  lat: number;
  lon: number;
  u: number; // 동서 방향 속도 (양수=동, 음수=서) [m/s]
  v: number; // 남북 방향 속도 (양수=북, 음수=남) [m/s]
}

interface Particle {
  x: number;
  y: number;
  age: number;
  maxAge: number;
}

interface OceanCurrentParticleLayerProps {
  visible: boolean;
}

// 해류 속도 기반 색상
function getCurrentColor(speed: number): string {
  if (speed < 0.2) return 'rgba(59, 130, 246, 0.8)'; // 파랑
  if (speed < 0.4) return 'rgba(6, 182, 212, 0.8)'; // 청록
  if (speed < 0.6) return 'rgba(34, 197, 94, 0.8)'; // 초록
  return 'rgba(249, 115, 22, 0.8)'; // 주황
}

// 한반도 육지 영역 판별 (간략화된 폴리곤)
const isOnLand = (lat: number, lon: number): boolean => {
  const peninsula: [number, number][] = [
    [38.5, 124.5],
    [38.5, 128.3],
    [37.8, 128.8],
    [37.0, 129.2],
    [36.0, 129.5],
    [35.1, 129.2],
    [34.8, 128.6],
    [34.5, 127.8],
    [34.3, 126.5],
    [34.8, 126.1],
    [35.5, 126.0],
    [36.0, 126.3],
    [36.8, 126.0],
    [37.5, 126.2],
    [38.5, 124.5],
  ];

  // 제주도 영역
  if (lat >= 33.1 && lat <= 33.7 && lon >= 126.1 && lon <= 127.0) return true;

  // Ray casting algorithm
  let inside = false;
  for (let i = 0, j = peninsula.length - 1; i < peninsula.length; j = i++) {
    const [yi, xi] = peninsula[i];
    const [yj, xj] = peninsula[j];
    if (yi > lat !== yj > lat && lon < ((xj - xi) * (lat - yi)) / (yj - yi) + xi) {
      inside = !inside;
    }
  }
  return inside;
};

// 한국 해역의 해류 u,v 벡터 데이터 생성 (Mock)
const generateOceanCurrentData = (): CurrentVectorPoint[] => {
  const data: CurrentVectorPoint[] = [];

  for (let lat = 33.5; lat <= 38.0; lat += 0.8) {
    for (let lon = 125.0; lon <= 130.5; lon += 0.8) {
      if (isOnLand(lat, lon)) continue;

      let u = 0;
      let v = 0;

      if (lon > 128.5) {
        // 동해 — 북동진하는 동한난류
        u = 0.2 + Math.random() * 0.2; // 동쪽 0.2~0.4
        v = 0.3 + Math.random() * 0.2; // 북쪽 0.3~0.5
      } else if (lon < 126.5) {
        // 서해 — 북진
        u = -0.05 + Math.random() * 0.1; // 동서 -0.05~0.05
        v = 0.15 + Math.random() * 0.15; // 북쪽 0.15~0.3
      } else {
        // 남해 — 동진
        u = 0.3 + Math.random() * 0.2; // 동쪽 0.3~0.5
        v = -0.05 + Math.random() * 0.15; // 남북 -0.05~0.1
      }

      data.push({ lat, lon, u, v });
    }
  }

  return data;
};

// 해류 데이터는 한 번만 생성
const CURRENT_DATA = generateOceanCurrentData();

// IDW 보간으로 특정 위치의 u,v 벡터 추정 → speed/direction 반환
function interpolateCurrent(
  lat: number,
  lon: number,
  points: CurrentVectorPoint[],
): { speed: number; direction: number } {
  if (points.length === 0) return { speed: 0.3, direction: 90 };

  let totalWeight = 0;
  let weightedU = 0;
  let weightedV = 0;

  for (const point of points) {
    const dist = Math.sqrt(Math.pow(point.lat - lat, 2) + Math.pow(point.lon - lon, 2));
    const weight = 1 / Math.pow(Math.max(dist, 0.01), 2);
    totalWeight += weight;
    weightedU += point.u * weight;
    weightedV += point.v * weight;
  }

  const u = weightedU / totalWeight;
  const v = weightedV / totalWeight;
  const speed = Math.sqrt(u * u + v * v);
  // u=동(+), v=북(+) → 화면 방향: sin=동(+x), -cos=남(+y)
  const direction = (Math.atan2(u, v) * 180) / Math.PI;
  return { speed, direction: (direction + 360) % 360 };
}

// MapLibre map.unproject()를 통해 픽셀 → 경위도 변환
function containerPointToLatLng(
  map: MapLibreMap,
  x: number,
  y: number,
): { lat: number; lng: number } {
  const lngLat = map.unproject([x, y]);
  return { lat: lngLat.lat, lng: lngLat.lng };
}

const PARTICLE_COUNT = 400;
const FADE_ALPHA = 0.93;

/**
 * OceanCurrentParticleLayer
 *
 * Canvas 2D + requestAnimationFrame 패턴으로 해류 흐름 시각화
 * u,v 벡터 격자 데이터를 IDW 보간하여 파티클 애니메이션 렌더링
 * 바람 파티클 대비: 적은 입자, 느린 속도, 긴 트레일
 */
export function OceanCurrentParticleLayer({ visible }: OceanCurrentParticleLayerProps) {
  const { current: mapRef } = useMap();
  const canvasRef = useRef<HTMLCanvasElement | null>(null);
  const particlesRef = useRef<Particle[]>([]);
  const animFrameRef = useRef<number>(0);

  const initParticles = useCallback((width: number, height: number) => {
    particlesRef.current = [];
    for (let i = 0; i < PARTICLE_COUNT; i++) {
      particlesRef.current.push({
        x: Math.random() * width,
        y: Math.random() * height,
        age: Math.floor(Math.random() * 150),
        maxAge: 120 + Math.floor(Math.random() * 60),
      });
    }
  }, []);

  useEffect(() => {
    const map = mapRef?.getMap();
    if (!map) return;

    if (!visible) {
      if (canvasRef.current) {
        canvasRef.current.remove();
        canvasRef.current = null;
      }
      cancelAnimationFrame(animFrameRef.current);
      return;
    }

    const container = map.getContainer();

    // Canvas 생성 또는 재사용
    let canvas = canvasRef.current;
    if (!canvas) {
      canvas = document.createElement('canvas');
      canvas.style.position = 'absolute';
      canvas.style.top = '0';
      canvas.style.left = '0';
      canvas.style.pointerEvents = 'none';
      canvas.style.zIndex = '440';
      container.appendChild(canvas);
      canvasRef.current = canvas;
    }

    const resize = () => {
      if (!canvas) return;
      const { clientWidth: w, clientHeight: h } = container;
      canvas.width = w;
      canvas.height = h;
    };
    resize();

    const ctx = canvas.getContext('2d');
    if (!ctx) return;

    initParticles(canvas.width, canvas.height);

    // 오프스크린 캔버스 (트레일 효과)
    let offCanvas: HTMLCanvasElement | null = null;
    let offCtx: CanvasRenderingContext2D | null = null;

    function animate() {
      if (!ctx || !canvas) return;

      // 오프스크린 캔버스 크기 동기화
      if (!offCanvas || offCanvas.width !== canvas.width || offCanvas.height !== canvas.height) {
        offCanvas = document.createElement('canvas');
        offCanvas.width = canvas.width;
        offCanvas.height = canvas.height;
        offCtx = offCanvas.getContext('2d');
      }

      if (!offCtx) return;

      // 트레일 페이드 효과 (느린 페이드 = 부드러운 흐름)
      offCtx.globalCompositeOperation = 'destination-in';
      offCtx.fillStyle = `rgba(0, 0, 0, ${FADE_ALPHA})`;
      offCtx.fillRect(0, 0, offCanvas.width, offCanvas.height);
      offCtx.globalCompositeOperation = 'source-over';

      // 현재 지도 bounds 확인
      const bounds = map!.getBounds();

      for (const particle of particlesRef.current) {
        particle.age++;

        // 수명 초과 시 리셋
        if (particle.age > particle.maxAge) {
          particle.x = Math.random() * canvas.width;
          particle.y = Math.random() * canvas.height;
          particle.age = 0;
          particle.maxAge = 120 + Math.floor(Math.random() * 60);
          continue;
        }

        const { lat, lng } = containerPointToLatLng(map!, particle.x, particle.y);

        // 화면 밖이면 리셋
        if (!bounds.contains([lng, lat])) {
          particle.x = Math.random() * canvas.width;
          particle.y = Math.random() * canvas.height;
          particle.age = 0;
          continue;
        }

        // 육지 위이면 리셋
        if (isOnLand(lat, lng)) {
          particle.x = Math.random() * canvas.width;
          particle.y = Math.random() * canvas.height;
          particle.age = 0;
          continue;
        }

        const current = interpolateCurrent(lat, lng, CURRENT_DATA);
        const rad = (current.direction * Math.PI) / 180;
        const pixelSpeed = current.speed * 2.0;

        const newX = particle.x + Math.sin(rad) * pixelSpeed;
        const newY = particle.y + -Math.cos(rad) * pixelSpeed;

        // 다음 위치가 육지이면 리셋
        const nextPos = containerPointToLatLng(map!, newX, newY);
        if (isOnLand(nextPos.lat, nextPos.lng)) {
          particle.x = Math.random() * canvas.width;
          particle.y = Math.random() * canvas.height;
          particle.age = 0;
          continue;
        }

        const oldX = particle.x;
        const oldY = particle.y;
        particle.x = newX;
        particle.y = newY;

        // 파티클 트레일 그리기
        const alpha = 1 - particle.age / particle.maxAge;
        offCtx.strokeStyle = getCurrentColor(current.speed).replace('0.8', String(alpha * 0.8));
        offCtx.lineWidth = 0.8;
        offCtx.beginPath();
        offCtx.moveTo(oldX, oldY);
        offCtx.lineTo(particle.x, particle.y);
        offCtx.stroke();
      }

      // 메인 캔버스에 합성 (배경 오버레이 없이 파티클만)
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      ctx.drawImage(offCanvas, 0, 0);

      animFrameRef.current = requestAnimationFrame(animate);
    }

    animate();

    // 지도 이동/줌 시 리셋
    const onMoveEnd = () => {
      resize();
      if (canvas) initParticles(canvas.width, canvas.height);
      if (offCanvas && canvas) {
        offCanvas.width = canvas.width;
        offCanvas.height = canvas.height;
      }
    };
    map.on('moveend', onMoveEnd);
    map.on('zoomend', onMoveEnd);

    return () => {
      cancelAnimationFrame(animFrameRef.current);
      map.off('moveend', onMoveEnd);
      map.off('zoomend', onMoveEnd);
      if (canvasRef.current) {
        canvasRef.current.remove();
        canvasRef.current = null;
      }
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [mapRef, visible, initParticles]);

  return null;
}
