"use client"

import { useState, useRef, useEffect, useCallback } from "react"
import { useMap } from "react-map-gl/mapbox"
import { BASE_BEARING, BASE_PITCH, BASE_ZOOM, MAPBOX_TILESET_ID } from "@/app/_utils/const/map"
import DistrictPopup from "../pop-up/district-popup"
import DistrictExtrusionLayer from "./district-extrusion-layer"
import ClusterLayer from "./cluster-layer"
import HeatmapLayer from "./heatmap-layer"
import TimelineLayer from "./timeline-layer"

import type { ICrimes } from "@/app/_utils/types/crimes"
import type { IDistrictFeature } from "@/app/_utils/types/map"
import { createFillColorExpression, processCrimeDataByDistrict } from "@/app/_utils/map"
import UnclusteredPointLayer from "./uncluster-layer"

import { toast } from "sonner"
import type { ITooltips } from "../controls/top/tooltips"
import type { IUnits } from "@/app/_utils/types/units"
import UnitsLayer from "./units-layer"
import DistrictFillLineLayer from "./district-layer"
import CrimePopup from "../pop-up/crime-popup"

// Interface for crime incident
interface ICrimeIncident {
    id: string
    district?: string
    category?: string
    type_category?: string | null
    description?: string
    status: string
    address?: string | null
    timestamp?: Date
    latitude?: number
    longitude?: number
}

// District layer props
export interface IDistrictLayerProps {
    visible?: boolean
    onClick?: (feature: IDistrictFeature) => void
    onDistrictClick?: (feature: IDistrictFeature) => void
    map?: any
    year: string
    month: string
    filterCategory: string | "all"
    crimes: ICrimes[]
    units?: IUnits[]
    tilesetId?: string
    focusedDistrictId?: string | null
    setFocusedDistrictId?: (id: string | null) => void
    crimeDataByDistrict?: Record<string, any>
    showFill?: boolean
    activeControl?: ITooltips
}

interface LayersProps {
    visible?: boolean
    crimes: ICrimes[]
    units?: IUnits[]
    year: string
    month: string
    filterCategory: string | "all"
    activeControl: ITooltips
    tilesetId?: string
    useAllData?: boolean
}

