"use client"

import { useState, useRef, useEffect, useCallback } from "react"
import { useMap } from "react-map-gl/mapbox"
import { BASE_BEARING, BASE_DURATION, BASE_PITCH, BASE_ZOOM, MAPBOX_TILESET_ID, PITCH_3D, ZOOM_3D } 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, IIncidentLogs } 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 { ITooltipsControl } 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"
import TimezoneLayer from "./timezone"
import FaultLinesLayer from "./fault-lines"
import EWSAlertLayer from "./ews-alert-layer"
import PanicButtonDemo from "../controls/panic-button-demo"

import type { IIncidentLog } from "@/app/_utils/types/ews"
import { addMockIncident, getAllIncidents, resolveIncident } from "@/app/_utils/mock/ews-data"
import RecentIncidentsLayer from "./recent-incidents-layer"

// 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?: ITooltipsControl
}

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

export default function Layers({
    visible = true,
    crimes,
    recentIncidents,
    units,
    year,
    month,
    filterCategory,
    activeControl,
    tilesetId = MAPBOX_TILESET_ID,
    useAllData = false,
    showEWS = true,
    sourceType = "cbt",
}: LayersProps) {
    const animationRef = useRef<number | null>(null)
    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)
    // Track if we're currently interacting with a marker to prevent district selection
    const isInteractingWithMarker = useRef<boolean>(false)

    const crimeDataByDistrict = processCrimeDataByDistrict(crimes)

    const [ewsIncidents, setEwsIncidents] = useState<IIncidentLog[]>([])
    const [showPanicDemo, setShowPanicDemo] = useState(true)
    const [displayPanicDemo, setDisplayPanicDemo] = useState(showEWS && showPanicDemo)

    useEffect(() => {
        setEwsIncidents(getAllIncidents())
    }, [])

    const handleTriggerAlert = useCallback((priority: "high" | "medium" | "low") => {
        const newIncident = addMockIncident({ priority })
        setEwsIncidents(getAllIncidents())
    }, [])

    const handleResolveIncident = useCallback((id: string) => {
        resolveIncident(id)
        setEwsIncidents(getAllIncidents())
    }, [])

    const handleResolveAllAlerts = useCallback(() => {
        ewsIncidents.forEach((incident) => {
            if (incident.status === "active") {
                resolveIncident(incident.id)
            }
        })
        setEwsIncidents(getAllIncidents())
    }, [ewsIncidents])

    const handlePopupClose = useCallback(() => {
        selectedDistrictRef.current = null
        setSelectedDistrict(null)
        setSelectedIncident(null)
        setFocusedDistrictId(null)
        isInteractingWithMarker.current = false

        if (map) {
            map.easeTo({
                zoom: BASE_ZOOM,
                pitch: BASE_PITCH,
                bearing: BASE_BEARING,
                duration: BASE_DURATION,
                easing: (t) => t * (2 - t),
            })

          if (map.getLayer("clusters")) {
              map.getMap().setLayoutProperty("clusters", "visibility", "visible")
          }
          if (map.getLayer("unclustered-point")) {
              map.getMap().setLayoutProperty("unclustered-point", "visibility", "visible")
          }

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


    const animateExtrusionDown = () => {
        if (!map || !map.getLayer("district-extrusion") || !focusedDistrictId) {
            return
        }

        if (animationRef.current) {
            cancelAnimationFrame(animationRef.current)
            animationRef.current = null
        }

        // Get the current height from the layer (default to 800 if not found)
        let currentHeight = 800

        try {
            const paint = map.getPaintProperty("district-extrusion", "fill-extrusion-height")
            if (Array.isArray(paint) && paint.length > 0) {
                // Try to extract the current height from the expression
                const idx = paint.findIndex((v) => v === focusedDistrictId)
                if (idx !== -1 && typeof paint[idx + 1] === "number") {
                    currentHeight = paint[idx + 1]
                }
            }
        } catch {
            // fallback to default
        }

        const startHeight = currentHeight
        const targetHeight = 0
        const duration = 700
        const startTime = performance.now()

        const animate = (currentTime: number) => {
            const elapsed = currentTime - startTime
            const progress = Math.min(elapsed / duration, 1)
            const easedProgress = progress * (2 - progress)
            const newHeight = startHeight + (targetHeight - startHeight) * easedProgress

            try {
                map.getMap().setPaintProperty("district-extrusion", "fill-extrusion-height", [
                    "case",
                    ["has", "kode_kec"],
                    ["match", ["get", "kode_kec"], focusedDistrictId, newHeight, 0],
                    0,
                ])

                if (progress < 1) {
                    animationRef.current = requestAnimationFrame(animate)
                } else {
                    animationRef.current = null
                }

            } catch (error) {
                if (animationRef.current) {
                    cancelAnimationFrame(animationRef.current)
                    animationRef.current = null
                }
            }
        }

        animationRef.current = requestAnimationFrame(animate)
    }

    const handleCloseDistrictPopup = useCallback(() => {
        console.log("Closing district popup")

        animateExtrusionDown()
        handlePopupClose()
    }, [handlePopupClose, animateExtrusionDown])

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

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

            // If we're currently interacting with a marker, don't process district click
            if (isInteractingWithMarker.current) {
                console.log("Ignoring district click because we're interacting with a marker")
                return
            }

            // Clear any existing incident selection
            setSelectedIncident(null)

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

            if (map && feature.longitude && feature.latitude) {
                map.flyTo({
                    center: [feature.longitude, feature.latitude],
                    zoom: ZOOM_3D,
                    pitch: PITCH_3D,
                    bearing: BASE_BEARING,
                    duration: BASE_DURATION,
                    easing: (t) => t * (2 - t),
                })

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

    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])

    useEffect(() => {
        if (!mapboxMap) return

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

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

          // Set the marker interaction flag to prevent district selection
          isInteractingWithMarker.current = true

          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)

          let foundIncident: ICrimeIncident | undefined

          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 {
              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)
            isInteractingWithMarker.current = false
            return
        }

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

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

          // Clear district selection when showing an incident
          setSelectedDistrict(null)
          selectedDistrictRef.current = null
          setFocusedDistrictId(null)

          setSelectedIncident(foundIncident)

          // Reset the marker interaction flag after a delay
          setTimeout(() => {
              isInteractingWithMarker.current = false
          }, 1000)
      }

        mapboxMap.getCanvas().addEventListener("incident_click", handleIncidentClickEvent as EventListener)
        document.addEventListener("incident_click", handleIncidentClickEvent as EventListener)

        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])

    // Add a listener for unit clicks to set the marker interaction flag
    useEffect(() => {
        if (!mapboxMap) return

        const handleUnitClickEvent = (e: Event) => {
            // Set the marker interaction flag to prevent district selection
            isInteractingWithMarker.current = true

            // Reset the flag after a delay
            setTimeout(() => {
                isInteractingWithMarker.current = false
            }, 1000)
        }

        mapboxMap.getCanvas().addEventListener("unit_click", handleUnitClickEvent as EventListener)
        document.addEventListener("unit_click", handleUnitClickEvent as EventListener)

        return () => {
            if (mapboxMap && mapboxMap.getCanvas()) {
                mapboxMap.getCanvas().removeEventListener("unit_click", handleUnitClickEvent as EventListener)
            }
            document.removeEventListener("unit_click", handleUnitClickEvent as EventListener)
        }
    }, [mapboxMap])

    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])

    const handleSetFocusedDistrictId = useCallback((id: string | null, isMarkerClick = false) => {
        console.log("Setting focused district ID:", id, "from marker click:", isMarkerClick)

        // If this is from a marker click, set the marker interaction flag
        if (isMarkerClick) {
            isInteractingWithMarker.current = true

            // Reset the flag after a delay
            setTimeout(() => {
                isInteractingWithMarker.current = false
            }, 1000)
        }

        setFocusedDistrictId(id)
    }, [])

    const crimesVisible = activeControl === "incidents"
    const showHeatmapLayer = activeControl === "heatmap" && sourceType !== "cbu"
    const showUnitsLayer = activeControl === "units"
    const showTimelineLayer = activeControl === "timeline"
    const showHistoricalLayer = activeControl === "historical"
    const showRecentIncidents = activeControl === "recents"
    const showDistrictFill =
        activeControl === "incidents" ||
        activeControl === "clusters" ||
        activeControl === "historical" ||
        activeControl === "recents"
    const showIncidentMarkers = activeControl !== "heatmap" && activeControl !== "timeline" && sourceType !== "cbu"

    // Ensure showPanicDemo is always defined
    // const [displayPanicDemo, setDisplayPanicDemo] = useState(showEWS && showPanicDemo)

    // Always render the DistrictExtrusionLayer when a district is focused
    // This ensures it's available when needed
    const shouldShowExtrusion = focusedDistrictId !== null && !isInteractingWithMarker.current

    return (
        <>
            <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}
            />

            {/* Always render the extrusion layer when a district is focused */}
            {shouldShowExtrusion && (
                <DistrictExtrusionLayer
                    visible={true}
                    map={mapboxMap}
                    tilesetId={tilesetId}
                    focusedDistrictId={focusedDistrictId}
                    crimeDataByDistrict={crimeDataByDistrict}
                />
            )}

            {/* Recent Incidents Layer (24 hours) */}
            <RecentIncidentsLayer visible={showRecentIncidents} map={mapboxMap} incidents={recentIncidents} />

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

            <TimelineLayer
                crimes={crimes}
                year={year}
                month={month}
                filterCategory={filterCategory}
                visible={showTimelineLayer}
                map={mapboxMap}
                useAllData={useAllData}
            />

            <UnitsLayer
                crimes={crimes}
                units={units}
                filterCategory={filterCategory}
                visible={showUnitsLayer}
                map={mapboxMap}
            />

            <ClusterLayer
                visible={visible && activeControl === "clusters"}
                map={mapboxMap}
                crimes={crimes}
                filterCategory={filterCategory}
                focusedDistrictId={focusedDistrictId}
                clusteringEnabled={activeControl === "clusters"}
                showClusters={activeControl === "clusters"}
                sourceType={sourceType}
            />

            <UnclusteredPointLayer
                visible={visible && showIncidentMarkers && !focusedDistrictId}
                map={mapboxMap}
                crimes={crimes}
                filterCategory={filterCategory}
                focusedDistrictId={focusedDistrictId}
            />

            {selectedDistrict && !selectedIncident && !isInteractingWithMarker.current && (
                <DistrictPopup
                    longitude={selectedDistrict.longitude || 0}
                    latitude={selectedDistrict.latitude || 0}
                    onClose={handleCloseDistrictPopup}
                    district={selectedDistrict}
                    year={year}
                    month={month}
                    filterCategory={filterCategory}
                />
            )}

            <TimezoneLayer map={mapboxMap} />

            <FaultLinesLayer map={mapboxMap} />

            {showEWS && <EWSAlertLayer map={mapboxMap} incidents={ewsIncidents} onIncidentResolved={handleResolveIncident} />}

            {showEWS && displayPanicDemo && (
                <div className="absolute top-0 right-20 z-50 p-2">
                    <PanicButtonDemo
                        onTriggerAlert={handleTriggerAlert}
                        onResolveAllAlerts={handleResolveAllAlerts}
                        activeIncidents={ewsIncidents.filter((inc) => inc.status === "active")}
                    />
                </div>
            )}

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