"use client";

import { useCallback, useEffect, useRef, useState } 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 { ICrimeSourceTypes, IDistrictFeature } from "@/app/_utils/types/map";
import {
    createFillColorExpression,
    getCrimeRateColor,
    processCrimeDataByDistrict,
} from "@/app/_utils/map/common";

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 TimezoneLayer from "./timezone";
import FaultLinesLayer from "./fault-lines";
import RecentIncidentsLayer from "./recent-incidents-layer";
import IncidentPopup from "../pop-up/incident-popup";
import { manageLayerVisibility } from "@/app/_utils/map/layer-visibility";
import AllIncidentsLayer from "./all-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?: ICrimeSourceTypes;
}

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

                map.getMap().setPaintProperty(
                    "district-extrusion",
                    "fill-extrusion-color",
                    [
                        "case",
                        ["has", "kode_kec"],
                        [
                            "match",
                            ["get", "kode_kec"],
                            focusedDistrictId || "",
                            "transparent",
                            "transparent",
                        ],
                        "transparent",
                    ],
                );

                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(() => {
        animateExtrusionDown();
        handlePopupClose();
    }, [handlePopupClose, animateExtrusionDown]);

    const handleDistrictClick = useCallback(
        (feature: IDistrictFeature) => {
            if (isInteractingWithMarker.current) {
                return;
            }

            setSelectedIncident(null);
            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),
                });

                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 (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) => {
            if (isMarkerClick) {
                isInteractingWithMarker.current = true;

                setTimeout(() => {
                    isInteractingWithMarker.current = false;
                }, 1000);
            }

            setFocusedDistrictId(id);
        },
        [],
    );

    const showHeatmapLayer = activeControl === "heatmap" && sourceType !== "cbu";
    const showUnitsLayer = activeControl === "units";
    const showTimelineLayer = activeControl === "timeline";
    const showRecentIncidents = activeControl === "recents";
    const showAllIncidents = activeControl === "incidents"; // Use the "incidents" tooltip for all incidents
    const showDistrictFill = activeControl === "clusters";

    const showIncidentMarkers = activeControl !== "heatmap" &&
        activeControl !== "timeline" && sourceType !== "cbu";

    const shouldShowExtrusion = focusedDistrictId !== null &&
        !isInteractingWithMarker.current;

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

        const recentLayerIds = [
            "very-recent-incidents-pulse",
            "recent-incidents-glow",
            "recent-incidents",
        ];
        const timelineLayerIds = ["timeline-markers-bg", "timeline-markers"];
        const heatmapLayerIds = ["heatmap-layer"];
        const unitsLayerIds = [
            "units-points",
            "incidents-points",
            "units-labels",
            "units-connection-lines",
        ];
        const clusterLayerIds = [
            "clusters",
            "cluster-count",
            "crime-points",
            "crime-count-labels",
        ];

        const allIncidentsLayerIds = [
            "all-incidents-pulse",
            "all-incidents-circles",
            "all-incidents",
        ];

        if (activeControl !== "recents") {
            manageLayerVisibility(mapboxMap, recentLayerIds, false);
        }

        if (activeControl !== "timeline") {
            manageLayerVisibility(mapboxMap, timelineLayerIds, false);
        }

        if (activeControl !== "heatmap") {
            manageLayerVisibility(mapboxMap, heatmapLayerIds, false);
        }

        if (activeControl !== "units") {
            manageLayerVisibility(mapboxMap, unitsLayerIds, false);
        }

        if (activeControl !== "clusters") {
            manageLayerVisibility(mapboxMap, clusterLayerIds, false);
        }

        if (activeControl !== "incidents") {
            manageLayerVisibility(mapboxMap, allIncidentsLayerIds, false);
        }


    }, [activeControl, mapboxMap]);

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

            {shouldShowExtrusion && (
                <DistrictExtrusionLayer
                    visible={true}
                    map={mapboxMap}
                    tilesetId={tilesetId}
                    focusedDistrictId={focusedDistrictId}
                    crimeDataByDistrict={crimeDataByDistrict}
                />
            )}

            <AllIncidentsLayer
                visible={showAllIncidents}
                map={mapboxMap}
                crimes={crimes}
                filterCategory={filterCategory}
            />

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

            {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} />
        </>
    );
}
