"use client"

import { useEffect, useMemo, useRef, useState } from 'react'
import { Layer, Source } from "react-map-gl/mapbox"
import { ICrimes, IDistanceResult } from "@/app/_utils/types/crimes"
import { IUnits } from "@/app/_utils/types/units"
import mapboxgl from 'mapbox-gl'
import { useQuery } from '@tanstack/react-query'

import { generateCategoryColorMap, getCategoryColor } from '@/app/_utils/colors'
import UnitPopup from '../pop-up/unit-popup'
import IncidentPopup from '../pop-up/incident-popup'
import { calculateDistances } from '@/app/(pages)/(admin)/dashboard/crime-management/crime-overview/action'

interface UnitsLayerProps {
    crimes: ICrimes[]
    units?: IUnits[]
    filterCategory: string | "all"
    visible?: boolean
    map?: mapboxgl.Map | null
}

// Custom hook for fetching distance data
const useDistanceData = (entityId?: string, isUnit: boolean = false, districtId?: string) => {
    // Skip the query when no entity is selected
    return useQuery({
        queryKey: ['distance-incidents', entityId, isUnit, districtId],
        queryFn: async () => {
            if (!entityId) return [];
            const unitId = isUnit ? entityId : undefined;
            const result = await calculateDistances(unitId, districtId);
            return result;
        },
        enabled: !!entityId, // Only run query when there's an entityId
        staleTime: 5 * 60 * 1000, // Consider data fresh for 5 minutes
        gcTime: 10 * 60 * 1000, // Keep cache for 10 minutes
    });
};

