"use client";

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

import { getRasterTileTemplateUrl, getVectorTileTemplateUrl } from "@/api/tiles";
import { initDrawing } from "@/lib/drawingEngine";
import { initSelect } from "@/lib/selectingEngine";
import { initPoint } from "@/lib/pointEngine";
import { initLine } from "@/lib/lineEngine";
import { initPath } from "@/lib/pathEngine";
import { initCircle } from "@/lib/circleEngine";
import { createEditingEngine } from "@/lib/editingEngine";
import { FeatureCollection, Geometry } from "@/lib/useEditorState";
import { BACKGROUND_LAYER_OPTIONS, BackgroundLayerVisibility } from "@/lib/backgroundLayers";

type MapProps = {
    mode: "idle" | "draw" | "select" | "add-point" | "add-line" | "add-path" | "add-circle";
    draft: FeatureCollection;
    backgroundVisibility: BackgroundLayerVisibility;
    selectedFeatureId: string | number | null;
    onSelectFeatureId: (id: string | number | null) => void;
    onCreateFeature?: (feature: FeatureCollection["features"][number]) => void;
    onDeleteFeature?: (id: string | number) => void;
    onUpdateFeature?: (id: string | number, geometry: Geometry) => void;
    allowGeometryEditing?: boolean;
    respectBindingFilter?: boolean;
    height?: CSSProperties["height"];
};

const DEFAULT_POINT_ICON_ID = "point-icon-default";
const POINT_ICON_URL = "/point.png";
const PATH_ARROW_ICON_ID = "path-arrow-icon";
const MAP_MIN_ZOOM = 1;
const MAP_MAX_ZOOM = 10;
const RASTER_BASE_SOURCE_ID = "rasterBase";
const RASTER_BASE_LAYER_ID = "raster-base-layer";
const RASTER_BASE_INSERT_BEFORE_LAYER_ID = "graticules-line";
const COUNTRY_COLOR_KEY_EXPRESSION: maplibregl.ExpressionSpecification = [
    "coalesce",
    ["get", "MAPCOLOR7"],
    ["get", "MAPCOLOR9"],
    ["get", "scalerank"],
    0,
];
const COUNTRY_FILL_COLOR_EXPRESSION: maplibregl.ExpressionSpecification = [
    "match",
    COUNTRY_COLOR_KEY_EXPRESSION,
    1, "#ef4444",
    2, "#f97316",
    3, "#f59e0b",
    4, "#22c55e",
    5, "#06b6d4",
    6, "#3b82f6",
    7, "#8b5cf6",
    8, "#a855f7",
    9, "#d946ef",
    10, "#14b8a6",
    "#64748b",
];

const POLYGON_FILL_BY_TYPE: Record<string, string> = {
    country: "#2563eb",
    state: "#0ea5e9",
    empire: "#f59e0b",
    kingdom: "#d97706",
    war: "#dc2626",
    battle: "#f43f5e",
    civilization: "#14b8a6",
    rebellion_zone: "#7c3aed",
};

const POLYGON_STROKE_BY_TYPE: Record<string, string> = {
    country: "#1e3a8a",
    state: "#0c4a6e",
    empire: "#7c2d12",
    kingdom: "#9a3412",
    war: "#7f1d1d",
    battle: "#9f1239",
    civilization: "#134e4a",
    rebellion_zone: "#4c1d95",
};

const POLYGON_OPACITY_BY_TYPE: Record<string, number> = {
    war: 0.3,
    battle: 0.34,
    civilization: 0.38,
    rebellion_zone: 0.32,
};

const LINE_COLOR_BY_TYPE: Record<string, string> = {
    defense_line: "#f97316",
    attack_route: "#ef4444",
    retreat_route: "#94a3b8",
    invasion_route: "#b91c1c",
    migration_route: "#0ea5e9",
    refugee_route: "#06b6d4",
    trade_route: "#eab308",
    shipping_route: "#2563eb",
};

const PATH_RENDER_BY_TYPE: Record<string, boolean> = {
    attack_route: true,
    retreat_route: true,
    invasion_route: true,
    migration_route: true,
    refugee_route: true,
    trade_route: true,
    shipping_route: true,
};

