<script lang="ts">
  import "leaflet/dist/leaflet.css";
  import { onMount, onDestroy } from "svelte";
  import type { WindArea } from "$lib/windkarte";
  import { useTranslate, T } from "$lib/translations";

  let {
    areas,
    onselect,
    onready = null,
    initialGebietsNr = null,
    hiddenStatuses = [],
    hiddenRings = [],
  }: {
    areas: WindArea[];
    onselect: (area: WindArea | null, center: [number, number] | null) => void;
    onready?: ((ctrl: { reset: () => void }) => void) | null;
    initialGebietsNr?: string | null;
    hiddenStatuses?: string[];
    hiddenRings?: string[];
  } = $props();

  const t = useTranslate();

  // Mirror prop into plain variable so Leaflet event handlers read current value.
  let _hidden: string[] = [];
  $effect(() => {
    _hidden = hiddenStatuses;
    if (!geojsonLayer) return;
    restyleAll(selectedSlug);
    // Deselect if selected area's status was just hidden.
    if (selectedSlug) {
      geojsonLayer.eachLayer((l) => {
        const f = (l as import("leaflet").Path & { feature?: GeoJSON.Feature }).feature;
        if (f?.properties?.name === selectedSlug) {
          const s = f.properties?.status ?? "rechtskraeftig";
          if (_hidden.includes(s)) {
            selectedSlug = null;
            onselect(null, null);
            bufferLayer?.clearLayers();
            if (ring2kmLayer && map?.hasLayer(ring2kmLayer)) map.removeLayer(ring2kmLayer);
            if (ring5kmLayer && map?.hasLayer(ring5kmLayer)) map.removeLayer(ring5kmLayer);
          }
        }
      });
    }
  });

  let mapEl: HTMLDivElement;
  let map: import("leaflet").Map | null = null;
  let bufferLayer: import("leaflet").LayerGroup | null = null;
  let ring2kmLayer: import("leaflet").LayerGroup | null = null;
  let ring5kmLayer: import("leaflet").LayerGroup | null = null;
  let labelLayer: import("leaflet").LayerGroup | null = null;
  let selectedSlug = $state<string | null>(null);
  let geojsonLayer: import("leaflet").GeoJSON | null = null;
  let buffering = $state(false);
  let _hiddenRings: string[] = [];

  $effect(() => {
    _hiddenRings = hiddenRings;
    if (!map) return;
    for (const [key, layer] of [["2km", ring2kmLayer], ["5km", ring5kmLayer]] as [string, import("leaflet").LayerGroup | null][]) {
      if (!layer) continue;
      if (_hiddenRings.includes(key)) {
        if (map.hasLayer(layer)) map.removeLayer(layer);
      } else {
        if (!map.hasLayer(layer)) map.addLayer(layer);
      }
    }
  });

  const COLORS: Record<string, { fill: string; stroke: string }> = {
    rechtskraeftig:           { fill: "#2d7a45", stroke: "#1a4d28" },
    entwurf_2:                { fill: "#b08a52", stroke: "#6e5530" },
    entwurf_2_voraussichtlich:{ fill: "#d4752a", stroke: "#8a4a18" },
    entwurf_3:                { fill: "#436e85", stroke: "#264150" },
  };

  function style(status: string, selected: boolean, hovered = false) {
    const c = COLORS[status] ?? COLORS.rechtskraeftig;
    return {
      color: c.stroke,
      fillColor: c.fill,
      weight: selected ? 2.5 : 1.5,
      fillOpacity: selected ? 0.55 : hovered ? 0.42 : 0.25,
      opacity: 1,
    };
  }

  function restyleAll(sel: string | null) {
    geojsonLayer?.eachLayer((l) => {
      const f = (l as import("leaflet").Path & { feature?: GeoJSON.Feature }).feature;
      if (!f) return;
      const s = f.properties?.status ?? "rechtskraeftig";
      if (_hidden.includes(s)) {
        (l as import("leaflet").Path).setStyle({ opacity: 0, fillOpacity: 0, weight: 0 });
      } else {
        (l as import("leaflet").Path).setStyle(style(s, f.properties?.name === sel));
      }
    });
  }

  async function drawBuffer(L: typeof import("leaflet"), feature: GeoJSON.Feature) {
    buffering = true;
    const { buffer } = await import("@turf/buffer");
    if (bufferLayer) bufferLayer.clearLayers();
    else bufferLayer = L.layerGroup().addTo(map!);

    for (const { m, color, dash } of [
      { m: 200,  color: "#e35651", dash: "4 4" },
      { m: 600,  color: "#b08a52", dash: "6 3" },
      { m: 1000, color: "#436e85", dash: "8 4" },
    ]) {
      const buffered = buffer(feature, m / 1000, { units: "kilometers" });
      if (!buffered) continue;
      L.geoJSON(buffered, {
        style: { color, fillColor: color, fillOpacity: 0.05, weight: 1.5, dashArray: dash },
      }).addTo(bufferLayer!);
    }

    // 2km / 5km — always compute on selection, show/hide controlled by hiddenRings toggle
    if (!ring2kmLayer) ring2kmLayer = L.layerGroup();
    else ring2kmLayer.clearLayers();
    if (!ring5kmLayer) ring5kmLayer = L.layerGroup();
    else ring5kmLayer.clearLayers();

    const b2 = buffer(feature, 2, { units: "kilometers" });
    if (b2) L.geoJSON(b2, { style: { color: "#5e6fa3", fillColor: "#5e6fa3", fillOpacity: 0.05, weight: 1.5, dashArray: "10 5" } }).addTo(ring2kmLayer);
    const b5 = buffer(feature, 5, { units: "kilometers" });
    if (b5) L.geoJSON(b5, { style: { color: "#8a567a", fillColor: "#8a567a", fillOpacity: 0.05, weight: 1.5, dashArray: "14 6" } }).addTo(ring5kmLayer);

    if (!_hiddenRings.includes("2km") && !map!.hasLayer(ring2kmLayer)) map!.addLayer(ring2kmLayer);
    if (!_hiddenRings.includes("5km") && !map!.hasLayer(ring5kmLayer)) map!.addLayer(ring5kmLayer);

    buffering = false;
  }

  async function selectFeature(
    L: typeof import("leaflet"),
    feature: GeoJSON.Feature,
    layer: import("leaflet").Layer,
    cmsArea: WindArea | null,
    zoom = true
  ) {
    const name = feature.properties?.name;
    const prev = selectedSlug;
    selectedSlug = name ?? null;
    restyleAll(selectedSlug);

    const bounds = (layer as import("leaflet").Polygon).getBounds();
    const c = bounds.getCenter();
    const center: [number, number] = [c.lat, c.lng];

    if (cmsArea) {
      onselect(cmsArea, center);
      await drawBuffer(L, feature);
    } else {
      onselect(null, null);
      bufferLayer?.clearLayers();
      if (ring2kmLayer && map!.hasLayer(ring2kmLayer)) map!.removeLayer(ring2kmLayer);
      if (ring5kmLayer && map!.hasLayer(ring5kmLayer)) map!.removeLayer(ring5kmLayer);
    }

    if (zoom && prev !== name) {
      const desktop = window.innerWidth >= 640;
      map!.fitBounds((layer as import("leaflet").Polygon).getBounds(), {
        paddingTopLeft:     [40, 40],
        paddingBottomRight: [desktop ? 320 : 40, desktop ? 40 : 300],
        maxZoom: 11,
      });
    }
  }

  onMount(async () => {
    const L = await import("leaflet");

    const iconUrl    = new URL("leaflet/dist/images/marker-icon.png",    import.meta.url).href;
    const icon2xUrl  = new URL("leaflet/dist/images/marker-icon-2x.png", import.meta.url).href;
    const shadowUrl  = new URL("leaflet/dist/images/marker-shadow.png",  import.meta.url).href;
    L.Icon.Default.mergeOptions({ iconUrl, iconRetinaUrl: icon2xUrl, shadowUrl });

    map = L.map(mapEl, { zoomControl: true, attributionControl: false }).setView([50.62, 10.42], 10);
    L.control.attribution({ position: "bottomleft" }).addTo(map);
    labelLayer = L.layerGroup(); // not added to map yet — shown only at zoom >= 13
    onready?.({ reset: () => map!.setView([50.62, 10.42], 10) });

    L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
      attribution:
        '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>-Mitwirkende | Geodaten: © GDI-Th, dl-de/by-2-0',
      maxZoom: 19,
    }).addTo(map);

    const byNr = Object.fromEntries(areas.map((a) => [a.gebiets_nr, a]));

    // Deselect on background map click
    map.on("click", () => {
      if (!selectedSlug) return;
      selectedSlug = null;
      restyleAll(null);
      onselect(null, null);
      bufferLayer?.clearLayers();
      if (ring2kmLayer && map!.hasLayer(ring2kmLayer)) map!.removeLayer(ring2kmLayer);
      if (ring5kmLayer && map!.hasLayer(ring5kmLayer)) map!.removeLayer(ring5kmLayer);
    });

    try {
      // Build FeatureCollection directly from CMS areas (geometry embedded)
      const geo: GeoJSON.FeatureCollection = {
        type: "FeatureCollection",
        features: areas
          .filter((a) => a.geometry)
          .map((a) => ({
            type: "Feature" as const,
            properties: {
              name: a.gebiets_nr,
              bezeichnung: a.bezeichnung,
              status: a.status ?? "rechtskraeftig",
            },
            geometry: a.geometry!,
          })),
      };

      const layerMap = new Map<string, { feature: GeoJSON.Feature; layer: import("leaflet").Layer }>();

      geojsonLayer = L.geoJSON(geo, {
        style: (f) => {
          const s = f?.properties?.status ?? "rechtskraeftig";
          return style(s, false);
        },
        onEachFeature: (feature, layer) => {
          const p = feature.properties ?? {};
          const area = byNr[p.name] ?? { _slug: "", gebiets_nr: p.name ?? "", status: p.status, bezeichnung: p.bezeichnung };
          feature.properties.cms = area;

          const name = feature.properties?.name ?? "";
          layerMap.set(name, { feature, layer });

          // Hover highlight
          layer.on("mouseover", () => {
            const s = feature.properties?.status ?? "rechtskraeftig";
            if (_hidden.includes(s)) return;
            if (feature.properties?.name !== selectedSlug) {
              (layer as import("leaflet").Path).setStyle(style(s, false, true));
            }
          });
          layer.on("mouseout", () => {
            const s = feature.properties?.status ?? "rechtskraeftig";
            if (_hidden.includes(s)) return;
            if (feature.properties?.name !== selectedSlug) {
              (layer as import("leaflet").Path).setStyle(style(s, false, false));
            }
          });

          layer.on("click", async (e) => {
            const s = feature.properties?.status ?? "rechtskraeftig";
            if (_hidden.includes(s)) return;
            L.DomEvent.stopPropagation(e);
            await selectFeature(L, feature, layer, area);
          });

          // Hover tooltip
          const nrLabel = area.gebiets_nr ?? p.name ?? "";
          const title = area.bezeichnung
            ? `${nrLabel} – ${area.bezeichnung}`
            : nrLabel;
          const lines: string[] = [];
          if (area.flaeche_ha != null) lines.push(`${t(T.windkarte_label_flaeche)}: ${area.flaeche_ha.toFixed(1)} ha`);
          if (area.anlagen_geplant != null) lines.push(`${t(T.windkarte_label_anlagen_geplant)}: ${area.anlagen_geplant}`);
          if (area.hoehe_max_m != null) lines.push(`${t(T.windkarte_label_hoehe_max)}: ${area.hoehe_max_m} m`);
          if (area.gemeinden && area.gemeinden.length > 0) {
            lines.push(`${t(T.windkarte_label_gemeinden)}: ${area.gemeinden.slice(0, 3).join(', ')}${area.gemeinden.length > 3 ? '…' : ''}`);
          }
          const esc = (s: string) => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
          const tooltipHtml =
            `<div class="font-semibold">${esc(title)}</div>` +
            (lines.length ? `<div class="mt-0.5 text-xs opacity-90">${lines.map(esc).join('<br/>')}</div>` : '');
          layer.bindTooltip(tooltipHtml, { sticky: true, className: "windkarte-tooltip" });

          // Permanent label via divIcon marker at polygon center
          const nr = area.gebiets_nr ?? p.name;
          if (nr && labelLayer) {
            const center = (layer as import("leaflet").Polygon).getBounds().getCenter();
            L.marker(center, {
              icon: L.divIcon({
                html: `<span class="windkarte-label">${nr}</span>`,
                className: "windkarte-label-wrap",
                iconSize: [0, 0],
                iconAnchor: [0, 0],
              }),
              interactive: false,
              keyboard: false,
            }).addTo(labelLayer);
          }
        },
      }).addTo(map);

      restyleAll(selectedSlug);

      // Labels: show from zoom 13, scale font proportionally with zoom
      const syncLabels = () => {
        if (!labelLayer || !map) return;
        const z = map.getZoom();
        if (z >= 13) {
          if (!map.hasLayer(labelLayer)) map.addLayer(labelLayer);
          const fontSize = Math.max(8, Math.min(26, Math.round(9 * Math.pow(2, z - 13))));
          labelLayer.eachLayer((l) => {
            const el = (l as import("leaflet").Marker).getElement();
            const span = el?.querySelector<HTMLElement>(".windkarte-label");
            if (span) span.style.fontSize = fontSize + "px";
          });
        } else {
          if (map.hasLayer(labelLayer)) map.removeLayer(labelLayer);
        }
      };
      map.on("zoomend", syncLabels);
      syncLabels();

      // Auto-select from hash
      if (initialGebietsNr) {
        const entry = layerMap.get(initialGebietsNr);
        if (entry) {
          await selectFeature(L, entry.feature, entry.layer, byNr[initialGebietsNr] ?? null);
        }
      }
    } catch (e) {
      console.error("Karte laden fehlgeschlagen:", e);
    }
  });

  onDestroy(() => {
    map?.remove();
    map = null;
  });
