/**
 * WorldMap — Aceternity-style dotted world map with animated arc routes.
 *
 * DottedMap (height=60, grid='diagonal') emits viewBox="0 0 119 60".
 * We project lat/lng into that same coordinate space so the arcs sit
 * exactly on the map dots.
 */
import { useRef, useMemo, memo } from 'react'
import { motion } from 'framer-motion'
import DottedMap from 'dotted-map'

/* ─── Public types ───────────────────────────────────── */
export interface MapDot {
  start: { lat: number; lng: number; label?: string }
  end:   { lat: number; lng: number; label?: string }
}

interface WorldMapProps {
  dots?:      MapDot[]
  lineColor?: string
  dotColor?:  string
}

/* ─── Map geometry constants ─────────────────────────── */
const VB_W = 119   // DottedMap viewBox width  (height=60 → width=119)
const VB_H = 60    // DottedMap viewBox height

/* Equirectangular projection matching DottedMap's output */
function project(lat: number, lng: number) {
  const x = (lng + 180) / 360 * VB_W
  const y = (90 - lat)  / 180 * VB_H
  return { x, y }
}

/* Quadratic bezier — control point lifted proportional to distance */
function arcPath(
  s: { x: number; y: number },
  e: { x: number; y: number },
) {
  const dx = e.x - s.x
  const dy = e.y - s.y
  const dist = Math.sqrt(dx * dx + dy * dy)
  const mx = (s.x + e.x) / 2
  const my = (s.y + e.y) / 2 - dist * 0.35  // lift proportional to length
  return `M ${s.x} ${s.y} Q ${mx} ${my} ${e.x} ${e.y}`
}

/* ─── Memoised base map (expensive, only computed once) ─ */
const BaseMap = memo(function BaseMap({ dotColor }: { dotColor: string }) {
  const map = new DottedMap({ height: 60, grid: 'diagonal' })
  const svg = map.getSVG({
    color:           dotColor,
    radius:          0.22,
    backgroundColor: 'transparent',
  })
  return (
    <div
      className="absolute inset-0 w-full h-full"
      /* eslint-disable-next-line react/no-danger */
      dangerouslySetInnerHTML={{ __html: svg }}
    />
  )
})

/* ─── Component ──────────────────────────────────────── */
export default function WorldMap({
  dots      = [],
  lineColor = '#9b1c1c',   // var(--red)
  dotColor  = '#d1cdc7',   // muted cream-grey
}: WorldMapProps) {
  const svgRef = useRef<SVGSVGElement>(null)

  /* Pre-compute projected coordinates */
  const projected = useMemo(
    () =>
      dots.map(d => ({
        start: project(d.start.lat, d.start.lng),
        end:   project(d.end.lat,   d.end.lng),
        startLabel: d.start.label,
        endLabel:   d.end.label,
      })),
    [dots],
  )

  return (
    /* aspect-ratio 119:60 ≈ 2:1 keeps the map undistorted */
    <div className="relative w-full" style={{ aspectRatio: `${VB_W} / ${VB_H}` }}>
      {/* Dotted world map base */}
      <BaseMap dotColor={dotColor} />

      {/* Arc + dot overlay — same viewBox as DottedMap */}
      <svg
        ref={svgRef}
        viewBox={`0 0 ${VB_W} ${VB_H}`}
        className="absolute inset-0 w-full h-full pointer-events-none"
        xmlns="http://www.w3.org/2000/svg"
        preserveAspectRatio="xMidYMid meet"
      >
        <defs>
          {projected.map((_, i) => (
            <linearGradient
              key={`g${i}`}
              id={`grd${i}`}
              x1="0%" y1="0%" x2="100%" y2="0%"
              gradientUnits="userSpaceOnUse"
            >
              <stop offset="0%"   stopColor={lineColor} stopOpacity="0" />
              <stop offset="40%"  stopColor={lineColor} stopOpacity="0.85" />
              <stop offset="60%"  stopColor={lineColor} stopOpacity="0.85" />
              <stop offset="100%" stopColor={lineColor} stopOpacity="0" />
            </linearGradient>
          ))}
        </defs>

        {projected.map((p, i) => {
          const d = arcPath(p.start, p.end)
          return (
            <g key={i}>
              {/* Ghost — very faint, instant, gives the "track" feel */}
              <path
                d={d}
                fill="none"
                stroke={lineColor}
                strokeWidth={0.25}
                strokeOpacity={0.12}
              />

              {/* Animated arc */}
              <motion.path
                d={d}
                fill="none"
                stroke={`url(#grd${i})`}
                strokeWidth={0.5}
                strokeLinecap="round"
                initial={{ pathLength: 0, opacity: 0 }}
                animate={{ pathLength: 1, opacity: 1 }}
                transition={{
                  pathLength: {
                    duration:  1.6,
                    delay:     i * 0.3,
                    ease:      'easeInOut',
                    repeat:    Infinity,
                    repeatDelay: 2.5,
                    repeatType: 'loop',
                  },
                  opacity: { duration: 0.2, delay: i * 0.3 },
                }}
              />

              {/* Start endpoint */}
              <PulsingDot cx={p.start.x} cy={p.start.y} color={lineColor} delay={i * 0.3} />
              {/* End endpoint */}
              <PulsingDot cx={p.end.x}   cy={p.end.y}   color={lineColor} delay={i * 0.3 + 0.8} />
            </g>
          )
        })}
      </svg>
    </div>
  )
}

/* ─── Pulsing dot ────────────────────────────────────── */
function PulsingDot({
  cx, cy, color, delay,
}: {
  cx: number; cy: number; color: string; delay: number
}) {
  return (
    <g>
      {/* Outer ring — pulses outward */}
      <motion.circle
        cx={cx} cy={cy} r={0.5}
        fill="none"
        stroke={color}
        strokeWidth={0.3}
        initial={{ scale: 1, opacity: 0.8 }}
        animate={{ scale: 3.5, opacity: 0 }}
        transition={{
          duration: 2,
          repeat: Infinity,
          delay,
          ease: 'easeOut',
        }}
        style={{ transformOrigin: `${cx}px ${cy}px` }}
      />
      {/* Solid centre */}
      <circle cx={cx} cy={cy} r={0.6} fill={color} opacity={0.9} />
      <circle cx={cx} cy={cy} r={0.3} fill="white"  opacity={0.6} />
    </g>
  )
}