export default function Map({
    mode,
    draft,
    backgroundVisibility,
    selectedFeatureId,
    onSelectFeatureId,
    onCreateFeature,
    onDeleteFeature,
    onUpdateFeature,
    allowGeometryEditing = true,
    respectBindingFilter = true,
    height = "100vh",
}: MapProps) {
    const mapRef = useRef<maplibregl.Map | null>(null);
    const modeRef = useRef<MapProps["mode"]>(mode);
    const draftRef = useRef<FeatureCollection>(draft);
    const backgroundVisibilityRef = useRef<BackgroundLayerVisibility>(backgroundVisibility);
    const selectedFeatureIdRef = useRef<string | number | null>(selectedFeatureId);
    const onSelectFeatureIdRef = useRef(onSelectFeatureId);
    const onCreateRef = useRef<MapProps["onCreateFeature"]>(onCreateFeature);
    const onDeleteRef = useRef<MapProps["onDeleteFeature"]>(onDeleteFeature);
    const onUpdateRef = useRef<MapProps["onUpdateFeature"]>(onUpdateFeature);
    const [zoomLevel, setZoomLevel] = useState(2);
    const [zoomBounds, setZoomBounds] = useState({ min: MAP_MIN_ZOOM, max: MAP_MAX_ZOOM });

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

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

    useEffect(() => {
        const map = mapRef.current;
        if (!map || !map.isStyleLoaded()) return;
        if (mode !== "add-line") {
            (map.getSource("draw-line-preview") as maplibregl.GeoJSONSource | undefined)?.setData({
                type: "FeatureCollection",
                features: [],
            });
        }
        if (mode !== "add-path") {
            (map.getSource("draw-path-preview") as maplibregl.GeoJSONSource | undefined)?.setData({
                type: "FeatureCollection",
                features: [],
            });
        }
        if (mode !== "add-circle") {
            (map.getSource("draw-circle-preview") as maplibregl.GeoJSONSource | undefined)?.setData({
                type: "FeatureCollection",
                features: [],
            });
        }
    }, [mode]);

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

    useEffect(() => {
        selectedFeatureIdRef.current = selectedFeatureId;
    }, [selectedFeatureId]);

    useEffect(() => {
        onSelectFeatureIdRef.current = onSelectFeatureId;
    }, [onSelectFeatureId]);

    useEffect(() => {
        backgroundVisibilityRef.current = backgroundVisibility;
        const map = mapRef.current;
        if (!map || !map.isStyleLoaded()) return;
        applyBackgroundLayerVisibility(map, backgroundVisibility);
    }, [backgroundVisibility]);

    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 visibleDraft = respectBindingFilter
            ? filterDraftByBinding(fc, selectedFeatureIdRef.current)
            : fc;
        const { polygons, points } = splitDraftFeatures(visibleDraft);

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

    useEffect(() => {
        const map = new maplibregl.Map({
            container: "map",
            attributionControl: false,
            minZoom: MAP_MIN_ZOOM,
            maxZoom: MAP_MAX_ZOOM,
            style: {
                version: 8,
                sources: {
                    base: {
                        type: "vector",
                        tiles: [getVectorTileTemplateUrl()],
                        minzoom: 0,
                        maxzoom: 6,
                    },
                },
                layers: [
                    {
                        id: "background",
                        type: "background",
                        paint: {
                            "background-color": "#0b1220",
                        },
                    },
                    {
                        id: "graticules-line",
                        type: "line",
                        source: "base",
                        "source-layer": "graticules",
                        paint: {
                            "line-color": "#334155",
                            "line-width": [
                                "interpolate",
                                ["linear"],
                                ["zoom"],
                                0, 0.3,
                                4, 0.6,
                                6, 0.8,
                            ],
                            "line-opacity": 0.55,
                        },
                    },
                    {
                        id: "land",
                        type: "fill",
                        source: "base",
                        "source-layer": "land",
                        paint: {
                            "fill-color": "#1e293b",
                            "fill-opacity": 0.25,
                        },
                    },
                    {
                        id: "bg-countries-fill",
                        type: "fill",
                        source: "base",
                        "source-layer": "countries",
                        paint: {
                            "fill-color": COUNTRY_FILL_COLOR_EXPRESSION,
                            "fill-opacity": 0.38,
                        },
                    },
                    {
                        id: "bg-country-borders-line",
                        type: "line",
                        source: "base",
                        "source-layer": "country_borders",
                        paint: {
                            "line-color": "#cbd5e1",
                            "line-width": [
                                "interpolate",
                                ["linear"],
                                ["zoom"],
                                0, 0.2,
                                4, 0.5,
                                6, 1.1,
                            ],
                            "line-opacity": 0.85,
                        },
                    },
                    {
                        id: "regions-line",
                        type: "line",
                        source: "base",
                        "source-layer": "regions",
                        paint: {
                            "line-color": "#475569",
                            "line-width": [
                                "interpolate",
                                ["linear"],
                                ["zoom"],
                                0, 0.2,
                                4, 0.6,
                                6, 1,
                            ],
                            "line-opacity": 0.6,
                        },
                    },
                    {
                        id: "lakes-fill",
                        type: "fill",
                        source: "base",
                        "source-layer": "lakes",
                        paint: {
                            "fill-color": "#1d4ed8",
                            "fill-opacity": 0.45,
                        },
                    },
                    {
                        id: "rivers-line",
                        type: "line",
                        source: "base",
                        "source-layer": "rivers",
                        paint: {
                            "line-color": "#38bdf8",
                            "line-width": [
                                "interpolate",
                                ["linear"],
                                ["zoom"],
                                0, 0.25,
                                4, 0.8,
                                6, 1.5,
                            ],
                            "line-opacity": 0.85,
                        },
                    },
                    {
                        id: "geolines-line",
                        type: "line",
                        source: "base",
                        "source-layer": "geolines",
                        paint: {
                            "line-color": "#94a3b8",
                            "line-width": 1.2,
                            "line-opacity": 0.8,
                        },
                    },
                ],
            },
            center: [0, 20],
            zoom: 2,
        });

        mapRef.current = map;

        map.on("load", async () => {
            const syncZoomLevel = () => {
                setZoomLevel(roundZoom(map.getZoom()));
            };

            applyBackgroundLayerVisibility(map, backgroundVisibilityRef.current);
            const hasPathArrowIcon = ensurePathArrowIcon(map);
            setZoomBounds({ min: MAP_MIN_ZOOM, max: MAP_MAX_ZOOM });
            syncZoomLevel();
            map.on("zoom", syncZoomLevel);

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

            map.addSource("draw-circle-preview", {
                type: "geojson",
                data: {
                    type: "FeatureCollection",
                    features: [],
                },
            });

            map.addLayer({
                id: "draw-circle-preview-fill",
                type: "fill",
                source: "draw-circle-preview",
                paint: {
                    "fill-color": "#0ea5e9",
                    "fill-opacity": 0.25,
                },
            });

            map.addLayer({
                id: "draw-circle-preview-line",
                type: "line",
                source: "draw-circle-preview",
                paint: {
                    "line-color": "#0284c7",
                    "line-width": 2,
                    "line-opacity": 0.95,
                },
            });

            map.addSource("draw-line-preview", {
                type: "geojson",
                data: {
                    type: "FeatureCollection",
                    features: [],
                },
            });

            map.addLayer({
                id: "draw-line-preview-line",
                type: "line",
                source: "draw-line-preview",
                paint: {
                    "line-color": "#38bdf8",
                    "line-width": 3,
                    "line-opacity": 0.9,
                    "line-dasharray": [1.2, 0.9],
                },
            });

            map.addSource("draw-path-preview", {
                type: "geojson",
                data: {
                    type: "FeatureCollection",
                    features: [],
                },
            });

            map.addLayer({
                id: "draw-path-preview-line",
                type: "line",
                source: "draw-path-preview",
                paint: {
                    "line-color": "#38bdf8",
                    "line-width": 3,
                    "line-opacity": 0.9,
                    "line-dasharray": [1.2, 0.9],
                },
            });

            if (hasPathArrowIcon) {
                map.addLayer({
                    id: "draw-path-preview-arrows",
                    type: "symbol",
                    source: "draw-path-preview",
                    layout: {
                        "symbol-placement": "line",
                        "symbol-spacing": 56,
                        "icon-image": PATH_ARROW_ICON_ID,
                        "icon-size": 0.45,
                        "icon-allow-overlap": true,
                        "icon-ignore-placement": true,
                    },
                });
            }

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

            });

            map.addLayer({
                id: "countries-fill",
                type: "fill",
                source: "countries",
                filter: ["==", ["geometry-type"], "Polygon"],
                paint: {
                    "fill-color": [
                        "case",
                        ["boolean", ["feature-state", "selected"], false],
                        "#22c55e", // selected
                        [
                            "==",
                            ["coalesce", ["get", "entity_id"], ""],
                            "",
                        ],
                        "#ef4444", // no entity
                        buildTypeMatchExpression(POLYGON_FILL_BY_TYPE, "#f59e0b"),
                    ],
                    "fill-opacity": [
                        "case",
                        ["boolean", ["feature-state", "selected"], false],
                        0.6,
                        buildTypeMatchExpression(POLYGON_OPACITY_BY_TYPE, 0.5),
                    ],
                },
            });

            map.addLayer({
                id: "countries-line",
                type: "line",
                source: "countries",
                filter: ["==", ["geometry-type"], "Polygon"],
                paint: {
                    "line-color": [
                        "case",
                        ["boolean", ["feature-state", "selected"], false],
                        "#14532d",
                        buildTypeMatchExpression(POLYGON_STROKE_BY_TYPE, "#fbbf24"),
                    ],
                    "line-width": 2,
                },
            });

            map.addLayer({
                id: "routes-line",
                type: "line",
                source: "countries",
                filter: ["==", ["geometry-type"], "LineString"],
                paint: {
                    "line-color": [
                        "case",
                        ["boolean", ["feature-state", "selected"], false],
                        "#22c55e",
                        ["==", ["coalesce", ["get", "entity_id"], ""], ""],
                        "#ef4444",
                        buildTypeMatchExpression(LINE_COLOR_BY_TYPE, "#38bdf8"),
                    ],
                    "line-width": [
                        "interpolate",
                        ["linear"],
                        ["zoom"],
                        1, 2.2,
                        4, 3.2,
                        6, 4.2,
                    ],
                    "line-opacity": 0.9,
                },
            });

            if (hasPathArrowIcon) {
                map.addLayer({
                    id: "routes-arrow",
                    type: "symbol",
                    source: "countries",
                    filter: [
                        "all",
                        ["==", ["geometry-type"], "LineString"],
                        buildTypeMatchExpression(PATH_RENDER_BY_TYPE, false),
                    ],
                    layout: {
                        "symbol-placement": "line",
                        "symbol-spacing": 60,
                        "icon-image": PATH_ARROW_ICON_ID,
                        "icon-size": 0.5,
                        "icon-allow-overlap": true,
                        "icon-ignore-placement": true,
                    },
                });
            }

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

            // fallback layer so points are still visible even if icon cannot be loaded
            map.addLayer({
                id: "places-circle",
                type: "circle",
                source: "places",
                paint: {
                    "circle-color": "#ef4444",
                    "circle-radius": 4,
                    "circle-stroke-color": "#ffffff",
                    "circle-stroke-width": 1,
                    "circle-opacity": 0.85,
                },
            });

            addPointSymbolLayer(map);

            // init drawing
            const cleanup = initDrawing(
                map,
                () => modeRef.current,
                (geometry: Geometry) => {
                    const id = crypto.randomUUID ? crypto.randomUUID() : Date.now();
                    onCreateRef.current?.({
                        type: "Feature",
                        properties: {
                            id,
                            type: null,
                            geometry_preset: "polygon",
                            entity_id: null,
                            entity_ids: [],
                            entity_name: null,
                            entity_type_id: null,
                            binding: [],
                        },
                        geometry,
                    });
                }
            );

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

            const cleanupPoint = initPoint(
                map,
                () => modeRef.current,
                (geometry: Geometry) => {
                    const id = crypto.randomUUID ? crypto.randomUUID() : Date.now();
                    onCreateRef.current?.({
                        type: "Feature",
                        properties: {
                            id,
                            type: null,
                            geometry_preset: "point",
                            entity_id: null,
                            entity_ids: [],
                            entity_name: null,
                            entity_type_id: null,
                            binding: [],
                        },
                        geometry,
                    });
                }
            );

            const cleanupLine = initLine(
                map,
                () => modeRef.current,
                (geometry: Geometry) => {
                    const id = crypto.randomUUID ? crypto.randomUUID() : Date.now();
                    onCreateRef.current?.({
                        type: "Feature",
                        properties: {
                            id,
                            type: "defense_line",
                            geometry_preset: "line",
                            entity_id: null,
                            entity_ids: [],
                            entity_name: null,
                            entity_type_id: null,
                            binding: [],
                        },
                        geometry,
                    });
                }
            );

            const cleanupPath = initPath(
                map,
                () => modeRef.current,
                (geometry: Geometry) => {
                    const id = crypto.randomUUID ? crypto.randomUUID() : Date.now();
                    onCreateRef.current?.({
                        type: "Feature",
                        properties: {
                            id,
                            type: "attack_route",
                            geometry_preset: "line",
                            entity_id: null,
                            entity_ids: [],
                            entity_name: null,
                            entity_type_id: null,
                            binding: [],
                        },
                        geometry,
                    });
                }
            );

            const cleanupCircle = initCircle(
                map,
                () => modeRef.current,
                (geometry: Geometry) => {
                    const id = crypto.randomUUID ? crypto.randomUUID() : Date.now();
                    onCreateRef.current?.({
                        type: "Feature",
                        properties: {
                            id,
                            type: null,
                            geometry_preset: "circle-area",
                            entity_id: null,
                            entity_ids: [],
                            entity_name: null,
                            entity_type_id: null,
                            binding: [],
                        },
                        geometry,
                    });
                }
            );

            map.on("remove", cleanupCircle);
            map.on("remove", cleanupPath);
            map.on("remove", cleanupLine);
            map.on("remove", cleanupPoint);

            map.on("remove", cleanupSelect);

            map.on("remove", cleanup);
            map.on("remove", () => map.off("zoom", syncZoomLevel));

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

            if (allowGeometryEditing) {
                editingEngineRef.current?.bindEditEvents(map);
            }
        });

        return () => {
            if (mapRef.current === map) {
                mapRef.current = null;
            }
            map.remove();
        };
    }, [allowGeometryEditing, applyDraftToMap]);

    const handleZoomByStep = (delta: number) => {
        const map = mapRef.current;
        if (!map) return;
        const next = clampNumber(zoomLevel + delta, zoomBounds.min, zoomBounds.max);
        map.easeTo({ zoom: next, duration: 120 });
    };

    const handleZoomSliderChange = (nextRaw: number) => {
        const map = mapRef.current;
        if (!map || !Number.isFinite(nextRaw)) return;
        const next = clampNumber(nextRaw, zoomBounds.min, zoomBounds.max);
        map.easeTo({ zoom: next, duration: 80 });
    };

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

    return (
        <div style={{ width: "100%", height, position: "relative" }}>
            <div id="map" style={{ width: "100%", height: "100%" }} />

            <div
                style={{
                    position: "absolute",
                    top: "10px",
                    left: "16px",
                    right: "16px",
                    zIndex: 12,
                    pointerEvents: "none",
                }}
            >
                <div
                    style={{
                        maxWidth: "520px",
                        margin: "0 auto",
                        display: "flex",
                        alignItems: "center",
                        gap: "10px",
                        background: "rgba(15, 23, 42, 0.88)",
                        border: "1px solid rgba(148, 163, 184, 0.38)",
                        borderRadius: "999px",
                        padding: "8px 12px",
                        color: "#e2e8f0",
                        backdropFilter: "blur(3px)",
                        pointerEvents: "auto",
                    }}
                >
                    <button
                        type="button"
                        onClick={() => handleZoomByStep(-0.8)}
                        style={zoomButtonStyle}
                        aria-label="Zoom out"
                    >
                        -
                    </button>

                    <input
                        type="range"
                        min={zoomBounds.min}
                        max={zoomBounds.max}
                        step={0.1}
                        value={zoomLevel}
                        onChange={(event) => handleZoomSliderChange(Number(event.target.value))}
                        style={{
                            flex: 1,
                            accentColor: "#38bdf8",
                            cursor: "pointer",
                        }}
                        aria-label="Map zoom"
                    />

                    <button
                        type="button"
                        onClick={() => handleZoomByStep(0.8)}
                        style={zoomButtonStyle}
                        aria-label="Zoom in"
                    >
                        +
                    </button>

                    <div
                        style={{
                            minWidth: "56px",
                            textAlign: "right",
                            fontSize: "12px",
                            color: "#cbd5e1",
                            fontVariantNumeric: "tabular-nums",
                        }}
                    >
                        {zoomLevel.toFixed(1)}x
                    </div>
                </div>
            </div>
        </div>
    );
}