</script>

<div bind:this={mapEl} class="h-full w-full"></div>

{#if buffering}
  <div class="pointer-events-none absolute inset-0 flex items-center justify-center z-[600]">
    <div class="rounded-full bg-white/90 p-2.5 shadow-md">
      <svg class="size-5 animate-spin text-stein-500" viewBox="0 0 24 24" fill="none">
        <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"/>
        <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v4l3-3-3-3v4a8 8 0 00-8 8h4z"/>
      </svg>
    </div>
  </div>
{/if}

<style>
  :global(.windkarte-tooltip) {
    background: white;
    border: 1px solid #d5d8d6;
    border-radius: 6px;
    box-shadow: 0 1px 4px rgba(0,0,0,0.15);
    color: #333735;
    font-size: 12px;
    font-weight: 500;
    padding: 4px 8px;
  }
  :global(.windkarte-tooltip::before) {
    display: none;
  }
  :global(.windkarte-label-wrap) {
    background: none !important;
    border: none !important;
    box-shadow: none !important;
  }
  :global(.windkarte-label) {
    background: rgba(255, 255, 255, 0.6);
    opacity: 0.75;
    border-radius: 3px;
    box-shadow: 0 1px 2px rgba(0,0,0,0.12);
    color: #1a1a1a;
    display: inline-block;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.02em;
    padding: 1px 4px;
    pointer-events: none;
    position: absolute;
    transform: translate(-50%, -50%);
    white-space: nowrap;
  }
</style>