export default function Layers({
    visible = true,
    crimes,
    units,
    year,
    month,
    filterCategory,
    activeControl,
    tilesetId = MAPBOX_TILESET_ID,
    useAllData = false,
}: LayersProps) {
    const { current: map } = useMap()

    if (!map) {
        toast.error("Map not found")
        return null
    }

    const mapboxMap = map.getMap()

    const [selectedDistrict, setSelectedDistrict] = useState<IDistrictFeature | null>(null)
    const [selectedIncident, setSelectedIncident] = useState<ICrimeIncident | null>(null)
    const [focusedDistrictId, setFocusedDistrictId] = useState<string | null>(null)
    const selectedDistrictRef = useRef<IDistrictFeature | null>(null)

    const crimeDataByDistrict = processCrimeDataByDistrict(crimes)

    // Handle popup close with a common reset pattern
    const handlePopupClose = useCallback(() => {
        // Reset selected state
        selectedDistrictRef.current = null
        setSelectedDistrict(null)
      setSelectedIncident(null)
      setFocusedDistrictId(null)

      // Reset map view/camera
      if (map) {
          map.easeTo({
              zoom: BASE_ZOOM,
              pitch: BASE_PITCH,
              bearing: BASE_BEARING,
              duration: 1500,
              easing: (t) => t * (2 - t), // easeOutQuad
          })

        // Show all clusters again when closing popup
        if (map.getLayer("clusters")) {
            map.getMap().setLayoutProperty("clusters", "visibility", "visible")
        }
        if (map.getLayer("unclustered-point")) {
            map.getMap().setLayoutProperty("unclustered-point", "visibility", "visible")
        }

        // Update fill color for all districts
        if (map.getLayer("district-fill")) {
            const fillColorExpression = createFillColorExpression(null, crimeDataByDistrict)
            map.getMap().setPaintProperty("district-fill", "fill-color", fillColorExpression as any)
        }
    }
  }, [map, crimeDataByDistrict])

    // Handle district popup close specifically
    const handleCloseDistrictPopup = useCallback(() => {
        console.log("Closing district popup")
        handlePopupClose()
    }, [handlePopupClose])

    // Handle incident popup close specifically
    const handleCloseIncidentPopup = useCallback(() => {
        console.log("Closing incident popup")
        handlePopupClose()
    }, [handlePopupClose])

    // Handle district clicks
    const handleDistrictClick = useCallback(
        (feature: IDistrictFeature) => {
            console.log("District clicked:", feature)

            // Clear any incident selection when showing a district
            setSelectedIncident(null)

            // Set the selected district
            setSelectedDistrict(feature)
            selectedDistrictRef.current = feature
            setFocusedDistrictId(feature.id)

            // Fly to the district
            if (map && feature.longitude && feature.latitude) {
                map.flyTo({
                    center: [feature.longitude, feature.latitude],
                    zoom: 12,
                    pitch: 45,
                    bearing: 0,
                    duration: 1500,
                    easing: (t) => t * (2 - t),
                })

                // Hide clusters when focusing on district
                if (map.getLayer("clusters")) {
                    map.getMap().setLayoutProperty("clusters", "visibility", "none")
                }
                if (map.getLayer("unclustered-point")) {
                    map.getMap().setLayoutProperty("unclustered-point", "visibility", "none")
        }
          }
      },
      [map],
  )

    // Set up custom event handler for fly-to events
    useEffect(() => {
        if (!mapboxMap) return

        const handleFlyToEvent = (e: Event) => {
            const customEvent = e as CustomEvent
            if (!map || !customEvent.detail) return

            const { longitude, latitude, zoom, bearing, pitch, duration } = customEvent.detail

            map.flyTo({
                center: [longitude, latitude],
                zoom: zoom || 15,
                bearing: bearing || 0,
                pitch: pitch || 45,
                duration: duration || 2000,
            })
        }

        mapboxMap.getCanvas().addEventListener("mapbox_fly_to", handleFlyToEvent as EventListener)

        return () => {
            if (mapboxMap && mapboxMap.getCanvas()) {
                mapboxMap.getCanvas().removeEventListener("mapbox_fly_to", handleFlyToEvent as EventListener)
            }
        }
    }, [mapboxMap, map])

    // Handle incident click events
    useEffect(() => {
        if (!mapboxMap) return

        const handleIncidentClickEvent = (e: Event) => {
            const customEvent = e as CustomEvent
            console.log("Received incident_click event in layers:", customEvent.detail)

            // Enhanced error checking
            if (!customEvent.detail) {
                console.error("Empty incident click event data")
                return
            }

            // Allow for different property names in the event data
            const incidentId = customEvent.detail.id || customEvent.detail.incidentId || customEvent.detail.incident_id

            if (!incidentId) {
                console.error("No incident ID found in event data:", customEvent.detail)
                return
            }

            console.log("Looking for incident with ID:", incidentId)

            // Improved incident finding
            let foundIncident: ICrimeIncident | undefined

            // First try to use the data directly from the event if it has all needed properties
            if (
                customEvent.detail.latitude !== undefined &&
                customEvent.detail.longitude !== undefined &&
                customEvent.detail.category !== undefined
            ) {
                foundIncident = {
                    id: incidentId,
                    district: customEvent.detail.district,
                    category: customEvent.detail.category,
                    type_category: customEvent.detail.type,
                    description: customEvent.detail.description,
                    status: customEvent.detail.status || "Unknown",
                    timestamp: customEvent.detail.timestamp ? new Date(customEvent.detail.timestamp) : undefined,
                    latitude: customEvent.detail.latitude,
                    longitude: customEvent.detail.longitude,
                    address: customEvent.detail.address,
                }
            } else {
                // Otherwise search through the crimes data
                for (const crime of crimes) {
                    for (const incident of crime.crime_incidents) {
                        if (incident.id === incidentId || incident.id?.toString() === incidentId?.toString()) {
                            console.log("Found matching incident:", incident)
                            foundIncident = {
                                id: incident.id,
                                district: crime.districts.name,
                                description: incident.description,
                                status: incident.status || "unknown",
                                timestamp: incident.timestamp,
                                category: incident.crime_categories.name,
                                type_category: incident.crime_categories.type,
                                address: incident.locations.address,
                                latitude: incident.locations.latitude,
                                longitude: incident.locations.longitude,
                            }
                            break
                        }
                    }
                    if (foundIncident) break
                }
            }

            if (!foundIncident) {
                console.error("Could not find incident with ID:", incidentId)
                return
            }

            if (!foundIncident.latitude || !foundIncident.longitude) {
                console.error("Found incident has invalid coordinates:", foundIncident)
                return
            }

            console.log("Setting selected incident:", foundIncident)

            // Clear any existing district selection first
            setSelectedDistrict(null)
            selectedDistrictRef.current = null
            setFocusedDistrictId(null)

            // Set the selected incident
            setSelectedIncident(foundIncident)
        }

        // Add event listeners to both the map canvas and document
        mapboxMap.getCanvas().addEventListener("incident_click", handleIncidentClickEvent as EventListener)
        document.addEventListener("incident_click", handleIncidentClickEvent as EventListener)

        // For debugging purposes, log when this effect runs
        console.log("Set up incident click event listener")

        return () => {
            console.log("Removing incident click event listener")
            if (mapboxMap && mapboxMap.getCanvas()) {
                mapboxMap.getCanvas().removeEventListener("incident_click", handleIncidentClickEvent as EventListener)
            }
            document.removeEventListener("incident_click", handleIncidentClickEvent as EventListener)
        }
    }, [mapboxMap, crimes, setFocusedDistrictId])

    // Update selected district when year/month/filter changes
    useEffect(() => {
        if (selectedDistrictRef.current) {
            const districtId = selectedDistrictRef.current.id
            const districtCrime = crimes.find((crime) => crime.district_id === districtId)

        if (districtCrime) {
            const selectedYearNum = year ? Number.parseInt(year) : new Date().getFullYear()

          let demographics = districtCrime.districts.demographics?.find((d) => d.year === selectedYearNum)

          if (!demographics && districtCrime.districts.demographics?.length) {
              demographics = districtCrime.districts.demographics.sort((a, b) => b.year - a.year)[0]
          }

          let geographics = districtCrime.districts.geographics?.find((g) => g.year === selectedYearNum)

          if (!geographics && districtCrime.districts.geographics?.length) {
              const validGeographics = districtCrime.districts.geographics
                  .filter((g) => g.year !== null)
                  .sort((a, b) => (b.year || 0) - (a.year || 0))

            geographics = validGeographics.length > 0 ? validGeographics[0] : districtCrime.districts.geographics[0]
        }

          if (!demographics || !geographics) {
              console.error("Missing district data:", { demographics, geographics })
              return
          }

          const crime_incidents = districtCrime.crime_incidents
              .filter((incident) => filterCategory === "all" || incident.crime_categories.name === filterCategory)
              .map((incident) => ({
                  id: incident.id,
                  timestamp: incident.timestamp,
                  description: incident.description,
                  status: incident.status || "",
                  category: incident.crime_categories.name,
                  type: incident.crime_categories.type || "",
                  address: incident.locations.address || "",
                  latitude: incident.locations.latitude,
                  longitude: incident.locations.longitude,
              }))

          const updatedDistrict: IDistrictFeature = {
              ...selectedDistrictRef.current,
              number_of_crime: crimeDataByDistrict[districtId]?.number_of_crime || 0,
              level: crimeDataByDistrict[districtId]?.level || selectedDistrictRef.current.level,
              demographics: {
                  number_of_unemployed: demographics.number_of_unemployed,
                  population: demographics.population,
                  population_density: demographics.population_density,
                  year: demographics.year,
              },
              geographics: {
                  address: geographics.address || "",
                  land_area: geographics.land_area || 0,
                  year: geographics.year || 0,
                  latitude: geographics.latitude,
                  longitude: geographics.longitude,
              },
              crime_incidents,
              selectedYear: year,
              selectedMonth: month,
          }

          selectedDistrictRef.current = updatedDistrict

              setSelectedDistrict((prevDistrict) => {
                  if (
                      prevDistrict?.id === updatedDistrict.id &&
                      prevDistrict?.selectedYear === updatedDistrict.selectedYear &&
                      prevDistrict?.selectedMonth === updatedDistrict.selectedMonth
                  ) {
                      return prevDistrict
                  }
                  return updatedDistrict
              })
          }
      }
  }, [crimes, filterCategory, year, month, crimeDataByDistrict])

    // Make sure we have a defined handler for setFocusedDistrictId
    const handleSetFocusedDistrictId = useCallback((id: string | null, isMarkerClick = false) => {
        console.log("Setting focused district ID:", id, "from marker click:", isMarkerClick)
        setFocusedDistrictId(id)
    }, [])

    if (!visible) return null

    // Determine which layers should be visible based on the active control
    const showDistrictLayer = activeControl === "incidents"
    const showHeatmapLayer = activeControl === "heatmap"
    const showClustersLayer = activeControl === "clusters"
    const showUnitsLayer = activeControl === "units"
    const showTimelineLayer = activeControl === "timeline"

    // District fill should only be visible for incidents and clusters
    const showDistrictFill = activeControl === "incidents" || activeControl === "clusters"

    // Show incident markers for incidents, clusters, AND units modes
    // But hide for heatmap and timeline
    const showIncidentMarkers = activeControl !== "heatmap" && activeControl !== "timeline"

    return (
        <>
            {/* Standard District Layer with incident points */}
            <DistrictFillLineLayer
                visible={true}
                map={mapboxMap}
                year={year}
                month={month}
                filterCategory={filterCategory}
                crimes={crimes}
                tilesetId={tilesetId}
                focusedDistrictId={focusedDistrictId}
                setFocusedDistrictId={handleSetFocusedDistrictId}
                crimeDataByDistrict={crimeDataByDistrict}
                showFill={showDistrictFill}
                activeControl={activeControl}
              onDistrictClick={handleDistrictClick} // Add this prop to pass the click handler
          />

          {/* Heatmap Layer */}
          <HeatmapLayer
              crimes={crimes}
              year={year}
              month={month}
              filterCategory={filterCategory}
              visible={showHeatmapLayer}
              useAllData={useAllData}
              enableInteractions={true}
              setFocusedDistrictId={handleSetFocusedDistrictId}
          />

          {/* Timeline Layer - make sure this is the only visible layer in timeline mode */}
          <TimelineLayer
              crimes={crimes}
              year={year}
              month={month}
              filterCategory={filterCategory}
              visible={showTimelineLayer}
              map={mapboxMap}
              useAllData={useAllData}
          />

          {/* Units Layer - always show incidents when Units is active */}
          <UnitsLayer
              crimes={crimes}
              units={units}
              filterCategory={filterCategory}
              visible={showUnitsLayer}
              map={mapboxMap}
          />

          {/* Cluster Layer - only enable when the clusters control is active and NOT in timeline mode */}
          <ClusterLayer
              visible={visible && activeControl === "clusters"}
              map={mapboxMap}
              crimes={crimes}
              filterCategory={filterCategory}
              focusedDistrictId={focusedDistrictId}
              clusteringEnabled={activeControl === "clusters"}
              showClusters={activeControl === "clusters"}
          />

          {/* Unclustered Points Layer - now show for both incidents and units modes */}
          <UnclusteredPointLayer
              visible={visible && showIncidentMarkers && !focusedDistrictId}
              map={mapboxMap}
              crimes={crimes}
              filterCategory={filterCategory}
              focusedDistrictId={focusedDistrictId}
          />

          {/* District Popup */}
          {selectedDistrict && !selectedIncident && (
              <>
                  <DistrictPopup
                      longitude={selectedDistrict.longitude || 0}
                      latitude={selectedDistrict.latitude || 0}
                      onClose={handleCloseDistrictPopup}
                      district={selectedDistrict}
                      year={year}
                      month={month}
                      filterCategory={filterCategory}
                  />

                  <DistrictExtrusionLayer
                      visible={visible}
                      map={mapboxMap}
                      tilesetId={tilesetId}
                      focusedDistrictId={focusedDistrictId}
                      crimeDataByDistrict={crimeDataByDistrict}
                  />
              </>
          )}

          {/* Incident Popup */}
          {selectedIncident && selectedIncident.latitude && selectedIncident.longitude && (
              <CrimePopup
                  longitude={selectedIncident.longitude}
                  latitude={selectedIncident.latitude}
                  onClose={handleCloseIncidentPopup}
                  incident={selectedIncident}
              />
          )}

          {/* Debug info for development */}
          <div
              className="text-red-500 bg-accent"
              style={{
                  position: "absolute",
                  bottom: 10,
                  left: 10,
                  backgroundColor: "rgba(255,255,255,0.7)",
                  padding: "5px",
                  zIndex: 999,
                  display: process.env.NODE_ENV === "development" ? "block" : "none",
              }}
          >
              <div>Selected District: {selectedDistrict ? selectedDistrict.name : "None"}</div>
              <div>Selected Incident: {selectedIncident ? selectedIncident.id : "None"}</div>
              <div>Focused District ID: {focusedDistrictId || "None"}</div>
          </div>
      </>
  )
}
