'use client'

import { useCallback, useState, useEffect, useMemo, memo, type ReactNode } from 'react'
import { Source, Layer, useMap, Popup } from 'react-map-gl/maplibre'
import type { GeoJSONSource, MapMouseEvent } from 'maplibre-gl'
import { createClusterLayers } from '../layers'
import type { ClusterLayerOptions } from '../types'

// Inject popup styles once
const POPUP_STYLE_ID = 'map-cluster-popup-styles'
function injectPopupStyles() {
  if (typeof document === 'undefined') return
  if (document.getElementById(POPUP_STYLE_ID)) return

  const style = document.createElement('style')
  style.id = POPUP_STYLE_ID
  style.textContent = `
    .maplibregl-popup.map-popup-clean .maplibregl-popup-content {
      padding: 0 !important;
      background: transparent !important;
      box-shadow: none !important;
      border-radius: 0 !important;
    }
    .maplibregl-popup.map-popup-clean .maplibregl-popup-tip {
      display: none !important;
    }
  `
  document.head.appendChild(style)
}

export interface MapClusterProps {
  sourceId: string
  data: GeoJSON.FeatureCollection
  clusterRadius?: number
  clusterMaxZoom?: number
  onClusterClick?: (clusterId: number, coordinates: [number, number]) => void
  onPointClick?: (feature: GeoJSON.Feature) => void
  /** Render prop for popup content - handles popup state automatically */
  renderPopup?: (feature: GeoJSON.Feature, onClose: () => void) => ReactNode
  /** Popup anchor position */
  popupAnchor?: 'top' | 'bottom' | 'left' | 'right'
  /** Popup offset */
  popupOffset?: number | [number, number]
  /** Pan offset X when clicking point (pixels). Positive = point shifts right. */
  panOffsetX?: number
  /** Pan offset Y when clicking point (pixels). Positive = point shifts down. */
  panOffsetY?: number
  colors?: [string, string, string]
  radii?: [number, number, number]
  thresholds?: [number, number]
  /** Color when marker/cluster is hovered */
  hoverColor?: string
}

