"use client";

import { useEffect, useRef, useState } from "react";
import "leaflet/dist/leaflet.css";
import type { Map as LMap, LayerGroup } from "leaflet";
import { ApiError } from "@/lib/api/client";
import { getPois, type Poi } from "@/lib/api/map";

/** Legend groups. Colors follow the home map legend. `types` are backend POI type keys. */
const GROUPS = [
  { id: "destinations", label: "Destinations", color: "#2A1B3D", types: ["destination", "neighborhood"] },
  { id: "culture", label: "Culture and fun", color: "#F59E0B", types: ["cultural_venue", "entertainment_activity", "celebration_venue"] },
  { id: "stays", label: "Stays", color: "#5B3FD9", types: ["accommodation"] },
  { id: "food", label: "Food and drink", color: "#C92A62", types: ["restaurant", "cafe"] },
  { id: "services", label: "Services", color: "#6F6580", types: ["airport", "transport_provider", "embassy", "medical_destination", "medical_center"] },
] as const;

const COLOR = Object.fromEntries(GROUPS.flatMap((g) => g.types.map((t) => [t, g.color])));

/** Detail page for a POI type, where one exists. */
function detailHref(p: Poi): string | null {
  switch (p.type) {
    case "destination":
    case "neighborhood":
    case "cultural_venue":
      return `/explore/place?kind=${p.type.replace("_", "-")}&id=${p.id}`;
    case "accommodation":
      return `/stays/details?id=${p.id}`;
    case "restaurant":
      return `/life-and-fun/details/restaurant?id=${p.id}`;
    case "cafe":
      return `/life-and-fun/details/cafe?id=${p.id}`;
    case "entertainment_activity":
      return `/life-and-fun/details/experience?id=${p.id}`;
    default:
      return null;
  }
}

function popup(p: Poi): HTMLElement {
  const box = document.createElement("div");
  box.className = "mp-pop";
  const name = document.createElement("strong");
  name.textContent = p.name;
  box.append(name);
  const where = p.address ?? p.city?.name;
  if (where) {
    const w = document.createElement("span");
    w.textContent = where;
    box.append(w);
  }
  const href = detailHref(p);
  if (href) {
    const a = document.createElement("a");
    a.href = href;
    a.textContent = "View details";
    box.append(a);
  }
  return box;
}

const SYRIA: [[number, number], [number, number]] = [[32.3, 35.6], [37.3, 42.4]];

/** Interactive map. Markers load for the visible area and reload (debounced) after every pan or zoom. */
export default function PoiMap() {
  const el = useRef<HTMLDivElement>(null);
  const [active, setActive] = useState<string[]>(GROUPS.map((g) => g.id));
  const [count, setCount] = useState<number | null>(null);
  const [error, setError] = useState<string | null>(null);
  const activeRef = useRef(active);
  const refresh = useRef<() => void>(() => {});

  // Map lives for the component's lifetime; leaflet touches window, so it is imported inside the effect.
  useEffect(() => {
    let map: LMap | null = null;
    let layer: LayerGroup;
    let timer = 0;
    let controller: AbortController | null = null;
    let dead = false;

    void import("leaflet").then((L) => {
      if (dead || !el.current) return;
      map = L.map(el.current, { zoomControl: true, scrollWheelZoom: true }).fitBounds(SYRIA);
      L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", { maxZoom: 18, attribution: "&copy; OpenStreetMap contributors" }).addTo(map);
      layer = L.layerGroup().addTo(map);

      const load = async () => {
        if (!map) return;
        controller?.abort();
        const mine = new AbortController();
        controller = mine;
        const b = map.getBounds();
        const on = GROUPS.filter((g) => activeRef.current.includes(g.id));
        try {
          if (on.length === 0) {
            layer.clearLayers();
            setCount(0);
            return;
          }
          const types = on.length === GROUPS.length ? null : on.flatMap((g) => g.types);
          const pois = await getPois({ north: b.getNorth(), south: b.getSouth(), east: b.getEast(), west: b.getWest() }, types, mine.signal);
          if (mine.signal.aborted) return;
          layer.clearLayers();
          for (const p of pois) {
            L.circleMarker([p.latitude, p.longitude], { radius: 8, weight: 2, color: "#fff", fillColor: COLOR[p.type] ?? "#2A1B3D", fillOpacity: 1 })
              .bindPopup(popup(p))
              .bindTooltip(p.name)
              .addTo(layer);
          }
          setCount(pois.length);
          setError(null);
        } catch (err) {
          if (mine.signal.aborted) return;
          setError(err instanceof ApiError ? err.message : "The map could not load places.");
        }
      };
      refresh.current = () => {
        window.clearTimeout(timer);
        timer = window.setTimeout(load, 300);
      };
      map.on("moveend", refresh.current);
      void load();
    });

    return () => {
      dead = true;
      window.clearTimeout(timer);
      controller?.abort();
      map?.remove();
    };
  }, []);

  const toggle = (id: string) => {
    const next = active.includes(id) ? active.filter((x) => x !== id) : [...active, id];
    activeRef.current = next;
    setActive(next);
    refresh.current();
  };

  return (
    <div className="mp">
      <div className="mp-bar">
        <div className="lf-group" role="group" aria-label="Place types">
          {GROUPS.map((g) => (
            <button key={g.id} type="button" className="lf-chip mp-chip" aria-pressed={active.includes(g.id)} onClick={() => toggle(g.id)}>
              <i style={{ background: g.color }} aria-hidden="true" />
              {g.label}
            </button>
          ))}
        </div>
        <p className="lf-count" aria-live="polite">
          {error ?? (count === null ? "Loading" : `${count} ${count === 1 ? "place" : "places"} in view`)}
        </p>
      </div>
      <div className="mp-map" ref={el} role="region" aria-label="Map of places in Syria" />
    </div>
  );
}
