"use client"

import { useEffect, useState, useRef } from "react"
import { useMap } from "react-map-gl/mapbox"
import { CRIME_RATE_COLORS, MAPBOX_TILESET_ID } from "@/app/_utils/const/map"
import { DistrictPopup } from "../pop-up"

// Types for district properties
export interface DistrictFeature {
    id: string
    name: string
    properties: Record<string, any>
    longitude?: number
    latitude?: number
    number_of_crime?: number
    level?: "low" | "medium" | "high" | "critical"
}

// District layer props
export interface DistrictLayerProps {
    visible?: boolean
    onClick?: (feature: DistrictFeature) => void
    year?: string
    month?: string
    crimes?: Array<{
      id: string
      district_name: string
      distrcit_id?: string
      number_of_crime?: number
      level?: "low" | "medium" | "high" | "critical"
      incidents: any[]
  }>
    tilesetId?: string
}

export default function DistrictLayer({
    visible = true,
    onClick,
    year,
    month,
    crimes = [],
    tilesetId = MAPBOX_TILESET_ID,
}: DistrictLayerProps) {
    const { current: map } = useMap()

    const [hoverInfo, setHoverInfo] = useState<{
      x: number
      y: number
      feature: any
  } | null>(null)
    const [selectedDistrict, setSelectedDistrict] = useState<DistrictFeature | null>(null)

    // Use a ref to track whether layers have been added
    const layersAdded = useRef(false)

    // Process crime data to map to districts by district_id (kode_kec)
    const crimeDataByDistrict = crimes.reduce(
        (acc, crime) => {
            // Use district_id (which corresponds to kode_kec in the tileset) as the key
            const districtId = crime.distrcit_id || crime.district_name

            console.log("Mapping district:", districtId, "level:", crime.level)

          acc[districtId] = {
              number_of_crime: crime.number_of_crime,
              level: crime.level,
          }
          return acc
      },
      {} as Record<string, { number_of_crime?: number; level?: "low" | "medium" | "high" | "critical" }>,
  )

    // Handle click on district
    const handleClick = (e: any) => {
      if (!map || !e.features || e.features.length === 0) return

      const feature = e.features[0]
      const districtId = feature.properties.kode_kec // Using kode_kec as the unique identifier
      const crimeData = crimeDataByDistrict[districtId] || {}

      const district: DistrictFeature = {
          id: districtId,
          name: feature.properties.nama || feature.properties.kecamatan,
          properties: feature.properties,
          longitude: e.lngLat.lng,
          latitude: e.lngLat.lat,
          ...crimeData,
    }

      if (onClick) {
        onClick(district)
    } else {
        setSelectedDistrict(district)
    }
  }

    // Handle mouse move for hover effect
    const handleMouseMove = (e: any) => {
      if (!map || !e.features || e.features.length === 0) return

      const feature = e.features[0]
      const districtId = feature.properties.kode_kec // Using kode_kec as the unique identifier
      const crimeData = crimeDataByDistrict[districtId] || {}

      console.log("Hover district:", districtId, "found data:", crimeData)

      // Enhance feature with crime data
      feature.properties = {
          ...feature.properties,
          ...crimeData,
    }

      setHoverInfo({
          x: e.point.x,
          y: e.point.y,
          feature: feature,
    })
  }

    // Add district layer to the map when it's loaded
    useEffect(() => {
      if (!map || !visible) return

      // Handler for style load event
      const onStyleLoad = () => {
        // Skip if map is not available
        if (!map) return

        try {
          // Check if the source already exists to prevent duplicates
          if (!map.getMap().getSource("districts")) {
              // Get the first symbol layer ID from the map style
              // This ensures our layers appear below labels and POIs
              const layers = map.getStyle().layers
              let firstSymbolId: string | undefined
              for (const layer of layers) {
                  if (layer.type === "symbol") {
                      firstSymbolId = layer.id
                      break
                  }
              }

            // Add the vector tile source
            map.getMap().addSource("districts", {
                type: "vector",
                url: `mapbox://${tilesetId}`,
            })

            // Create the dynamic fill color expression based on crime data
            const fillColorExpression: any = [
                "case",
                ["has", "kode_kec"],
                [
                    "match",
                    ["get", "kode_kec"],
                    ...Object.entries(crimeDataByDistrict).flatMap(([districtId, data]) => {
                        console.log("Initial color setting for:", districtId, "level:", data.level)
                        return [
                            districtId,
                            data.level === "low"
                                ? CRIME_RATE_COLORS.low
                                : data.level === "medium"
                                    ? CRIME_RATE_COLORS.medium
                                    : data.level === "high"
                                        ? CRIME_RATE_COLORS.high
                                        : data.level === "critical"
                                            ? CRIME_RATE_COLORS.critical
                                            : CRIME_RATE_COLORS.default,
                        ]
                    }),
                    CRIME_RATE_COLORS.default,
                ],
                CRIME_RATE_COLORS.default,
            ]

            // Only add layers if they don't already exist
            if (!map.getMap().getLayer("district-fill")) {
                // Add the fill layer for districts with dynamic colors from the start
                // Insert below the first symbol layer to preserve Mapbox default layers
                map.getMap().addLayer(
                    {
                        id: "district-fill",
                        type: "fill",
                        source: "districts",
                        "source-layer": "Districts",
                        paint: {
                            "fill-color": fillColorExpression, // Apply colors based on crime data
                            "fill-opacity": 0.6,
                        },
                    },
                    firstSymbolId,
                ) // Add before the first symbol layer
            }

            if (!map.getMap().getLayer("district-line")) {
                // Add the line layer for district borders
                map.getMap().addLayer(
                    {
                        id: "district-line",
                        type: "line",
                        source: "districts",
                        "source-layer": "Districts",
                        paint: {
                            "line-color": "#ffffff",
                            "line-width": 1,
                            "line-opacity": 0.5,
                        },
                    },
                    firstSymbolId,
                )
            }

            if (!map.getMap().getLayer("district-labels")) {
                // Add district labels with improved visibility and responsive sizing
                map.getMap().addLayer(
                    {
                        id: "district-labels",
                        type: "symbol",
                        source: "districts",
                        "source-layer": "Districts",
                        layout: {
                            "text-field": ["get", "nama"],
                            "text-font": ["Open Sans Bold", "Arial Unicode MS Bold"],
                            // Make text size responsive to zoom level
                            "text-size": [
                                "interpolate",
                                ["linear"],
                                ["zoom"],
                                9,
                                8, // At zoom level 9, size 8px
                                12,
                                12, // At zoom level 12, size 12px
                                15,
                                14, // At zoom level 15, size 14px
                            ],
                            "text-allow-overlap": false,
                            "text-ignore-placement": false,
                            // Adjust text anchor based on zoom level
                            "text-anchor": "center",
                            "text-justify": "center",
                            "text-max-width": 8,
                            // Show labels only at certain zoom levels
                            "text-optional": true,
                            "symbol-sort-key": ["get", "kode_kec"], // Sort labels by district code
                            "symbol-z-order": "source",
                        },
                        paint: {
                            "text-color": "#000000",
                            "text-halo-color": "#ffffff",
                            "text-halo-width": 2,
                            "text-halo-blur": 1,
                            // Fade in text opacity based on zoom level
                            "text-opacity": [
                                "interpolate",
                                ["linear"],
                                ["zoom"],
                                8,
                                0, // Fully transparent at zoom level 8
                                9,
                                0.6, // 60% opacity at zoom level 9
                                10,
                                1.0, // Fully opaque at zoom level 10
                            ],
                        },
                    },
                    firstSymbolId,
                )
            }

            // Create a source for clustered incident markers
            if (crimes.length > 0 && !map.getMap().getSource("crime-incidents")) {
                // Collect all incidents from all districts
                const allIncidents = crimes.flatMap((crime) =>
                    crime.incidents.map((incident) => ({
                        type: "Feature" as const,
                        properties: {
                            id: incident.id,
                            district: crime.district_name,
                            category: incident.category,
                            incidentType: incident.type,
                            level: crime.level,
                            description: incident.description,
                        },
                        geometry: {
                            type: "Point" as const,
                            coordinates: [incident.longitude, incident.latitude],
                        },
                    })),
                )

                // Add a clustered GeoJSON source for incidents
                map.getMap().addSource("crime-incidents", {
                    type: "geojson",
                    data: {
                        type: "FeatureCollection",
                        features: allIncidents,
                    },
                    cluster: true,
                    clusterMaxZoom: 14,
                    clusterRadius: 50,
                })

              // Only add layers if they don't already exist
              if (!map.getMap().getLayer("clusters")) {
                  // Add a layer for the clusters - place below default symbol layers
                  map.getMap().addLayer(
                      {
                          id: "clusters",
                          type: "circle",
                          source: "crime-incidents",
                          filter: ["has", "point_count"],
                          paint: {
                      "circle-color": [
                          "step",
                          ["get", "point_count"],
                          "#51bbd6", // Blue for small clusters
                          5,
                          "#f1f075", // Yellow for medium clusters
                          15,
                          "#f28cb1", // Pink for large clusters
                      ],
                      "circle-radius": [
                          "step",
                          ["get", "point_count"],
                          20, // Size for small clusters
                          5,
                          30, // Size for medium clusters
                          15,
                          40, // Size for large clusters
                      ],
                          "circle-opacity": 0.75,
                      },
                  },
                  firstSymbolId,
              )
            }

              if (!map.getMap().getLayer("cluster-count")) {
                  // Add a layer for cluster counts
                  map.getMap().addLayer({
                  id: "cluster-count",
                  type: "symbol",
                  source: "crime-incidents",
                  filter: ["has", "point_count"],
                  layout: {
                      "text-field": "{point_count_abbreviated}",
                      "text-font": ["DIN Offc Pro Medium", "Arial Unicode MS Bold"],
                      "text-size": 12,
                  },
                  paint: {
                      "text-color": "#ffffff",
                  },
              })
              }

              if (!map.getMap().getLayer("unclustered-point")) {
                  // Add a layer for individual incident points
                  map.getMap().addLayer(
                      {
                          id: "unclustered-point",
                          type: "circle",
                          source: "crime-incidents",
                          filter: ["!", ["has", "point_count"]],
                          paint: {
                          "circle-color": "#11b4da",
                          "circle-radius": 8,
                          "circle-stroke-width": 1,
                          "circle-stroke-color": "#fff",
                      },
                  },
                  firstSymbolId,
              )
              }

              // Add click handler for clusters
              map.on("click", "clusters", (e) => {
                  const features = map.queryRenderedFeatures(e.point, { layers: ["clusters"] })

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

                  const clusterId = features[0].properties?.cluster_id

                      // Get the cluster expansion zoom
                      ; (map.getSource("crime-incidents") as mapboxgl.GeoJSONSource).getClusterExpansionZoom(
                          clusterId,
                          (err, zoom) => {
                              if (err) return

                              map.easeTo({
                                  center: (features[0].geometry as any).coordinates,
                                  zoom: zoom ?? undefined,
                              })
                          },
                      )
              })

              // Show pointer cursor on clusters and points
              map.on("mouseenter", "clusters", () => {
                  map.getCanvas().style.cursor = "pointer"
              })

              map.on("mouseleave", "clusters", () => {
                  map.getCanvas().style.cursor = ""
              })

              map.on("mouseenter", "unclustered-point", () => {
                  map.getCanvas().style.cursor = "pointer"
              })

              map.on("mouseleave", "unclustered-point", () => {
                  map.getCanvas().style.cursor = ""
              })
          }

            // Set event handlers
            map.on("click", "district-fill", handleClick)
            map.on("mousemove", "district-fill", handleMouseMove)
            map.on("mouseleave", "district-fill", () => setHoverInfo(null))

            // Mark layers as added
              layersAdded.current = true
              console.log("District layers added successfully")
          } else {
              // If the source already exists, just update the data
              console.log("District source already exists, updating data")

              // Update the district-fill layer with new crime data if it exists
              if (map.getMap().getLayer("district-fill")) {
                  map.getMap().setPaintProperty("district-fill", "fill-color", [
                      "case",
                      ["has", "kode_kec"],
                      [
                          "match",
                          ["get", "kode_kec"],
                          ...Object.entries(crimeDataByDistrict).flatMap(([districtId, data]) => {
                              return [
                                  districtId,
                                  data.level === "low"
                                      ? CRIME_RATE_COLORS.low
                                      : data.level === "medium"
                                          ? CRIME_RATE_COLORS.medium
                                          : data.level === "high"
                                              ? CRIME_RATE_COLORS.high
                                              : data.level === "critical"
                                                  ? CRIME_RATE_COLORS.critical
                                                  : CRIME_RATE_COLORS.default,
                              ]
                          }),
                          CRIME_RATE_COLORS.default,
                      ],
                      CRIME_RATE_COLORS.default,
                  ] as any)
              }
          }
      } catch (error) {
          console.error("Error adding district layers:", error)
      }
    }

      // If the map's style is already loaded, add the layers immediately
      if (map.isStyleLoaded()) {
        onStyleLoad()
    } else {
        // Otherwise, wait for the style.load event
        map.once("style.load", onStyleLoad)
    }

      // Cleanup function
      return () => {
        if (map) {
            // Only remove event listeners, not the layers themselves
            map.off("click", "district-fill", handleClick)
            map.off("mousemove", "district-fill", handleMouseMove)
            map.off("mouseleave", "district-fill", () => setHoverInfo(null))

          // We're not removing the layers or sources here to avoid disrupting the map
          // This prevents the issue of removing default layers
      }
      }
  }, [map, visible, tilesetId, crimes])

    // Update the crime data when it changes
    useEffect(() => {
      if (!map || !layersAdded.current) return

      console.log("Updating district colors with data:", crimeDataByDistrict)

      // Update the district-fill layer with new crime data
      try {
        // Check if the layer exists before updating it
        if (map.getMap().getLayer("district-fill")) {
          // We need to update the layer paint property to correctly apply colors
            map.getMap().setPaintProperty("district-fill", "fill-color", [
                "case",
                ["has", "kode_kec"],
                [
                    "match",
                    ["get", "kode_kec"],
                    ...Object.entries(crimeDataByDistrict).flatMap(([districtId, data]) => {
                        console.log("Setting color for:", districtId, "level:", data.level)
                        return [
                            districtId,
                            data.level === "low"
                                ? CRIME_RATE_COLORS.low
                                : data.level === "medium"
                                    ? CRIME_RATE_COLORS.medium
                                    : data.level === "high"
                                        ? CRIME_RATE_COLORS.high
                                        : data.level === "critical"
                                            ? CRIME_RATE_COLORS.critical
                                            : CRIME_RATE_COLORS.default,
                        ]
                    }),
                    CRIME_RATE_COLORS.default,
                ],
                CRIME_RATE_COLORS.default,
            ] as any)
        }
    } catch (error) {
        console.error("Error updating district layer:", error)
    }
  }, [map, crimes])

    if (!visible) return null

    return (
        <>
            {/* Hover tooltip */}
            {hoverInfo && (
                <div
                    className="absolute z-10 bg-white rounded-md shadow-md px-3 py-2 pointer-events-none"
                    style={{
                        left: hoverInfo.x + 10,
                        top: hoverInfo.y + 10,
                    }}
                >
                    <p className="text-sm font-medium">
                        {hoverInfo.feature.properties.nama || hoverInfo.feature.properties.kecamatan}
                    </p>
                    {hoverInfo.feature.properties.number_of_crime !== undefined && (
                        <p className="text-xs text-gray-600">
                            {hoverInfo.feature.properties.number_of_crime} incidents
                            {hoverInfo.feature.properties.level && (
                              <span className="ml-2 text-xs font-semibold text-gray-500">({hoverInfo.feature.properties.level})</span>
                          )}
                      </p>
                  )}
              </div>
          )}

          {/* District popup */}
          {selectedDistrict && selectedDistrict.longitude && selectedDistrict.latitude && (
              <DistrictPopup
                  longitude={selectedDistrict.longitude}
                  latitude={selectedDistrict.latitude}
                  onClose={() => setSelectedDistrict(null)}
                  district={selectedDistrict}
                  year={year}
                  month={month}
              />
          )}
      </>
  )
}