function applyBackgroundLayerVisibility(
    map: maplibregl.Map,
    visibility: BackgroundLayerVisibility
) {
    syncRasterBaseVisibility(map, visibility[RASTER_BASE_LAYER_ID]);

    for (const layer of BACKGROUND_LAYER_OPTIONS) {
        if (layer.id === RASTER_BASE_LAYER_ID) continue;
        if (!map.getLayer(layer.id)) continue;
        map.setLayoutProperty(
            layer.id,
            "visibility",
            visibility[layer.id] ? "visible" : "none"
        );
    }
}

function syncRasterBaseVisibility(map: maplibregl.Map, shouldShow: boolean) {
    if (shouldShow) {
        ensureRasterBaseLayer(map);
        return;
    }

    removeRasterBaseLayer(map);
}

function ensureRasterBaseLayer(map: maplibregl.Map) {
    if (!map.getSource(RASTER_BASE_SOURCE_ID)) {
        map.addSource(RASTER_BASE_SOURCE_ID, createRasterBaseSource());
    }

    if (!map.getLayer(RASTER_BASE_LAYER_ID)) {
        const beforeId = map.getLayer(RASTER_BASE_INSERT_BEFORE_LAYER_ID)
            ? RASTER_BASE_INSERT_BEFORE_LAYER_ID
            : undefined;
        map.addLayer(createRasterBaseLayer(), beforeId);
    }

    map.setLayoutProperty(RASTER_BASE_LAYER_ID, "visibility", "visible");
}

