"use client";

import { useRef, useEffect, useState, useCallback, useImperativeHandle, forwardRef } from "react";
import maplibregl from "maplibre-gl";
import { cn } from "@/shared/lib/utils";

/* Ensure MapLibre CSS is loaded — static import fails with next/dynamic + standalone.
   Load from /public to avoid CDN/CSP issues. */
if (typeof document !== "undefined") {
  const LINK_ID = "maplibre-gl-css";
  if (!document.getElementById(LINK_ID)) {
    const link = document.createElement("link");
    link.id = LINK_ID;
    link.rel = "stylesheet";
    link.href = "/maplibre-gl.css";
    document.head.appendChild(link);
  }
}
import type { BasemapId, ClickedFeature, LayerVisibility, SelectedFeature } from "../types";

/* ------------------------------------------------------------------ */
/*  Constants                                                          */
/* ------------------------------------------------------------------ */

/**
 * Martin tile URL — relative /tiles is proxied by Next.js rewrite (dev)
 * or Traefik (production). Falls back to env var if set.
 */
const DEFAULT_MARTIN_URL = process.env.NEXT_PUBLIC_MARTIN_URL || "/tiles";

/** Default center: Romania roughly centered */
const DEFAULT_CENTER: [number, number] = [23.8, 46.1];
const DEFAULT_ZOOM = 7;

/** Source/layer IDs used on the map */
const SOURCES = {
  uats: "gis_uats",
  terenuri: "gis_terenuri",
  cladiri: "gis_cladiri",
} as const;

/** Map layer IDs (prefixed to avoid collisions) */
const LAYER_IDS = {
  uatsFill: "layer-uats-fill",
  uatsLine: "layer-uats-line",
  uatsLabel: "layer-uats-label",
  terenuriFill: "layer-terenuri-fill",
  terenuriLine: "layer-terenuri-line",
  cladiriFill: "layer-cladiri-fill",
  cladiriLine: "layer-cladiri-line",
  selectionFill: "layer-selection-fill",
  selectionLine: "layer-selection-line",
} as const;

/** Basemap tile definitions */
const BASEMAP_TILES: Record<BasemapId, { tiles: string[]; attribution: string; tileSize: number; maxzoom?: number }> = {
  osm: {
    tiles: [
      "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png",
      "https://b.tile.openstreetmap.org/{z}/{x}/{y}.png",
      "https://c.tile.openstreetmap.org/{z}/{x}/{y}.png",
    ],
    attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
    tileSize: 256,
  },
  satellite: {
    tiles: [
      "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
    ],
    attribution: '&copy; <a href="https://www.esri.com">Esri</a>, Maxar, Earthstar Geographics',
    tileSize: 256,
  },
  topo: {
    tiles: [
      "https://a.tile.opentopomap.org/{z}/{x}/{y}.png",
      "https://b.tile.opentopomap.org/{z}/{x}/{y}.png",
      "https://c.tile.opentopomap.org/{z}/{x}/{y}.png",
    ],
    attribution:
      '&copy; <a href="https://opentopomap.org">OpenTopoMap</a> (<a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a>)',
    tileSize: 256,
    maxzoom: 17,
  },
};

/* ------------------------------------------------------------------ */
/*  Props                                                              */
/* ------------------------------------------------------------------ */

export type MapViewerHandle = {
  getMap: () => maplibregl.Map | null;
  setLayerVisibility: (visibility: LayerVisibility) => void;
  flyTo: (center: [number, number], zoom?: number) => void;
  clearSelection: () => void;
};

type MapViewerProps = {
  center?: [number, number];
  zoom?: number;
  martinUrl?: string;
  className?: string;
  basemap?: BasemapId;
  selectionMode?: boolean;
  onFeatureClick?: (feature: ClickedFeature) => void;
  onSelectionChange?: (features: SelectedFeature[]) => void;
  /** External layer visibility control */
  layerVisibility?: LayerVisibility;
};

/* ------------------------------------------------------------------ */
/*  Helpers                                                            */
/* ------------------------------------------------------------------ */

