"use client";

import { useEffect, useRef, useCallback } from "react";
import maplibregl from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";

import { initDrawing } from "@/lib/drawingEngine";
import { initSelect } from "@/lib/selectingEngine";
import { initPoint } from "@/lib/pointEngine";
import { createEditingEngine } from "@/lib/editingEngine";
import { FeatureCollection, Geometry } from "@/lib/useEditorState";

type MapProps = {
    mode: "idle" | "draw" | "select" | "add-point";
    draft: FeatureCollection;
    onCreateFeature: (feature: FeatureCollection["features"][number]) => void;
    onDeleteFeature: (id: string | number) => void;
    onUpdateFeature: (id: string | number, geometry: Geometry) => void;
};

export default function Map({ mode, draft, onCreateFeature, onDeleteFeature, onUpdateFeature }: MapProps) {
    const mapRef = useRef<maplibregl.Map | null>(null);
    const modeRef = useRef<MapProps["mode"]>(mode);
    const draftRef = useRef<FeatureCollection>(draft);
    const onCreateRef = useRef(onCreateFeature);
    const onDeleteRef = useRef(onDeleteFeature);
    const onUpdateRef = useRef(onUpdateFeature);

    const editingEngineRef = useRef<ReturnType<typeof createEditingEngine> | null>(null);

    useEffect(() => {
        modeRef.current = mode;
    }, [mode]);

    useEffect(() => {
        draftRef.current = draft;
    }, [draft]);

    useEffect(() => {
        onCreateRef.current = onCreateFeature;
    }, [onCreateFeature]);

    useEffect(() => {
        onDeleteRef.current = onDeleteFeature;
    }, [onDeleteFeature]);

    useEffect(() => {
        onUpdateRef.current = onUpdateFeature;
    }, [onUpdateFeature]);

    useEffect(() => {
        if (!editingEngineRef.current) {
            editingEngineRef.current = createEditingEngine({
                mapRef,
                onUpdate: (id, geometry) => onUpdateRef.current?.(id, geometry),
            });
        }
    }, []);

    /**
     * Push given draft into map sources (idempotent).
     * Always clear feature-state to avoid stale selection overlays after undo/replace.
     */
    const applyDraftToMap = useCallback((fc: FeatureCollection) => {
        const map = mapRef.current;
        if (!map) return;

        const countriesSource = map.getSource("countries") as maplibregl.GeoJSONSource | undefined;
        const placesSource = map.getSource("places") as maplibregl.GeoJSONSource | undefined;

        if (!countriesSource || !placesSource) return;

        // clear all feature-state (selection) to prevent ghost layers after undo
        map.removeFeatureState({ source: "countries" });

        const { polygons, points } = splitDraftFeatures(fc);

        countriesSource.setData(polygons);
        placesSource.setData(points);
    }, []);

    useEffect(() => {
        const map = new maplibregl.Map({
            container: "map",
            attributionControl: false,
            style: {
                version: 8,
                sources: {
                    base: {
                        type: "vector",
                        tiles: ["http://localhost:3000/tiles/{z}/{x}/{y}"],
                        minzoom: 0,
                        maxzoom: 6,
                    },
                },
                layers: [
                    {
                        id: "background",
                        type: "background",
                        paint: {
                            "background-color": "#0b1220",
                        },
                    },
                    {
                        id: "land",
                        type: "fill",
                        source: "base",
                        "source-layer": "land",
                        paint: {
                            "fill-color": "#1e293b",
                        },
                    },
                ],
            },
            center: [0, 20],
            zoom: 2,
        });

        mapRef.current = map;

        map.on("load", async () => {
            // preview (drawing)
            map.addSource("draw-preview", {
                type: "geojson",
                data: {
                    type: "FeatureCollection",
                    features: [],
                },
            });

            map.addLayer({
                id: "draw-preview-fill",
                type: "fill",
                source: "draw-preview",
                paint: {
                    "fill-color": "#22c55e",
                    "fill-opacity": 0.4,
                },
            });

            map.addLayer({
                id: "draw-preview-line",
                type: "line",
                source: "draw-preview",
                paint: {
                    "line-color": "#16a34a",
                    "line-width": 2,
                },
            });

            // data thật
            map.addSource("countries", {
                type: "geojson",
                data: {
                    type: "FeatureCollection",
                    features: [],
                },
                promoteId: "id",

            });

            map.addLayer({
                id: "countries-fill",
                type: "fill",
                source: "countries",
                paint: {
                    "fill-color": [
                        "case",
                        ["boolean", ["feature-state", "selected"], false],
                        "#22c55e", // selected
                        "#f59e0b", // normal
                    ],
                    "fill-opacity": 0.5,
                },
            });

            map.addLayer({
                id: "countries-line",
                type: "line",
                source: "countries",
                paint: {
                    "line-color": "#fbbf24",
                    "line-width": 2,
                },
            });

            map.addSource("places", {
                type: "geojson",
                data: {
                    type: "FeatureCollection",
                    features: [],
                },
            });

            // editing overlays
            map.addSource("edit-shape", {
                type: "geojson",
                data: { type: "FeatureCollection", features: [] },
            });
            map.addSource("edit-handles", {
                type: "geojson",
                data: { type: "FeatureCollection", features: [] },
            });

            map.addLayer({
                id: "edit-shape-line",
                type: "line",
                source: "edit-shape",
                paint: {
                    "line-color": "#38bdf8",
                    "line-width": 3,
                },
            });

            map.addLayer({
                id: "edit-handles-circle",
                type: "circle",
                source: "edit-handles",
                paint: {
                    "circle-color": "#f97316",
                    "circle-radius": 12,
                    "circle-stroke-color": "#0f172a",
                    "circle-stroke-width": 3,
                },
            });

            // load icon từ /public
            map.loadImage("/point.png", (err, image) => {
                if (err) throw err;

                if (!map.hasImage("point-icon")) {
                    map.addImage("point-icon", image);
                }
            });

            // layer
            map.addLayer({
                id: "places-symbol",
                type: "symbol",
                source: "places",
                layout: {
                    "icon-image": "point-icon",
                    "icon-size": 0.5,
                    "icon-anchor": "bottom",
                },
            });

            // init drawing
            const cleanup = initDrawing(
                map,
                () => modeRef.current,
                (geometry: Geometry) => {
                    const id = crypto.randomUUID ? crypto.randomUUID() : Date.now();
                    onCreateRef.current({
                        type: "Feature",
                        properties: { id, kind: "country" },
                        geometry,
                    });
                }
            );

            const cleanupSelect = initSelect(
                map,
                () => modeRef.current,
                (id: string | number) => {
                    // ensure edit overlays are cleared when a feature gets removed
                    editingEngineRef.current?.clearEditing();
                    onDeleteRef.current(id);
                },
                (feature) => editingEngineRef.current?.beginEditing(feature)
            );

            const cleanupPoint = initPoint(
                map,
                () => modeRef.current,
                (geometry: Geometry) => {
                    const id = crypto.randomUUID ? crypto.randomUUID() : Date.now();
                    onCreateRef.current({
                        type: "Feature",
                        properties: { id, kind: "place" },
                        geometry,
                    });
                }
            );

            map.on("remove", cleanupPoint);

            map.on("remove", cleanupSelect);

            map.on("remove", cleanup);

            // after everything mounted, push current draft to sources
            applyDraftToMap(draftRef.current);

            editingEngineRef.current?.bindEditEvents(map);
        });

        return () => map.remove();
    }, [applyDraftToMap]);

    // sync draft -> map sources and drop edit overlays if feature vanished
    useEffect(() => {
        applyDraftToMap(draft);
        const editingId = editingEngineRef.current?.editingRef.current?.id;
        if (editingId !== undefined && editingId !== null) {
            const stillExists = draft.features.some((f) => f.properties.id === editingId);
            if (!stillExists) {
                editingEngineRef.current?.clearEditing();
            }
        }
    }, [draft, applyDraftToMap]);

    return <div id="map" style={{ flex: 1, height: "100vh" }} />;
}

function splitDraftFeatures(fc: FeatureCollection) {
    const polygons = {
        type: "FeatureCollection",
        features: fc.features.filter((f) => f.geometry.type !== "Point"),
    } as FeatureCollection;

    const points = {
        type: "FeatureCollection",
        features: fc.features.filter((f) => f.geometry.type === "Point"),
    } as FeatureCollection;

    return { polygons, points };
}
