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

interface WindPoint {
  lat: number
  lon: number
  speed: number     // m/s
  direction: number // degrees (0=N, 90=E, 180=S, 270=W)
}

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

interface WindParticleLayerProps {
  visible: boolean
  stations: {
    location: { lat: number; lon: number }
    wind: { speed: number; direction: number }
  }[]
}

// 풍속 기반 색상 (Windy.com 스타일)
function getWindColor(speed: number): string {
  if (speed < 3) return 'rgba(98, 113, 183, 0.8)'
  if (speed < 5) return 'rgba(57, 160, 246, 0.8)'
  if (speed < 7) return 'rgba(80, 213, 145, 0.8)'
  if (speed < 10) return 'rgba(165, 226, 63, 0.8)'
  if (speed < 13) return 'rgba(250, 226, 30, 0.8)'
  if (speed < 16) return 'rgba(250, 170, 25, 0.8)'
  if (speed < 20) return 'rgba(240, 84, 33, 0.8)'
  return 'rgba(180, 30, 70, 0.8)'
}

// 풍속 기반 배경 색상 (반투명 오버레이)
function getWindBgColor(speed: number): string {
  if (speed < 3) return 'rgba(98, 113, 183, 0.12)'
  if (speed < 5) return 'rgba(57, 160, 246, 0.12)'
  if (speed < 7) return 'rgba(80, 213, 145, 0.12)'
  if (speed < 10) return 'rgba(165, 226, 63, 0.12)'
  if (speed < 13) return 'rgba(250, 226, 30, 0.12)'
  if (speed < 16) return 'rgba(250, 170, 25, 0.12)'
  if (speed < 20) return 'rgba(240, 84, 33, 0.12)'
  return 'rgba(180, 30, 70, 0.12)'
}

// 격자 보간으로 특정 위치의 풍속/풍향 추정 (IDW)
function interpolateWind(
  lat: number,
  lon: number,
  windPoints: WindPoint[]
): { speed: number; direction: number } {
  if (windPoints.length === 0) return { speed: 5, direction: 270 }

  let totalWeight = 0
  let weightedSpeed = 0
  let weightedDx = 0
  let weightedDy = 0

  for (const point of windPoints) {
    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
    weightedSpeed += point.speed * weight

    const rad = (point.direction * Math.PI) / 180
    weightedDx += Math.sin(rad) * weight
    weightedDy += -Math.cos(rad) * weight
  }

  const speed = weightedSpeed / totalWeight
  const direction =
    (Math.atan2(weightedDx / totalWeight, -weightedDy / totalWeight) * 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 = 800
const FADE_ALPHA = 0.93

/**
 * WindParticleLayer
 *
 * 기존: Canvas 2D + requestAnimationFrame + map.containerPointToLatLng() (Leaflet)
 * 전환: Canvas 2D + requestAnimationFrame + map.unproject() (MapLibre)
 *
 * @vis.gl/react-maplibre의 useMap()으로 MapLibre Map 인스턴스를 참조
 * Canvas는 MapLibre 컨테이너 위에 absolute 레이어로 삽입
 */
export function WindParticleLayer({ visible, stations }: WindParticleLayerProps) {
  const { current: mapRef } = useMap()
  const canvasRef = useRef<HTMLCanvasElement | null>(null)
  const particlesRef = useRef<Particle[]>([])
  const animFrameRef = useRef<number>(0)

  const windPoints: WindPoint[] = stations.map((s) => ({
    lat: s.location.lat,
    lon: s.location.lon,
    speed: s.wind.speed,
    direction: s.wind.direction,
  }))

  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() * 80),
        maxAge: 60 + Math.floor(Math.random() * 40),
      })
    }
  }, [])

  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 = '450'
      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 drawWindOverlay() {
      if (!ctx || !canvas) return
      const gridSize = 30

      for (let x = 0; x < canvas.width; x += gridSize) {
        for (let y = 0; y < canvas.height; y += gridSize) {
          const { lat, lng } = containerPointToLatLng(map!, x + gridSize / 2, y + gridSize / 2)
          const wind = interpolateWind(lat, lng, windPoints)
          ctx.fillStyle = getWindBgColor(wind.speed)
          ctx.fillRect(x, y, gridSize, gridSize)
        }
      }
    }

    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 = 60 + Math.floor(Math.random() * 40)
          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
        }

        const wind = interpolateWind(lat, lng, windPoints)
        const rad = (wind.direction * Math.PI) / 180
        const pixelSpeed = wind.speed * 0.4

        const oldX = particle.x
        const oldY = particle.y
        particle.x += Math.sin(rad) * pixelSpeed
        particle.y += -Math.cos(rad) * pixelSpeed

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

      // 메인 캔버스에 합성
      ctx.clearRect(0, 0, canvas.width, canvas.height)
      drawWindOverlay()
      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
      }
    }
    // windPoints 배열은 렌더마다 재생성되므로 stations만 의존
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [mapRef, visible, stations, initParticles])

  return null
}
