"use client"

import { getCrimeRateColor } from "@/app/_utils/map"
import { IExtrusionLayerProps } from "@/app/_utils/types/map"
import { useEffect, useRef } from "react"

export default function DistrictExtrusionLayer({
    visible = true,
    map,
    tilesetId,
    focusedDistrictId,
    crimeDataByDistrict,
}: IExtrusionLayerProps) {
    const animationRef = useRef<number | null>(null)
    const bearingRef = useRef(0)
    const rotationAnimationRef = useRef<number | null>(null)
    const extrusionCreatedRef = useRef(false)
    const lastFocusedDistrictRef = useRef<string | null>(null)

    // Handle extrusion layer creation and updates
    useEffect(() => {
        if (!map || !visible) return

        const onStyleLoad = () => {
            if (!map) return

            try {
                const layers = map.getStyle().layers
                let firstSymbolId: string | undefined
                for (const layer of layers) {
                    if (layer.type === "symbol") {
                        firstSymbolId = layer.id
                        break
                    }
                }

                // Remove existing layer if it exists to avoid conflicts
                if (map.getLayer("district-extrusion")) {
                    map.removeLayer("district-extrusion")
                    extrusionCreatedRef.current = false
                }

                // Make sure the districts source exists
                if (!map.getSource("districts")) {
                    if (!tilesetId) {
                        console.error("No tileset ID provided for districts source");
                        return;
                    }

                    map.addSource("districts", {
                        type: "vector",
                        url: `mapbox://${tilesetId}`,
                    })
                }

                // Create the extrusion layer
                map.addLayer(
                    {
                        id: "district-extrusion",
                        type: "fill-extrusion",
                        source: "districts",
                        "source-layer": "Districts",
                        paint: {
                            "fill-extrusion-color": [
                                "case",
                                ["has", "kode_kec"],
                                [
                                    "match",
                                    ["get", "kode_kec"],
                                    focusedDistrictId || "",
                                    getCrimeRateColor(crimeDataByDistrict[focusedDistrictId || ""]?.level),
                                    "transparent",
                                ],
                                "transparent",
                            ],
                            "fill-extrusion-height": [
                                "case",
                                ["has", "kode_kec"],
                                ["match", ["get", "kode_kec"], focusedDistrictId || "", 0, 0], // Start at 0 for animation
                                0,
                            ],
                            "fill-extrusion-base": 0,
                            "fill-extrusion-opacity": 0.8,
                        },
                        filter: ["==", ["get", "kode_kec"], focusedDistrictId || ""],
                    },
                    firstSymbolId
                )

                extrusionCreatedRef.current = true

                // If a district is focused, start the animation
                if (focusedDistrictId) {
                    lastFocusedDistrictRef.current = focusedDistrictId
                    animateExtrusion()
                }
            } catch (error) {
                console.error("Error adding district extrusion layer:", error)
            }
        }

        if (map.isStyleLoaded()) {
            onStyleLoad()
        } else {
            map.once("style.load", onStyleLoad)
        }

        return () => {
            if (animationRef.current) {
                cancelAnimationFrame(animationRef.current)
                animationRef.current = null
            }
        }
    }, [map, visible, tilesetId])

    // Update filter and color when focused district changes
    useEffect(() => {
        if (!map || !map.getLayer("district-extrusion")) return

        // Skip unnecessary updates if nothing has changed
        if (lastFocusedDistrictRef.current === focusedDistrictId) return;

        // If we're unfocusing a district
        if (!focusedDistrictId) {
            // Stop rotation when unfocusing
            if (rotationAnimationRef.current) {
                cancelAnimationFrame(rotationAnimationRef.current)
                rotationAnimationRef.current = null
            }
            bearingRef.current = 0

            // Animate height down
            const animateHeightDown = () => {
                if (!map || !map.getLayer("district-extrusion")) return;

                let currentHeight = 800;
                const duration = 500;
                const startTime = performance.now();

                const animate = (time: number) => {
                    const elapsed = time - startTime;
                    const progress = Math.min(elapsed / duration, 1);
                    const easedProgress = progress * (2 - progress); // easeOutQuad
                    const height = 800 - (800 * easedProgress);

                    try {
                        map.setPaintProperty("district-extrusion", "fill-extrusion-height", [
                            "case",
                            ["has", "kode_kec"],
                            ["match", ["get", "kode_kec"], lastFocusedDistrictRef.current || "", height, 0],
                            0,
                        ]);

                        if (progress < 1) {
                            animationRef.current = requestAnimationFrame(animate);
                        } else {
                            // Reset when animation completes
                            map.setPaintProperty("district-extrusion", "fill-extrusion-height", [
                                "case",
                                ["has", "kode_kec"],
                                ["match", ["get", "kode_kec"], "", 0, 0],
                                0,
                            ]);
                            map.setFilter("district-extrusion", ["==", ["get", "kode_kec"], ""]);

                            lastFocusedDistrictRef.current = null;

                            // Ensure bearing is reset
                            map.setBearing(0);
                        }
                    } catch (error) {
                        console.error("Error animating extrusion down:", error);
                        if (animationRef.current) {
                            cancelAnimationFrame(animationRef.current);
                            animationRef.current = null;
                        }
                    }
                };

                if (animationRef.current) {
                    cancelAnimationFrame(animationRef.current);
                }
                animationRef.current = requestAnimationFrame(animate);
            };

            animateHeightDown();
            return;
        }

        try {
            // Update filter for the new district
            map.setFilter("district-extrusion", ["==", ["get", "kode_kec"], focusedDistrictId]);

            // Update the extrusion color
            map.setPaintProperty("district-extrusion", "fill-extrusion-color", [
                "case",
                ["has", "kode_kec"],
                [
                    "match",
                    ["get", "kode_kec"],
                    focusedDistrictId,
                    getCrimeRateColor(crimeDataByDistrict[focusedDistrictId]?.level),
                    "transparent",
                ],
                "transparent",
            ]);

            // Reset height for animation
            map.setPaintProperty("district-extrusion", "fill-extrusion-height", [
                "case",
                ["has", "kode_kec"],
                ["match", ["get", "kode_kec"], focusedDistrictId, 0, 0],
                0,
            ]);

            // Store current focused district
            lastFocusedDistrictRef.current = focusedDistrictId;

            // Stop any existing animations and restart
            if (rotationAnimationRef.current) {
                cancelAnimationFrame(rotationAnimationRef.current);
                rotationAnimationRef.current = null;
            }

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

            // Start animation with small delay to ensure smooth transition
            setTimeout(() => {
                animateExtrusion();
            }, 100);

        } catch (error) {
            console.error("Error updating district extrusion:", error)
        }
    }, [map, focusedDistrictId, crimeDataByDistrict])

    // Cleanup on unmount
    useEffect(() => {
        return () => {
            if (animationRef.current) {
                cancelAnimationFrame(animationRef.current)
                animationRef.current = null
            }
            if (rotationAnimationRef.current) {
                cancelAnimationFrame(rotationAnimationRef.current)
                rotationAnimationRef.current = null
            }
        }
    }, [])

    // Animate extrusion height
    const animateExtrusion = () => {
        if (!map || !map.getLayer("district-extrusion") || !focusedDistrictId) return

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

        const startHeight = 0
        const targetHeight = 800
        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) // easeOutQuad
            const currentHeight = startHeight + (targetHeight - startHeight) * easedProgress

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

                if (progress < 1) {
                    animationRef.current = requestAnimationFrame(animate)
                } else {
                    // Start rotation after extrusion completes
                    startRotation()
                }
            } catch (error) {
                console.error("Error animating extrusion:", error)
                if (animationRef.current) {
                    cancelAnimationFrame(animationRef.current)
                    animationRef.current = null
                }
            }
        }

        animationRef.current = requestAnimationFrame(animate)
    }

    // Start rotation animation
    const startRotation = () => {
        if (!map || !focusedDistrictId) return

        const rotationSpeed = 0.05 // degrees per frame
        bearingRef.current = 0 // Reset bearing at start

        const animate = () => {
            if (!map || !focusedDistrictId || focusedDistrictId !== lastFocusedDistrictRef.current) {
                if (rotationAnimationRef.current) {
                    cancelAnimationFrame(rotationAnimationRef.current)
                    rotationAnimationRef.current = null
                }
                return
            }

            try {
                // Update bearing with smooth increment
                bearingRef.current = (bearingRef.current + rotationSpeed) % 360
                map.setBearing(bearingRef.current)

                // Continue the animation
                rotationAnimationRef.current = requestAnimationFrame(animate)
            } catch (error) {
                console.error("Error during rotation animation:", error)
                if (rotationAnimationRef.current) {
                    cancelAnimationFrame(rotationAnimationRef.current)
                    rotationAnimationRef.current = null
                }
            }
        }

        // Start the animation loop
        if (rotationAnimationRef.current) {
            cancelAnimationFrame(rotationAnimationRef.current)
            rotationAnimationRef.current = null
        }
        rotationAnimationRef.current = requestAnimationFrame(animate)
    }

    return null
}