export const MapCluster = memo(function MapCluster({
  sourceId,
  data,
  clusterRadius = 50,
  clusterMaxZoom = 14,
  onClusterClick,
  onPointClick,
  renderPopup,
  popupAnchor = 'bottom',
  popupOffset = 15,
  panOffsetX = 0,
  panOffsetY = 150,
  colors,
  radii,
  thresholds,
  hoverColor,
}: MapClusterProps) {
  const { current: map } = useMap()
  const [selectedFeature, setSelectedFeature] = useState<GeoJSON.Feature | null>(null)
  const [popupCoords, setPopupCoords] = useState<[number, number] | null>(null)

  // Inject popup styles on mount
  useEffect(() => {
    injectPopupStyles()
  }, [])

  // Memoize layers so layer ids stay referentially stable across renders.
  // Without this, the click/hover effect below re-subscribes on every render.
  const { cluster, clusterCount, unclusteredPoint } = useMemo(() => {
    const layerOptions: ClusterLayerOptions = {
      sourceId,
      colors,
      radii,
      thresholds,
      hoverColor,
    }
    return createClusterLayers(layerOptions)
  }, [sourceId, colors, radii, thresholds, hoverColor])

  const clusterLayerId = cluster.id as string
  const pointLayerId = unclusteredPoint.id as string

  const handleClosePopup = useCallback(() => {
    setSelectedFeature(null)
    setPopupCoords(null)
  }, [])

  const handleClick = useCallback(
    async (event: MapMouseEvent) => {
      if (!map) return

      const features = map.queryRenderedFeatures(event.point, {
        layers: [clusterLayerId, pointLayerId],
      })

      if (!features || features.length === 0) return

      const feature = features[0]
      const geometry = feature.geometry

      if (geometry.type !== 'Point') return

      const coordinates = geometry.coordinates as [number, number]
      const clusterId = feature.properties?.cluster_id

      if (clusterId) {
        const source = map.getSource(sourceId) as GeoJSONSource
        if (!source) return

        try {
          const zoom = await source.getClusterExpansionZoom(clusterId)
          map.easeTo({
            center: coordinates,
            zoom,
            duration: 500,
          })
          onClusterClick?.(clusterId, coordinates)
        } catch (error) {
          console.error('Error expanding cluster:', error)
        }
      } else {
        // Handle point click
        onPointClick?.(feature)

        // If renderPopup is provided, show popup and pan to point
        if (renderPopup) {
          // Pan map to center on point with offset for popup visibility
          map.easeTo({
            center: coordinates,
            duration: 300,
            offset: [panOffsetX, panOffsetY],
          })
          setSelectedFeature(feature)
          setPopupCoords(coordinates)
        }
      }
    },
    [map, sourceId, clusterLayerId, pointLayerId, onClusterClick, onPointClick, renderPopup, panOffsetX, panOffsetY]
  )

  // Close popup when clicking empty area
  const handleMapClick = useCallback(
    (event: MapMouseEvent) => {
      if (!map) return

      const features = map.queryRenderedFeatures(event.point, {
        layers: [clusterLayerId, pointLayerId],
      })

      // Close popup if clicked on empty area (no features)
      if (!features || features.length === 0) {
        handleClosePopup()
      }
    },
    [map, clusterLayerId, pointLayerId, handleClosePopup]
  )

  // Register click and hover handlers with proper cleanup
  useEffect(() => {
    if (!map) return

    let currentHoveredId: number | string | null = null

    // Helper to clear hover state.
    // Guarded: on unmount the source may already be gone, which would throw.
    const clearHoverState = () => {
      if (currentHoveredId !== null) {
        if (map.getSource(sourceId)) {
          map.setFeatureState(
            { source: sourceId, id: currentHoveredId },
            { hover: false }
          )
        }
        currentHoveredId = null
      }
    }

    // Hover handler for both clusters and points
    const handleMouseMove = (e: MapMouseEvent) => {
      const features = map.queryRenderedFeatures(e.point, {
        layers: [clusterLayerId, pointLayerId],
      })

      if (features.length > 0) {
        map.getCanvas().style.cursor = 'pointer'
        const feature = features[0]
        const featureId = feature.id

        if (featureId !== undefined && featureId !== currentHoveredId) {
          clearHoverState()
          currentHoveredId = featureId
          map.setFeatureState(
            { source: sourceId, id: featureId },
            { hover: true }
          )
        }
      } else {
        map.getCanvas().style.cursor = ''
        clearHoverState()
      }
    }

    const handleMouseLeave = () => {
      map.getCanvas().style.cursor = ''
      clearHoverState()
    }

    map.on('click', clusterLayerId, handleClick)
    map.on('click', pointLayerId, handleClick)
    map.on('click', handleMapClick)
    map.on('mousemove', clusterLayerId, handleMouseMove)
    map.on('mousemove', pointLayerId, handleMouseMove)
    map.on('mouseleave', clusterLayerId, handleMouseLeave)
    map.on('mouseleave', pointLayerId, handleMouseLeave)

    // Cleanup on unmount
    return () => {
      clearHoverState()
      map.off('click', clusterLayerId, handleClick)
      map.off('click', pointLayerId, handleClick)
      map.off('click', handleMapClick)
      map.off('mousemove', clusterLayerId, handleMouseMove)
      map.off('mousemove', pointLayerId, handleMouseMove)
      map.off('mouseleave', clusterLayerId, handleMouseLeave)
      map.off('mouseleave', pointLayerId, handleMouseLeave)
    }
  }, [map, sourceId, clusterLayerId, pointLayerId, handleClick, handleMapClick])

  return (
    <>
      <Source
        id={sourceId}
        type="geojson"
        data={data}
        cluster={true}
        clusterMaxZoom={clusterMaxZoom}
        clusterRadius={clusterRadius}
        generateId={true}
      >
        <Layer {...cluster} />
        <Layer {...clusterCount} />
        <Layer {...unclusteredPoint} />
      </Source>

      {/* Popup rendered via render prop */}
      {renderPopup && selectedFeature && popupCoords && (
        <Popup
          longitude={popupCoords[0]}
          latitude={popupCoords[1]}
          anchor={popupAnchor}
          onClose={handleClosePopup}
          closeOnClick={false}
          closeButton={false}
          offset={popupOffset}
          maxWidth="none"
          className="map-popup-clean"
        >
          {renderPopup(selectedFeature, handleClosePopup)}
        </Popup>
      )}
    </>
  )
})