function formatPopupContent(properties: Record<string, unknown>): string {
  const rows: string[] = [];
  for (const [key, value] of Object.entries(properties)) {
    if (value == null || value === "") continue;
    const displayKey = key.replace(/_/g, " ");
    rows.push(
      `<tr><td style="font-weight:600;padding:2px 8px 2px 0;vertical-align:top;white-space:nowrap;color:#64748b">${displayKey}</td><td style="padding:2px 0">${String(value)}</td></tr>`
    );
  }
  if (rows.length === 0) return "<p style='color:#94a3b8'>Fara atribute</p>";
  return `<table style="font-size:13px;line-height:1.4">${rows.join("")}</table>`;
}

/* ------------------------------------------------------------------ */
/*  Component                                                          */
/* ------------------------------------------------------------------ */

export const MapViewer = forwardRef<MapViewerHandle, MapViewerProps>(
  function MapViewer(
    {
      center,
      zoom,
      martinUrl,
      className,
      basemap = "osm",
      selectionMode = false,
      onFeatureClick,
      onSelectionChange,
      layerVisibility,
    },
    ref
  ) {
    const containerRef = useRef<HTMLDivElement>(null);
    const mapRef = useRef<maplibregl.Map | null>(null);
    const popupRef = useRef<maplibregl.Popup | null>(null);
    const selectedRef = useRef<Map<string, SelectedFeature>>(new Map());
    const selectionModeRef = useRef(selectionMode);
    const [mapReady, setMapReady] = useState(false);

    // Keep ref in sync
    selectionModeRef.current = selectionMode;

    // MapLibre web workers can't resolve relative URLs — need absolute
    const resolvedMartinUrl = (() => {
      const raw = martinUrl ?? DEFAULT_MARTIN_URL;
      if (raw.startsWith("http")) return raw;
      if (typeof window !== "undefined") return `${window.location.origin}${raw}`;
      return raw;
    })();

    /* ---- Selection helpers ---- */
    const updateSelectionFilter = useCallback(() => {
      const map = mapRef.current;
      if (!map) return;
      const ids = Array.from(selectedRef.current.keys());
      // Use objectId matching for the selection highlight layer
      const filter: maplibregl.FilterSpecification =
        ids.length > 0
          ? ["in", ["to-string", ["get", "object_id"]], ["literal", ids]]
          : ["==", "object_id", "__NONE__"];

      try {
        if (map.getLayer(LAYER_IDS.selectionFill)) {
          map.setFilter(LAYER_IDS.selectionFill, filter);
        }
        if (map.getLayer(LAYER_IDS.selectionLine)) {
          map.setFilter(LAYER_IDS.selectionLine, filter);
        }
      } catch {
        // layers might not exist yet
      }
    }, []);

    const clearSelection = useCallback(() => {
      selectedRef.current.clear();
      updateSelectionFilter();
      onSelectionChange?.([]);
    }, [updateSelectionFilter, onSelectionChange]);

    /* ---- Imperative handle ---- */
    useImperativeHandle(ref, () => ({
      getMap: () => mapRef.current,
      setLayerVisibility: (vis: LayerVisibility) => {
        applyLayerVisibility(vis);
      },
      flyTo: (c: [number, number], z?: number) => {
        mapRef.current?.flyTo({ center: c, zoom: z ?? 14, duration: 1500 });
      },
      clearSelection,
    }));

    /* ---- Apply layer visibility ---- */
    const applyLayerVisibility = useCallback((vis: LayerVisibility) => {
      const map = mapRef.current;
      if (!map || !map.isStyleLoaded()) return;

      const mapping: Record<string, string[]> = {
        uats: [LAYER_IDS.uatsFill, LAYER_IDS.uatsLine, LAYER_IDS.uatsLabel],
        terenuri: [LAYER_IDS.terenuriFill, LAYER_IDS.terenuriLine],
        cladiri: [LAYER_IDS.cladiriFill, LAYER_IDS.cladiriLine],
      };

      for (const [group, layerIds] of Object.entries(mapping)) {
        const visible = vis[group] !== false; // default visible
        for (const lid of layerIds) {
          try {
            map.setLayoutProperty(lid, "visibility", visible ? "visible" : "none");
          } catch {
            // layer might not exist yet
          }
        }
      }
    }, []);

    /* ---- Sync external visibility prop ---- */
    useEffect(() => {
      if (mapReady && layerVisibility) {
        applyLayerVisibility(layerVisibility);
      }
    }, [mapReady, layerVisibility, applyLayerVisibility]);

    /* ---- Map initialization (recreates on basemap change) ---- */
    useEffect(() => {
      if (!containerRef.current) return;

      const initialBasemap = BASEMAP_TILES[basemap];

      const map = new maplibregl.Map({
        container: containerRef.current,
        style: {
          version: 8,
          sources: {
            basemap: {
              type: "raster",
              tiles: initialBasemap.tiles,
              tileSize: initialBasemap.tileSize,
              attribution: initialBasemap.attribution,
            },
          },
          layers: [
            {
              id: "basemap-tiles",
              type: "raster",
              source: "basemap",
              minzoom: 0,
              maxzoom: initialBasemap.maxzoom ?? 19,
            },
          ],
        },
        center: center ?? DEFAULT_CENTER,
        zoom: zoom ?? DEFAULT_ZOOM,
        maxZoom: initialBasemap.maxzoom ?? 20,
      });

      mapRef.current = map;

      /* ---- Controls ---- */
      map.addControl(new maplibregl.NavigationControl(), "top-right");
      map.addControl(new maplibregl.ScaleControl({ unit: "metric" }), "bottom-left");

      /* ---- Add Martin sources + layers on load ---- */
      map.on("load", () => {
        // --- UAT boundaries ---
        map.addSource(SOURCES.uats, {
          type: "vector",
          tiles: [`${resolvedMartinUrl}/${SOURCES.uats}/{z}/{x}/{y}`],
          minzoom: 0,
          maxzoom: 16,
        });

        map.addLayer({
          id: LAYER_IDS.uatsFill,
          type: "fill",
          source: SOURCES.uats,
          "source-layer": SOURCES.uats,
          paint: {
            "fill-color": "#8b5cf6",
            "fill-opacity": 0.05,
          },
        });

        map.addLayer({
          id: LAYER_IDS.uatsLine,
          type: "line",
          source: SOURCES.uats,
          "source-layer": SOURCES.uats,
          paint: {
            "line-color": "#7c3aed",
            "line-width": 1.5,
          },
        });

        map.addLayer({
          id: LAYER_IDS.uatsLabel,
          type: "symbol",
          source: SOURCES.uats,
          "source-layer": SOURCES.uats,
          layout: {
            "text-field": ["coalesce", ["get", "name"], ["get", "uat_name"], ""],
            "text-size": 12,
            "text-anchor": "center",
            "text-allow-overlap": false,
          },
          paint: {
            "text-color": "#5b21b6",
            "text-halo-color": "#ffffff",
            "text-halo-width": 1.5,
          },
        });

        // --- Terenuri (parcels) ---
        map.addSource(SOURCES.terenuri, {
          type: "vector",
          tiles: [`${resolvedMartinUrl}/${SOURCES.terenuri}/{z}/{x}/{y}`],
          minzoom: 10,
          maxzoom: 18,
        });

        map.addLayer({
          id: LAYER_IDS.terenuriFill,
          type: "fill",
          source: SOURCES.terenuri,
          "source-layer": SOURCES.terenuri,
          minzoom: 13,
          paint: {
            "fill-color": "#22c55e",
            "fill-opacity": 0.15,
          },
        });

        map.addLayer({
          id: LAYER_IDS.terenuriLine,
          type: "line",
          source: SOURCES.terenuri,
          "source-layer": SOURCES.terenuri,
          minzoom: 13,
          paint: {
            "line-color": "#15803d",
            "line-width": 0.8,
          },
        });

        // --- Cladiri (buildings) ---
        map.addSource(SOURCES.cladiri, {
          type: "vector",
          tiles: [`${resolvedMartinUrl}/${SOURCES.cladiri}/{z}/{x}/{y}`],
          minzoom: 12,
          maxzoom: 18,
        });

        map.addLayer({
          id: LAYER_IDS.cladiriFill,
          type: "fill",
          source: SOURCES.cladiri,
          "source-layer": SOURCES.cladiri,
          minzoom: 14,
          paint: {
            "fill-color": "#3b82f6",
            "fill-opacity": 0.5,
          },
        });

        map.addLayer({
          id: LAYER_IDS.cladiriLine,
          type: "line",
          source: SOURCES.cladiri,
          "source-layer": SOURCES.cladiri,
          minzoom: 14,
          paint: {
            "line-color": "#1e3a5f",
            "line-width": 0.6,
          },
        });

        // --- Selection highlight layer (uses same sources) ---
        // We add a highlight layer on top for terenuri (primary selection target)
        map.addLayer({
          id: LAYER_IDS.selectionFill,
          type: "fill",
          source: SOURCES.terenuri,
          "source-layer": SOURCES.terenuri,
          minzoom: 13,
          filter: ["==", "object_id", "__NONE__"], // empty initially
          paint: {
            "fill-color": "#f59e0b",
            "fill-opacity": 0.5,
          },
        });

        map.addLayer({
          id: LAYER_IDS.selectionLine,
          type: "line",
          source: SOURCES.terenuri,
          "source-layer": SOURCES.terenuri,
          minzoom: 13,
          filter: ["==", "object_id", "__NONE__"],
          paint: {
            "line-color": "#d97706",
            "line-width": 2.5,
          },
        });

        // Apply initial visibility if provided
        if (layerVisibility) {
          applyLayerVisibility(layerVisibility);
        }

        setMapReady(true);
      });

      /* ---- Click handler ---- */
      const clickableLayers = [
        LAYER_IDS.terenuriFill,
        LAYER_IDS.cladiriFill,
        LAYER_IDS.uatsFill,
      ];

      map.on("click", (e) => {
        const features = map.queryRenderedFeatures(e.point, {
          layers: clickableLayers.filter((l) => {
            try {
              return !!map.getLayer(l);
            } catch {
              return false;
            }
          }),
        });

        // Close existing popup
        if (popupRef.current) {
          popupRef.current.remove();
          popupRef.current = null;
        }

        if (features.length === 0) return;

        const first = features[0];
        if (!first) return;

        const props = (first.properties ?? {}) as Record<string, unknown>;
        const sourceLayer = first.sourceLayer ?? first.source ?? "";

        // Selection mode: toggle feature in selection
        if (selectionModeRef.current && sourceLayer === SOURCES.terenuri) {
          const objectId = String(props.object_id ?? props.objectId ?? "");
          if (!objectId) return;

          if (selectedRef.current.has(objectId)) {
            selectedRef.current.delete(objectId);
          } else {
            selectedRef.current.set(objectId, {
              id: objectId,
              sourceLayer,
              properties: props,
            });
          }
          updateSelectionFilter();
          onSelectionChange?.(Array.from(selectedRef.current.values()));
          return;
        }

        // Notify parent
        if (onFeatureClick) {
          onFeatureClick({
            layerId: first.layer?.id ?? "",
            sourceLayer,
            properties: props,
            coordinates: [e.lngLat.lng, e.lngLat.lat],
          });
        }

        // Show popup (only in non-selection mode)
        const popup = new maplibregl.Popup({
          maxWidth: "360px",
          closeButton: true,
          closeOnClick: true,
        })
          .setLngLat(e.lngLat)
          .setHTML(formatPopupContent(props))
          .addTo(map);

        popupRef.current = popup;
      });

      /* ---- Cursor change on hover ---- */
      for (const lid of clickableLayers) {
        map.on("mouseenter", lid, () => {
          map.getCanvas().style.cursor = "pointer";
        });
        map.on("mouseleave", lid, () => {
          map.getCanvas().style.cursor = "";
        });
      }

      /* ---- Cleanup ---- */
      return () => {
        if (popupRef.current) {
          popupRef.current.remove();
          popupRef.current = null;
        }
        map.remove();
        mapRef.current = null;
        setMapReady(false);
      };
      // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [resolvedMartinUrl, basemap]);

    /* ---- Sync center/zoom prop changes ---- */
    useEffect(() => {
      if (!mapReady || !mapRef.current) return;
      if (center) {
        mapRef.current.flyTo({
          center,
          zoom: zoom ?? mapRef.current.getZoom(),
          duration: 1500,
        });
      }
    }, [center, zoom, mapReady]);

    return (
      <div className={cn("absolute inset-0", className)}>
        <div ref={containerRef} className="w-full h-full" />
        {!mapReady && (
          <div className="absolute inset-0 flex items-center justify-center bg-muted/50">
            <p className="text-sm text-muted-foreground">Se incarca harta...</p>
          </div>
        )}
      </div>
    );
  }
);
