'use client'

import { useEffect, useRef, useCallback, useState } from 'react'
import type L from 'leaflet'
import 'leaflet/dist/leaflet.css'
import 'leaflet-draw/dist/leaflet.draw.css'
import type { Feature, Polygon } from 'geojson'
import type { LayerType } from './cropZoningTypes'
import type { ZoneMapData, ZoneInitialData } from '@/libs/api/services/cropZoningService'

export type MapDrawGeoJSON = Record<string, unknown> | null

type CropZoningMapProps = {
  center?: [number, number]
  zoom?: number
  height?: string | number
  activeLayer: LayerType
  onAreaChange?: (geojson: MapDrawGeoJSON) => void
  onZoneClick?: (zone: ZoneInitialData) => void
  optimizationKey?: number
  className?: string
  /** منطقهٔ اولیه؛ وقتی مقدار دارد و readOnly باشد نقشه فقط نمایشی است */
  initialAreaGeoJson?: MapDrawGeoJSON | null
  /** دیتای زون‌ها برای نقشه — از APIهای zones/initial یا water-need یا soil-quality یا cultivation-risk */
  zonesData?: ZoneMapData[] | null
  /** لیبل محصولات از API (id -> label) — فقط برای لایهٔ crops */
  productLabels?: Record<string, string>
  /** غیرفعال کردن رسم و ویرایش منطقه توسط کاربر */
  readOnly?: boolean
}