function removeRasterBaseLayer(map: maplibregl.Map) {
    if (map.getLayer(RASTER_BASE_LAYER_ID)) {
        map.removeLayer(RASTER_BASE_LAYER_ID);
    }

    if (map.getSource(RASTER_BASE_SOURCE_ID)) {
        map.removeSource(RASTER_BASE_SOURCE_ID);
    }
}

function createRasterBaseSource() {
    return {
        type: "raster" as const,
        tiles: [getRasterTileTemplateUrl()],
        tileSize: 256,
        minzoom: 0,
        maxzoom: 6,
    };
}

function createRasterBaseLayer() {
    return {
        id: RASTER_BASE_LAYER_ID,
        type: "raster" as const,
        source: RASTER_BASE_SOURCE_ID,
        paint: {
            "raster-opacity": 0.92,
            "raster-resampling": "linear" as const,
        },
    };
}

function filterDraftByBinding(
    fc: FeatureCollection,
    selectedFeatureId: string | number | null
): FeatureCollection {
    const selectedId = selectedFeatureId !== null ? String(selectedFeatureId) : null;
    if (selectedId === null) {
        return {
            ...fc,
            features: fc.features.filter((feature) => !normalizeBindingIds(feature.properties.binding).length),
        };
    }

    return {
        ...fc,
        features: fc.features.filter((feature) => {
            const featureId = String(feature.properties.id);
            if (featureId === selectedId) return true;
            const bindingIds = normalizeBindingIds(feature.properties.binding);
            if (!bindingIds.length) return true;
            return bindingIds.includes(selectedId);
        }),
    };
}

