import { useEffect, useRef } from 'react'
import L from 'leaflet'
import { sportColor } from '../../utils/format'

delete L.Icon.Default.prototype._getIconUrl
L.Icon.Default.mergeOptions({
  iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png',
  iconRetinaUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png',
  shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
})

const TILE_LAYERS = {
  dark: {
    url: 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',
    attribution: '© <a href="https://www.openstreetmap.org/copyright">OSM</a> © <a href="https://carto.com/">CARTO</a>',
  },
  street: {
    url: 'https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png',
    attribution: '© <a href="https://www.openstreetmap.org/copyright">OSM</a> © <a href="https://carto.com/">CARTO</a>',
  },
  satellite: {
    url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
    attribution: '© <a href="https://www.esri.com/">Esri</a>',
  },
}

function decodePolyline(encoded) {
  const coords = []
  let index = 0, lat = 0, lng = 0
  while (index < encoded.length) {
    let b, shift = 0, result = 0
    do { b = encoded.charCodeAt(index++) - 63; result |= (b & 0x1f) << shift; shift += 5 } while (b >= 0x20)
    lat += (result & 1) ? ~(result >> 1) : result >> 1
    shift = 0; result = 0
    do { b = encoded.charCodeAt(index++) - 63; result |= (b & 0x1f) << shift; shift += 5 } while (b >= 0x20)
    lng += (result & 1) ? ~(result >> 1) : result >> 1
    coords.push([lat / 1e5, lng / 1e5])
  }
  return coords
}

function drawRoute(map, polyline, sportType, trackRef) {
  if (trackRef.current) {
    trackRef.current.remove()
    trackRef.current = null
  }
  if (!polyline) return

  const coords = decodePolyline(polyline)
  if (!coords.length) return

  trackRef.current = L.polyline(coords, {
    color: sportColor(sportType),
    weight: 3,
    opacity: 0.9,
  }).addTo(map)

  map.fitBounds(trackRef.current.getBounds(), { padding: [20, 20] })

  const dot = (color) => L.divIcon({
    html: `<div style="width:12px;height:12px;background:${color};border:2px solid white;border-radius:50%"></div>`,
    iconSize: [12, 12], iconAnchor: [6, 6], className: '',
  })
  L.marker(coords[0], { icon: dot('#22c55e') }).addTo(map)
  L.marker(coords[coords.length - 1], { icon: dot('#ef4444') }).addTo(map)
}

export default function ActivityMap({ polyline, dataPoints, hoveredDistance, sportType, mapType = 'dark' }) {
  const mapRef = useRef(null)
  const mapInstanceRef = useRef(null)
  const markerRef = useRef(null)
  const trackRef = useRef(null)
  const tileLayerRef = useRef(null)
  const polylineRef = useRef(polyline)
  const sportTypeRef = useRef(sportType)

  useEffect(() => { polylineRef.current = polyline }, [polyline])
  useEffect(() => { sportTypeRef.current = sportType }, [sportType])

  useEffect(() => {
    if (!mapRef.current || mapInstanceRef.current) return

    mapInstanceRef.current = L.map(mapRef.current, {
      zoomControl: true,
      attributionControl: true,
    })

    const tile = TILE_LAYERS.dark
    tileLayerRef.current = L.tileLayer(tile.url, {
      attribution: tile.attribution,
      maxZoom: 19,
    }).addTo(mapInstanceRef.current)

    return () => {
      mapInstanceRef.current?.remove()
      mapInstanceRef.current = null
    }
  }, [])

  useEffect(() => {
    if (!mapInstanceRef.current) return
    const tile = TILE_LAYERS[mapType] || TILE_LAYERS.dark
    if (tileLayerRef.current) tileLayerRef.current.remove()
    tileLayerRef.current = L.tileLayer(tile.url, {
      attribution: tile.attribution,
      maxZoom: 19,
    }).addTo(mapInstanceRef.current)
    drawRoute(mapInstanceRef.current, polylineRef.current, sportTypeRef.current, trackRef)
  }, [mapType])

  useEffect(() => {
    if (!mapInstanceRef.current) return
    drawRoute(mapInstanceRef.current, polyline, sportType, trackRef)
  }, [polyline, sportType])

  useEffect(() => {
    if (!mapInstanceRef.current || !dataPoints || hoveredDistance == null) return
    const point = dataPoints.find(p => p.distance_m >= hoveredDistance)
    if (!point?.latitude || !point?.longitude) return
    if (markerRef.current) {
      markerRef.current.setLatLng([point.latitude, point.longitude])
    } else {
      const icon = L.divIcon({
        html: '<div style="width:14px;height:14px;background:#fff;border:3px solid #3b82f6;border-radius:50%;box-shadow:0 0 6px rgba(59,130,246,0.8)"></div>',
        iconSize: [14, 14], iconAnchor: [7, 7], className: '',
      })
      markerRef.current = L.marker([point.latitude, point.longitude], { icon })
        .addTo(mapInstanceRef.current)
    }
  }, [hoveredDistance, dataPoints])

  return <div ref={mapRef} style={{ height: '100%', width: '100%', background: '#1a1a2e' }} />
}