export default function UnitsLayer({
    crimes,
    units = [],
    filterCategory,
    visible = false,
    map
}: UnitsLayerProps) {
    const [loadedUnits, setLoadedUnits] = useState<IUnits[]>([])
    const loadedUnitsRef = useRef<IUnits[]>([])

    // For popups
    const [selectedUnit, setSelectedUnit] = useState<IUnits | null>(null)
    const [selectedIncident, setSelectedIncident] = useState<any | null>(null)
    const [selectedEntityId, setSelectedEntityId] = useState<string | undefined>()
    const [isUnitSelected, setIsUnitSelected] = useState<boolean>(false)
    const [selectedDistrictId, setSelectedDistrictId] = useState<string | undefined>()

    // Use react-query for distance data
    const { data: distances = [], isLoading: isLoadingDistances } = useDistanceData(
        selectedEntityId,
        isUnitSelected,
        selectedDistrictId
    );

    // Use either provided units or loaded units
    const unitsData = useMemo(() => {
        return units.length > 0 ? units : (loadedUnits || [])
    }, [units, loadedUnits])

    // Extract all unique crime categories for color generation
    const uniqueCategories = useMemo(() => {
        const categories = new Set<string>();
        crimes.forEach(crime => {
            crime.crime_incidents.forEach(incident => {
                if (incident.crime_categories?.name) {
                    categories.add(incident.crime_categories.name);
                }
            });
        });
        return Array.from(categories);
    }, [crimes]);

    // Generate color map for all categories
    const categoryColorMap = useMemo(() => {
        return generateCategoryColorMap(uniqueCategories);
    }, [uniqueCategories]);

    // Process units data to GeoJSON format
    const unitsGeoJSON = useMemo(() => {
        return {
            type: "FeatureCollection" as const,
            features: unitsData.map(unit => ({
                type: "Feature" as const,
                properties: {
                    id: unit.code_unit,
                    name: unit.name,
                    address: unit.address,
                    phone: unit.phone,
                    type: unit.type,
                    district: unit.districts?.name || "",
                    district_id: unit.district_id,
                },
                geometry: {
                    type: "Point" as const,
                    coordinates: [unit.longitude || 0, unit.latitude || 0]
                }
            })).filter(feature =>
                feature.geometry.coordinates[0] !== 0 &&
                feature.geometry.coordinates[1] !== 0
            )
        }
    }, [unitsData])

    // Process incident data to GeoJSON format
    const incidentsGeoJSON = useMemo(() => {
        const features: any[] = [];

        crimes.forEach(crime => {
            crime.crime_incidents.forEach(incident => {
                // Skip incidents without location data or filtered by category
                if (
                    !incident.locations?.latitude ||
                    !incident.locations?.longitude ||
                    (filterCategory !== "all" && incident.crime_categories.name !== filterCategory)
                ) return;

                features.push({
                    type: "Feature" as const,
                    properties: {
                        id: incident.id,
                        description: incident.description || "No description",
                        category: incident.crime_categories.name,
                        date: incident.timestamp,
                        district: crime.districts?.name || "",
                        district_id: crime.district_id,
                        categoryColor: categoryColorMap[incident.crime_categories.name] || '#22c55e',
                    },
                    geometry: {
                        type: "Point" as const,
                        coordinates: [incident.locations.longitude, incident.locations.latitude]
                    }
                });
            });
        });

        return {
            type: "FeatureCollection" as const,
            features
        };
    }, [crimes, filterCategory, categoryColorMap]);

    // Create lines between units and incidents within their districts
    const connectionLinesGeoJSON = useMemo(() => {
        if (!unitsData.length || !crimes.length) return {
            type: "FeatureCollection" as const,
            features: []
        }

        // Map district IDs to their units
        const districtUnitsMap = new Map<string, IUnits[]>()

        unitsData.forEach(unit => {
            if (!unit.district_id || !unit.longitude || !unit.latitude) return

            if (!districtUnitsMap.has(unit.district_id)) {
                districtUnitsMap.set(unit.district_id, [])
            }
            districtUnitsMap.get(unit.district_id)!.push(unit)
        })

        // Create lines from units to incidents in their district
        const lineFeatures: any[] = []

        crimes.forEach(crime => {
            // Get all units in this district
            const districtUnits = districtUnitsMap.get(crime.district_id) || []
            if (!districtUnits.length) return

            // For each incident in this district
            crime.crime_incidents.forEach(incident => {
                // Skip incidents without location data or filtered by category
                if (
                    !incident.locations?.latitude ||
                    !incident.locations?.longitude ||
                    (filterCategory !== "all" && incident.crime_categories.name !== filterCategory)
                ) return

                // Create a line from each unit in this district to this incident
                districtUnits.forEach(unit => {
                    if (!unit.longitude || !unit.latitude) return

                    lineFeatures.push({
                        type: "Feature" as const,
                        properties: {
                            unit_id: unit.code_unit,
                            unit_name: unit.name,
                            incident_id: incident.id,
                            district_id: crime.district_id,
                            district_name: crime.districts.name,
                            category: incident.crime_categories.name,
                            lineColor: categoryColorMap[incident.crime_categories.name] || '#22c55e',
                        },
                        geometry: {
                            type: "LineString" as const,
                            coordinates: [
                                [unit.longitude, unit.latitude],
                                [incident.locations.longitude, incident.locations.latitude]
                            ]
                        }
                    })
                })
            })
        })

        return {
            type: "FeatureCollection" as const,
            features: lineFeatures
        }
    }, [unitsData, crimes, filterCategory, categoryColorMap])

    useEffect(() => {
        if (!map || !visible) return

        const handleUnitClick = (e: mapboxgl.MapMouseEvent & { features?: mapboxgl.MapboxGeoJSONFeature[] }) => {
            if (!e.features || e.features.length === 0) return

            const feature = e.features[0]
            const properties = feature.properties

            if (!properties) return

            // Find the unit in our data
            const unit = unitsData.find(u => u.code_unit === properties.id);
            if (!unit) return;

            // Set the selected unit and query parameters
            setSelectedUnit(unit);
            setSelectedIncident(null); // Clear any selected incident
            setSelectedEntityId(properties.id);
            setIsUnitSelected(true);
            setSelectedDistrictId(properties.district_id);

            // Highlight the connected lines for this unit
            if (map.getLayer('units-connection-lines')) {
                map.setFilter('units-connection-lines', [
                    '==',
                    ['get', 'unit_id'],
                    properties.id
                ])
            }
        }

        const handleIncidentClick = (e: mapboxgl.MapMouseEvent & { features?: mapboxgl.MapboxGeoJSONFeature[] }) => {
            if (!e.features || e.features.length === 0) return;

            const feature = e.features[0];
            const properties = feature.properties;

            if (!properties) return;

            // Create incident object from properties
            const incident = {
                id: properties.id,
                category: properties.category,
                description: properties.description,
                date: properties.date,
                district: properties.district,
                district_id: properties.district_id,
            };

            // Set the selected incident and query parameters
            setSelectedIncident({
                ...incident,
                latitude: feature.geometry.type === 'Point' ?
                    (feature.geometry as any).coordinates[1] : 0,
                longitude: feature.geometry.type === 'Point' ?
                    (feature.geometry as any).coordinates[0] : 0
            });
            setSelectedUnit(null);
            setSelectedEntityId(properties.id);
            setIsUnitSelected(false);
            setSelectedDistrictId(properties.district_id);

            // Highlight the connected lines for this incident
            if (map.getLayer('units-connection-lines')) {
                map.setFilter('units-connection-lines', [
                    '==',
                    ['get', 'incident_id'],
                    properties.id
                ]);
            }
        };

        // Define event handlers that can be referenced for both adding and removing
        const handleMouseEnter = () => {
            map.getCanvas().style.cursor = 'pointer'
        }

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

        // Add click event for units-points layer
        if (map.getLayer('units-points')) {
            map.on('click', 'units-points', handleUnitClick)

            // Change cursor on hover
            map.on('mouseenter', 'units-points', handleMouseEnter)
            map.on('mouseleave', 'units-points', handleMouseLeave)
        }

        // Add click event for incidents-points layer
        if (map.getLayer('incidents-points')) {
            map.on('click', 'incidents-points', handleIncidentClick)

            // Change cursor on hover
            map.on('mouseenter', 'incidents-points', handleMouseEnter)
            map.on('mouseleave', 'incidents-points', handleMouseLeave)
        }

        return () => {
            if (map.getLayer('units-points')) {
                map.off('click', 'units-points', handleUnitClick)
                map.off('mouseenter', 'units-points', handleMouseEnter)
                map.off('mouseleave', 'units-points', handleMouseLeave)
            }

            if (map.getLayer('incidents-points')) {
                map.off('click', 'incidents-points', handleIncidentClick)
                map.off('mouseenter', 'incidents-points', handleMouseEnter)
                map.off('mouseleave', 'incidents-points', handleMouseLeave)
            }
        }
    }, [map, visible, unitsData])

    // Reset map filters when popup is closed
    const handleClosePopup = () => {
        setSelectedUnit(null);
        setSelectedIncident(null);
        setSelectedEntityId(undefined);
        setSelectedDistrictId(undefined);

        if (map && map.getLayer('units-connection-lines')) {
            map.setFilter('units-connection-lines', ['has', 'unit_id']);
        }
    };

    if (!visible) return null

    return (
        <>
            {/* Units Points */}
            <Source id="units-source" type="geojson" data={unitsGeoJSON}>
                <Layer
                    id="units-points"
                    type="circle"
                    paint={{
                        'circle-radius': 8,
                        'circle-color': '#1e40af', // Deep blue for police units
                        'circle-stroke-width': 2,
                        'circle-stroke-color': '#ffffff',
                        'circle-opacity': 0.8
                    }}
                />

                {/* Units Symbols */}
                <Layer
                    id="units-symbols"
                    type="symbol"
                    layout={{
                        'text-field': ['get', 'name'],
                        'text-font': ['DIN Offc Pro Medium', 'Arial Unicode MS Bold'],
                        'text-size': 12,
                        'text-offset': [0, -2],
                        'text-anchor': 'bottom',
                        'text-allow-overlap': false,
                        'text-ignore-placement': false
                    }}
                    paint={{
                        'text-color': '#ffffff',
                        'text-halo-color': '#000000',
                        'text-halo-width': 1
                    }}
                />
            </Source>

            {/* Incidents Points */}
            <Source id="incidents-source" type="geojson" data={incidentsGeoJSON}>
                <Layer
                    id="incidents-points"
                    type="circle"
                    paint={{
                        'circle-radius': 6,
                        // Use the pre-computed color stored in the properties
                        'circle-color': ['get', 'categoryColor'],
                        'circle-stroke-width': 1,
                        'circle-stroke-color': '#ffffff',
                        'circle-opacity': 0.8
                    }}
                />
            </Source>

            {/* Connection Lines */}
            <Source id="units-lines-source" type="geojson" data={connectionLinesGeoJSON}>
                <Layer
                    id="units-connection-lines"
                    type="line"
                    paint={{
                        // Use the pre-computed color stored in the properties
                        'line-color': ['get', 'lineColor'],
                        'line-width': 3,
                        'line-opacity': 0.9,
                        'line-blur': 0.5,
                        'line-dasharray': [3, 1],
                    }}
                />
            </Source>

            {/* Custom Unit Popup */}
            {selectedUnit && (
                <UnitPopup
                    longitude={selectedUnit.longitude || 0}
                    latitude={selectedUnit.latitude || 0}
                    onClose={handleClosePopup}
                    unit={{
                        id: selectedUnit.code_unit,
                        name: selectedUnit.name,
                        type: selectedUnit.type,
                        address: selectedUnit.address || "No address",
                        phone: selectedUnit.phone || "No phone",
                        district: selectedUnit.districts?.name,
                        district_id: selectedUnit.district_id
                    }}
                    distances={distances}
                    isLoadingDistances={isLoadingDistances}
                />
            )}

            {/* Custom Incident Popup */}
            {selectedIncident && (
                <IncidentPopup
                    longitude={selectedIncident.longitude}
                    latitude={selectedIncident.latitude}
                    onClose={handleClosePopup}
                    incident={selectedIncident}
                    distances={distances}
                    isLoadingDistances={isLoadingDistances}
                />
            )}
        </>
    )
}