function normalizeBindingIds(rawBinding: unknown): string[] {
    if (!Array.isArray(rawBinding)) return [];
    const deduped: string[] = [];
    const seen = new Set<string>();
    for (const rawId of rawBinding) {
        if (typeof rawId !== "string" && typeof rawId !== "number") continue;
        const id = String(rawId).trim();
        if (!id || seen.has(id)) continue;
        seen.add(id);
        deduped.push(id);
    }
    return deduped;
}

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

function ensurePathArrowIcon(map: maplibregl.Map): boolean {
    if (map.hasImage(PATH_ARROW_ICON_ID)) return true;
    const imageData = createPathArrowImageData();
    if (!imageData) return false;
    map.addImage(PATH_ARROW_ICON_ID, imageData, { pixelRatio: 2 });
    return true;
}

function createPathArrowImageData(): ImageData | null {
    const size = 56;
    const canvas = document.createElement("canvas");
    canvas.width = size;
    canvas.height = size;
    const ctx = canvas.getContext("2d");
    if (!ctx) return null;

    ctx.clearRect(0, 0, size, size);

    ctx.strokeStyle = "#0f172a";
    ctx.fillStyle = "#38bdf8";
    ctx.lineWidth = 4;
    ctx.lineJoin = "round";
    ctx.lineCap = "round";

    ctx.beginPath();
    ctx.moveTo(8, 16);
    ctx.lineTo(28, 16);
    ctx.lineTo(28, 10);
    ctx.lineTo(46, 28);
    ctx.lineTo(28, 46);
    ctx.lineTo(28, 40);
    ctx.lineTo(8, 40);
    ctx.closePath();
    ctx.fill();
    ctx.stroke();

    return ctx.getImageData(0, 0, size, size);
}