export default function CropZoningMap({
  center = [35.6892, 51.389],
  zoom = 13,
  height = '100%',
  activeLayer,
  onAreaChange,
  onZoneClick,
  optimizationKey = 0,
  className = '',
  initialAreaGeoJson = null,
  zonesData = null,
  productLabels = {},
  readOnly = false
}: CropZoningMapProps) {
  const mapRef = useRef<HTMLDivElement>(null)
  const mapInstanceRef = useRef<L.Map | null>(null)
  const drawnItemsRef = useRef<L.FeatureGroup | null>(null)
  const drawControlRef = useRef<L.Control.Draw | null>(null)
  const zonesLayerRef = useRef<L.GeoJSON | null>(null)
  const [isMapReady, setIsMapReady] = useState(false)

  const renderZonesFromApi = useCallback(
    (map: L.Map, zones: ZoneMapData[]) => {
      if (zonesLayerRef.current) {
        map.removeLayer(zonesLayerRef.current)
        zonesLayerRef.current = null
      }
      if (zones.length === 0) return

      const grid = {
        type: 'FeatureCollection' as const,
        features: zones.map(z => ({
          type: 'Feature' as const,
          geometry: z.geometry,
          properties: {
            zoneId: z.zoneId,
            color: z.color,
            tooltipContent: z.tooltipContent,
            cultivable: z.cultivable,
            zoneInitialData: z.zoneInitialData
          }
        }))
      }

      const L = (window as unknown as { L: typeof import('leaflet') }).L
      if (!L) return

      const geoJsonLayer = L.geoJSON(grid as never, {
        style: (feature?: { properties?: { color?: string } }) => {
          const color = feature?.properties?.color ?? '#94a3b8'
          return {
            fillColor: color,
            fillOpacity: 0.5,
            weight: 1,
            color: '#fff'
          }
        },
        onEachFeature: (feat: unknown, leafLayer: L.Layer) => {
          const f = feat as { geometry?: Polygon; properties?: Record<string, unknown> }
          const props = f?.properties
          const geom = f?.geometry
          if (!props || !geom) return
          const layer = leafLayer as L.Polygon
          const tooltipContent = (props.tooltipContent as string) ?? ''
          const cultivable = props.cultivable === true
          const zoneInitialData = props.zoneInitialData as ZoneInitialData | undefined
          layer.bindTooltip(tooltipContent, {
            sticky: true,
            className: 'zone-tooltip',
            direction: 'top',
            offset: [0, -8]
          })
          layer.on('click', () => cultivable && zoneInitialData && onZoneClick?.(zoneInitialData))
        }
      })

      geoJsonLayer.addTo(map)
      zonesLayerRef.current = geoJsonLayer

      const bounds = geoJsonLayer.getBounds()
      if (bounds.isValid()) {
        map.fitBounds(bounds, { padding: [24, 24] })
      }

      let idx = 0
      geoJsonLayer.eachLayer((layer: L.Layer) => {
        const leafLayer = layer as L.Polygon
        leafLayer.setStyle({ fillOpacity: 0 })
        const index = idx++
        const targetOpacity = 0.5
        const delay = Math.min(index * 30, 600)
        setTimeout(() => {
          leafLayer.setStyle({ fillOpacity: targetOpacity })
        }, delay)
      })
    },
    [onZoneClick]
  )

  useEffect(() => {
    if (typeof window === 'undefined' || !mapRef.current) return

    let isMounted = true
    let cleanupFn: (() => void) | null = null

    const initMap = async () => {
      const L = (await import('leaflet')).default
      await import('leaflet-draw')
      ;(window as unknown as { L: typeof L }).L = L

      if (!isMounted || !mapRef.current || mapInstanceRef.current) return

      const map = L.map(mapRef.current).setView(center, zoom)

      L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        attribution: '© OpenStreetMap'
      }).addTo(map)

      const drawnItems = L.featureGroup().addTo(map)
      drawnItemsRef.current = drawnItems

      if (!readOnly) {
        const drawControl = new L.Control.Draw({
          position: 'topright',
          draw: {
            polygon: { shapeOptions: { color: '#3388ff' } },
            rectangle: { shapeOptions: { color: '#3388ff' } },
            circle: false,
            circlemarker: false,
            marker: false,
            polyline: false
          },
          edit: { featureGroup: drawnItems, remove: true }
        })
        map.addControl(drawControl)
        drawControlRef.current = drawControl
      }

      const getGeoJsonFromDrawn = (): MapDrawGeoJSON => {
        const geojson = drawnItems.toGeoJSON()
        if (geojson.type === 'FeatureCollection' && geojson.features.length > 0) {
          return geojson.features[0] as unknown as MapDrawGeoJSON
        }
        return null
      }

      const emitAreaChange = () => {
        const geojson = getGeoJsonFromDrawn()
        onAreaChange?.(geojson)
        if (!geojson && zonesLayerRef.current) {
          map.removeLayer(zonesLayerRef.current)
          zonesLayerRef.current = null
        }
      }

      if (readOnly && initialAreaGeoJson && initialAreaGeoJson.geometry && (initialAreaGeoJson.geometry as { type: string }).type === 'Polygon') {
        drawnItems.clearLayers()
        L.geoJSON(initialAreaGeoJson as unknown as Feature<Polygon>).eachLayer((layer) => drawnItems.addLayer(layer))
        emitAreaChange()
        const bounds = drawnItems.getBounds()
        if (bounds.isValid()) {
          map.fitBounds(bounds, { padding: [24, 24] })
        }
      }

      const onCreated = (e: L.LeafletEvent) => {
        const event = e as L.DrawEvents.Created
        drawnItems.clearLayers()
        drawnItems.addLayer(event.layer)
        emitAreaChange()
      }

      const onEdited = () => emitAreaChange()
      const onDeleted = () => {
        onAreaChange?.(null)
        if (zonesLayerRef.current) {
          map.removeLayer(zonesLayerRef.current)
          zonesLayerRef.current = null
        }
      }

      if (!readOnly) {
        map.on(L.Draw.Event.CREATED, onCreated)
        map.on(L.Draw.Event.EDITED, onEdited)
        map.on(L.Draw.Event.DELETED, onDeleted)
      }

      mapInstanceRef.current = map
      setIsMapReady(true)

      cleanupFn = () => {
        setIsMapReady(false)
        if (!readOnly) {
          map.off(L.Draw.Event.CREATED, onCreated)
          map.off(L.Draw.Event.EDITED, onEdited)
          map.off(L.Draw.Event.DELETED, onDeleted)
          if (drawControlRef.current) {
            map.removeControl(drawControlRef.current)
          }
        }
        if (zonesLayerRef.current) map.removeLayer(zonesLayerRef.current)
        map.remove()
        mapInstanceRef.current = null
        drawnItemsRef.current = null
        drawControlRef.current = null
        zonesLayerRef.current = null
      }
    }

    initMap()

    return () => {
      isMounted = false
      if (cleanupFn) cleanupFn()
    }
  }, [])

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

    mapInstanceRef.current.invalidateSize()

    if (zonesData && zonesData.length > 0) {
      renderZonesFromApi(mapInstanceRef.current, zonesData)
    } else if (zonesLayerRef.current) {
      mapInstanceRef.current.removeLayer(zonesLayerRef.current)
      zonesLayerRef.current = null
    }
  }, [isMapReady, zonesData, optimizationKey, renderZonesFromApi])


  return (
    <div
      ref={mapRef}
      style={{
        height: typeof height === 'number' ? `${height}px` : height,
        width: '100%',
        borderRadius: 12
      }}
      className={`overflow-hidden ${className}`}
    />
  )
}
