import { useEffect, useMemo, useRef, useState } from 'react'
import { MapContainer, TileLayer, Marker, Popup, useMap } from 'react-leaflet'
import L from 'leaflet'
import { parseCoords, type WikidataLocation } from '../../api/wikidata'

interface Props {
  locations: WikidataLocation[] | null | undefined
}

// Inline SVG icon - default Leaflet markers reference image files we
// don't ship; building our own keeps the bundle clean and matches the
// app's accent color.
const markerIcon = L.divIcon({
  className: 'jf-filming-marker',
  html: `<svg width="22" height="30" viewBox="0 0 22 30" xmlns="http://www.w3.org/2000/svg">
    <path d="M11 0C4.92 0 0 4.7 0 10.5 0 18 11 30 11 30s11-12 11-19.5C22 4.7 17.08 0 11 0z" fill="#F5B642" stroke="#1d1208" stroke-width="1.4"/>
    <circle cx="11" cy="10.5" r="3.5" fill="#1d1208"/>
  </svg>`,
  iconSize: [22, 30],
  iconAnchor: [11, 30],
  popupAnchor: [0, -28],
})

interface Pin {
  loc: WikidataLocation
  lat: number
  lon: number
}

function FitToPins({ pins }: { pins: Pin[] }) {
  const map = useMap()
  useEffect(() => {
    if (pins.length === 0) return
    if (pins.length === 1) {
      map.setView([pins[0].lat, pins[0].lon], 6)
      return
    }
    const bounds = L.latLngBounds(pins.map(p => [p.lat, p.lon] as [number, number]))
    map.fitBounds(bounds, { padding: [40, 40], maxZoom: 8 })
  }, [pins, map])
  return null
}

/**
 * Leaflet map plotting filming locations from Wikidata. Shows a small
 * legend listing locations missing coordinates so users can still see
 * them. Renders nothing when no locations are returned.
 *
 * The map uses CARTO's dark basemap to match the app's overall
 * aesthetic. No API key required.
 */
export default function FilmingLocationsMap({ locations }: Props) {
  const [mounted, setMounted] = useState(false)
  const containerRef = useRef<HTMLDivElement>(null)

  // Defer mount until the user scrolls the map into view. Leaflet creates
  // a lot of DOM and tiles on construction; lazy-mount keeps cold detail
  // page navigation snappy.
  useEffect(() => {
    if (!containerRef.current) return
    const el = containerRef.current
    const obs = new IntersectionObserver(entries => {
      for (const e of entries) {
        if (e.isIntersecting) {
          setMounted(true)
          obs.disconnect()
          return
        }
      }
    }, { rootMargin: '120px' })
    obs.observe(el)
    return () => obs.disconnect()
  }, [])

  const { pins, missing } = useMemo(() => {
    const pinned: Pin[] = []
    const without: WikidataLocation[] = []
    for (const loc of locations || []) {
      const c = parseCoords(loc.coords)
      if (c) pinned.push({ loc, lat: c[0], lon: c[1] })
      else without.push(loc)
    }
    return { pins: pinned, missing: without }
  }, [locations])

  if (!locations || locations.length === 0) return null

  return (
    <div ref={containerRef} className="space-y-3">
      <div className="relative aspect-[16/9] rounded-xl overflow-hidden ring-1 ring-border bg-elevated">
        {mounted && pins.length > 0 ? (
          <MapContainer
            center={[pins[0].lat, pins[0].lon]}
            zoom={4}
            scrollWheelZoom={false}
            className="w-full h-full"
            attributionControl={false}
          >
            <TileLayer
              url="https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png"
              attribution='&copy; OpenStreetMap, &copy; CARTO'
            />
            {pins.map(p => (
              <Marker key={p.loc.id} position={[p.lat, p.lon]} icon={markerIcon}>
                <Popup>
                  <div className="text-[12px]">
                    <p className="font-semibold">{p.loc.label}</p>
                    {p.loc.countryLabel && (
                      <p className="text-[11px] opacity-70">{p.loc.countryLabel}</p>
                    )}
                  </div>
                </Popup>
              </Marker>
            ))}
            <FitToPins pins={pins} />
          </MapContainer>
        ) : (
          <div className="absolute inset-0 grid place-items-center text-text-4 text-[12px]">
            {pins.length === 0 ? 'No mappable coordinates' : 'Loading map...'}
          </div>
        )}
      </div>
      <div className="flex flex-wrap gap-1.5">
        {locations.map(loc => (
          <span
            key={loc.id}
            className="inline-flex items-center gap-1.5 h-6 px-2.5 rounded-full bg-elevated/60 ring-1 ring-border text-[11px] text-text-2 tracking-tight"
            title={loc.countryLabel || ''}
          >
            <svg className="w-2.5 h-2.5 text-accent" viewBox="0 0 12 12" fill="currentColor">
              <path d="M6 0a4 4 0 00-4 4c0 3 4 8 4 8s4-5 4-8a4 4 0 00-4-4zm0 5.5a1.5 1.5 0 110-3 1.5 1.5 0 010 3z" />
            </svg>
            {loc.label}
            {loc.countryLabel && (
              <span className="text-text-4">· {loc.countryLabel}</span>
            )}
          </span>
        ))}
      </div>
      {missing.length > 0 && pins.length > 0 && (
        <p className="text-[10.5px] text-text-5 tracking-tight">
          {missing.length} {missing.length === 1 ? 'location has' : 'locations have'} no mapped coordinates yet.
        </p>
      )}
    </div>
  )
}
