'use client'

import { useEffect, useRef } from 'react';

import { useMapContext } from '../context';

import type { MapEventHandlers, MapMouseEvent, MapViewport } from '../types'

/**
 * Hook for subscribing to map events
 * Automatically cleans up event listeners on unmount
 *
 * Handlers are stored in a ref so callers don't need to memoize the
 * `handlers` object — the underlying map listeners are attached once
 * (per `isLoaded`) and always call the latest handler implementations.
 */
export function useMapEvents(handlers: MapEventHandlers) {
  const { mapRef, setViewport, setHoveredFeature, isLoaded } = useMapContext()

  // Keep latest handlers without forcing re-subscription.
  const handlersRef = useRef(handlers)
  useEffect(() => {
    handlersRef.current = handlers
  }, [handlers])

  useEffect(() => {
    const map = mapRef.current?.getMap()
    if (!map || !isLoaded) return

    const handleClick = (
      event: maplibregl.MapMouseEvent & { features?: GeoJSON.Feature[] }
    ) => {
      const onClick = handlersRef.current.onClick
      if (!onClick) return
      const mapEvent: MapMouseEvent = {
        lngLat: event.lngLat,
        point: event.point,
        features: event.features,
        originalEvent: event.originalEvent,
      }
      onClick(mapEvent)
    }

    const handleMouseMove = (
      event: maplibregl.MapMouseEvent & { features?: GeoJSON.Feature[] }
    ) => {
      const onHover = handlersRef.current.onHover
      if (!onHover) return
      const feature = event.features?.[0] ?? null
      setHoveredFeature(feature)
      const mapEvent: MapMouseEvent = {
        lngLat: event.lngLat,
        point: event.point,
        features: event.features,
        originalEvent: event.originalEvent,
      }
      onHover(mapEvent)
    }

    const handleMoveStart = () => {
      handlersRef.current.onMoveStart?.()
    }

    const handleMoveEnd = () => {
      const center = map.getCenter()
      const newViewport: MapViewport = {
        longitude: center.lng,
        latitude: center.lat,
        zoom: map.getZoom(),
        bearing: map.getBearing(),
        pitch: map.getPitch(),
      }
      setViewport(newViewport)
      handlersRef.current.onMoveEnd?.(newViewport)
    }

    const handleZoomStart = () => {
      handlersRef.current.onZoomStart?.()
    }

    const handleZoomEnd = () => {
      handlersRef.current.onZoomEnd?.(map.getZoom())
    }

    map.on('click', handleClick)
    map.on('mousemove', handleMouseMove)
    map.on('movestart', handleMoveStart)
    map.on('moveend', handleMoveEnd)
    map.on('zoomstart', handleZoomStart)
    map.on('zoomend', handleZoomEnd)

    return () => {
      map.off('click', handleClick)
      map.off('mousemove', handleMouseMove)
      map.off('movestart', handleMoveStart)
      map.off('moveend', handleMoveEnd)
      map.off('zoomstart', handleZoomStart)
      map.off('zoomend', handleZoomEnd)
    }
  }, [mapRef, isLoaded, setViewport, setHoveredFeature])

  // Fire onLoad once the map reports loaded.
  useEffect(() => {
    if (isLoaded) {
      handlersRef.current.onLoad?.()
    }
  }, [isLoaded])
}