function addPointSymbolLayer(map: maplibregl.Map) {
    void ensurePointAssetIcon(map).then((hasPointIcon) => {
        if (!hasPointIcon || !map.getSource("places") || map.getLayer("places-symbol")) return;

        map.addLayer({
            id: "places-symbol",
            type: "symbol",
            source: "places",
            layout: {
                "icon-image": DEFAULT_POINT_ICON_ID,
                "icon-size": 0.06,
                "icon-anchor": "center",
                "icon-allow-overlap": true,
            },
        });

        if (map.getLayer("places-circle")) {
            map.setLayoutProperty("places-circle", "visibility", "none");
        }
    });
}

async function ensurePointAssetIcon(map: maplibregl.Map): Promise<boolean> {
    if (map.hasImage(DEFAULT_POINT_ICON_ID)) return true;

    try {
        const image = await map.loadImage(POINT_ICON_URL);
        if (!map.hasImage(DEFAULT_POINT_ICON_ID)) {
            map.addImage(DEFAULT_POINT_ICON_ID, image.data);
        }
        return true;
    } catch (error) {
        console.error(`Failed to load point icon asset: ${POINT_ICON_URL}`, error);
        return false;
    }
}

function buildTypeMatchExpression(
    valueByType: Record<string, string | number | boolean>,
    fallback: string | number | boolean
): maplibregl.ExpressionSpecification {
    const expression: unknown[] = ["match", getFeatureTypeExpression()];

    for (const [typeId, value] of Object.entries(valueByType)) {
        expression.push(typeId, value);
    }

    expression.push(fallback);
    return expression as maplibregl.ExpressionSpecification;
}

function getFeatureTypeExpression(): maplibregl.ExpressionSpecification {
    return [
        "coalesce",
        ["get", "type"],
        ["get", "entity_type_id"],
        "",
    ] as maplibregl.ExpressionSpecification;
}

function roundZoom(value: number): number {
    return Math.round(value * 10) / 10;
}

function clampNumber(value: number, min: number, max: number): number {
    if (value < min) return min;
    if (value > max) return max;
    return value;
}

const zoomButtonStyle: React.CSSProperties = {
    width: "28px",
    height: "28px",
    borderRadius: "999px",
    border: "1px solid #334155",
    background: "#1e293b",
    color: "#f8fafc",
    fontSize: "18px",
    lineHeight: "1",
    cursor: "pointer",
    display: "grid",
    placeItems: "center",
};
