import { Show, createEffect, createSignal, onCleanup, onMount } from "solid-js";
import { useTheme } from "../providers/theme-provider";
import {
  DEFAULT_MAP_STYLE_ID,
  resolveMapTileStyle,
  validateCoordinates,
  type MapCoordinates,
} from "../lib/map";

declare global {
  interface Window {
    L?: any;
  }
}

interface LocationMapProps {
  coordinates: MapCoordinates;
  mapStyle?: string;
  customTileUrl?: string;
  markerColor?: string;
  markerLabel?: string;
  height?: number;
  zoom?: number;
  interactive?: boolean;
  class?: string;
}

let leafletPromise: Promise<any> | null = null;

function loadLeaflet(): Promise<any> {
  if (window.L) return Promise.resolve(window.L);
  if (leafletPromise) return leafletPromise;

  leafletPromise = new Promise((resolve, reject) => {
    if (!document.getElementById("leaflet-css")) {
      const link = document.createElement("link");
      link.id = "leaflet-css";
      link.rel = "stylesheet";
      link.href = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.css";
      document.head.appendChild(link);
    }

    const existingScript = document.getElementById("leaflet-js") as HTMLScriptElement | null;
    if (existingScript) {
      existingScript.addEventListener("load", () => resolve(window.L));
      existingScript.addEventListener("error", () => reject(new Error("Map library failed to load")));
      return;
    }

    const script = document.createElement("script");
    script.id = "leaflet-js";
    script.src = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.js";
    script.async = true;
    script.onload = () => resolve(window.L);
    script.onerror = () => reject(new Error("Map library failed to load"));
    document.head.appendChild(script);
  });

  return leafletPromise;
}

function safeMarkerColor(color: string | undefined): string {
  const fallback = "#a65c3e";
  if (!color) return fallback;
  const trimmed = color.trim();
  return /^#[0-9a-fA-F]{3,8}$/.test(trimmed) ? trimmed : fallback;
}

function escapeHtml(value: string | undefined): string {
  return (value ?? "")
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#039;");
}

function createMarkerIcon(leaflet: any, color: string | undefined) {
  const safeColor = safeMarkerColor(color);
  return leaflet.divIcon({
    className: "bookra-map-marker",
    html: `<span style="--bookra-marker-color:${safeColor}"></span>`,
    iconSize: [34, 42],
    iconAnchor: [17, 40],
    popupAnchor: [0, -38],
  });
}

export function LocationMap(props: LocationMapProps) {
  let mapElement: HTMLDivElement | undefined;
  let map: any;
  let tileLayer: any;
  let marker: any;
  let leaflet: any;

  const [isReady, setIsReady] = createSignal(false);
  const [error, setError] = createSignal<string | null>(null);

  const theme = useTheme();
  const isDark = () => theme.resolvedTheme() === "dark";

  const zoom = () => props.zoom ?? props.coordinates.zoom ?? 15;
  const resolvedStyleId = () => {
    const styleId = props.mapStyle ?? DEFAULT_MAP_STYLE_ID;
    if (styleId === "positron" && isDark()) return "dark";
    return styleId;
  };
  const mapStyle = () => resolveMapTileStyle(resolvedStyleId(), props.customTileUrl);
  const popupContent = () => {
    const label = props.markerLabel ?? props.coordinates.address;
    const address = props.coordinates.address && props.coordinates.address !== label ? props.coordinates.address : "";
    if (!label && !address) return "";
    return [
      label ? `<strong>${escapeHtml(label)}</strong>` : "",
      address ? `<span>${escapeHtml(address)}</span>` : "",
    ].filter(Boolean).join("<br>");
  };

  onMount(() => {
    loadLeaflet()
      .then((loadedLeaflet) => {
        leaflet = loadedLeaflet;
        setIsReady(true);
      })
      .catch((loadError: Error) => {
        setError(loadError.message);
      });
  });

  createEffect(() => {
    if (!isReady() || !leaflet || !mapElement || map) return;
    const { latitude, longitude } = props.coordinates;
    if (!validateCoordinates(latitude, longitude)) {
      setError("Invalid map coordinates");
      return;
    }

    map = leaflet.map(mapElement, {
      center: [latitude, longitude],
      zoom: zoom(),
      scrollWheelZoom: false,
      dragging: props.interactive !== false,
      touchZoom: props.interactive !== false,
      doubleClickZoom: props.interactive !== false,
      zoomControl: props.interactive !== false,
    });

    const style = mapStyle();
    tileLayer = leaflet.tileLayer(style.url, {
      attribution: style.attribution,
      className: style.tileClassName ?? "",
      maxZoom: 19,
    }).addTo(map);

    marker = leaflet
      .marker([latitude, longitude], {
        icon: createMarkerIcon(leaflet, props.markerColor),
      })
      .addTo(map);

    const popup = popupContent();
    if (popup) marker.bindPopup(popup);

    setTimeout(() => map?.invalidateSize(), 120);
  });

  createEffect(() => {
    if (!map || !marker) return;
    const { latitude, longitude } = props.coordinates;
    if (!validateCoordinates(latitude, longitude)) return;

    map.setView([latitude, longitude], zoom());
    marker.setLatLng([latitude, longitude]);
    marker.setIcon(createMarkerIcon(leaflet, props.markerColor));

    const popup = popupContent();
    if (popup) {
      marker.bindPopup(popup);
    } else {
      marker.unbindPopup();
    }
  });

  createEffect(() => {
    if (!map || !leaflet) return;
    const style = mapStyle();
    if (tileLayer) map.removeLayer(tileLayer);
    tileLayer = leaflet.tileLayer(style.url, {
      attribution: style.attribution,
      className: style.tileClassName ?? "",
      maxZoom: 19,
    }).addTo(map);
  });

  onCleanup(() => {
    if (map) {
      map.remove();
      map = null;
    }
  });

  return (
    <div
      class={`bookra-location-map relative overflow-hidden rounded-2xl border border-border shadow-sm bg-canvas-subtle ${props.class ?? ""}`}
      style={{ height: `${props.height ?? 360}px` }}
      role="img"
      aria-label={props.coordinates.address ? `Map for ${props.coordinates.address}` : "Location map"}
    >
      <div ref={mapElement} class="absolute inset-0" />
      <Show when={!isReady() && !error()}>
        <div class="absolute inset-0 grid place-items-center bg-canvas-subtle text-sm text-ink-muted">
          Loading map...
        </div>
      </Show>
      <Show when={error()}>
        <div class="absolute inset-0 grid place-items-center bg-error-subtle px-6 text-center text-sm text-error">
          {error()}
        </div>
      </Show>
    </div>
  );
}
