---
import { getImage } from "astro:assets";
import type { ImageMetadata } from "astro";
import PhotosSubNav from "@/components/PhotosSubNav.astro";
import BaseLayout from "@/layouts/BaseLayout.astro";

type PhotoSidecar = {
  id: string;
  title: string[];
  image: string;
  alt: string;
  location: string;
  date: string;
  tags: string[];
  exif: {
    camera: string;
    lens: string;
    aperture: string;
    iso: string;
    focal_length: string;
    shutter_speed: string;
  };
};

type MarkerData = {
  id: string;
  collectionSlug: string;
  lat: number;
  lng: number;
  thumbSrc: string;
  thumbWidth: number;
  thumbHeight: number;
  alt: string;
  title: string;
};

function parseDMS(dms: string): { lat: number; lng: number } | null {
  const re =
    /(\d+)\s*deg\s*(\d+)'\s*([\d.]+)"\s*([NS])\s*,\s*(\d+)\s*deg\s*(\d+)'\s*([\d.]+)"\s*([EW])/;
  const m = dms.match(re);
  if (!m) return null;
  const [, d1, mi1, s1, dir1, d2, mi2, s2, dir2] = m;
  if (!d1 || !mi1 || !s1 || !dir1 || !d2 || !mi2 || !s2 || !dir2) return null;
  const toDD = (d: string, min: string, sec: string, dir: string): number => {
    const dd = Number(d) + Number(min) / 60 + Number(sec) / 3600;
    return dir === "S" || dir === "W" ? -dd : dd;
  };
  return {
    lat: toDD(d1, mi1, s1, dir1),
    lng: toDD(d2, mi2, s2, dir2),
  };
}

const sidecars = import.meta.glob<PhotoSidecar>(
  "/src/content/photos/collections/**/*.json",
  { eager: true },
);

const imageModules = import.meta.glob<{ default: ImageMetadata }>(
  "/src/content/photos/collections/**/*.jpg",
  { eager: true },
);

const photos = Object.entries(sidecars)
  .map(([jsonPath, sidecar]) => {
    const imgPath = jsonPath.replace(".json", ".jpg");
    const imageModule = imageModules[imgPath];
    const collectionSlug = jsonPath
      .replace("/src/content/photos/collections/", "")
      .replace(/\/img\/.*$/, "");
    return { sidecar, image: imageModule?.default, collectionSlug };
  })
  .filter(
    (
      p,
    ): p is {
      sidecar: PhotoSidecar;
      image: ImageMetadata;
      collectionSlug: string;
    } => !!p.image,
  );

const markers: MarkerData[] = (
  await Promise.all(
    photos.map(async ({ sidecar, image, collectionSlug }) => {
      const coords = parseDMS(sidecar.location);
      if (!coords) return null;
      const thumb = await getImage({ src: image, width: 240, format: "jpeg" });
      return {
        id: sidecar.id,
        collectionSlug,
        lat: coords.lat,
        lng: coords.lng,
        thumbSrc: thumb.src,
        thumbWidth: thumb.attributes.width as number,
        thumbHeight: thumb.attributes.height as number,
        alt: sidecar.alt,
        title: sidecar.title[0] ?? sidecar.id,
      } satisfies MarkerData;
    }),
  )
).filter((m): m is MarkerData => m !== null);

const markersJson = JSON.stringify(markers);
---

<BaseLayout
  title="Map"
  description="All photo locations mapped — travel and street photography by Adrian Altner."
  navDesktopHidden={true}
  footerDark={true}
>
  <div class="page">
    <main class="main">
      <div class="nav-area">
        <PhotosSubNav />
      </div>
      <div id="map" class="map-container"></div>
    </main>
  </div>
  <script
    is:inline
    type="application/json"
    id="map-data"
    set:html={markersJson}
  />
</BaseLayout>

<script>
  import "leaflet/dist/leaflet.css";
  import "leaflet.markercluster/dist/MarkerCluster.css";
  import "leaflet.markercluster/dist/MarkerCluster.Default.css";
  import L from "leaflet";
  import "leaflet.markercluster";

  type MarkerData = {
    id: string;
    collectionSlug: string;
    lat: number;
    lng: number;
    thumbSrc: string;
    thumbWidth: number;
    thumbHeight: number;
    alt: string;
    title: string;
  };

  const raw = document.getElementById("map-data")?.textContent ?? "[]";
  const markers: MarkerData[] = JSON.parse(raw);

  const map = L.map("map", { zoomControl: true });

  L.tileLayer("https://tiles-eu.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.png", {
    attribution:
      '&copy; <a href="https://stadiamaps.com/" target="_blank">Stadia Maps</a> &copy; <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> &copy; <a href="https://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>',
    maxZoom: 20,
    opacity: 0.7,
  }).addTo(map);

  const cluster = L.markerClusterGroup({
    iconCreateFunction: (c) =>
      L.divIcon({
        html: `<div class="cluster-icon">${c.getChildCount()}</div>`,
        className: "",
        iconSize: [36, 36],
      }),
    showCoverageOnHover: false,
    maxClusterRadius: 60,
  });

  for (const m of markers) {
    const popup = `<div class="map-popup">
      <a href="/photos/collections/${m.collectionSlug}/${m.id}" class="map-popup__link">
        <img src="${m.thumbSrc}" width="${m.thumbWidth}" height="${m.thumbHeight}" alt="${m.alt.replace(/"/g, "&quot;")}" loading="lazy" class="map-popup__img" />
        <p class="map-popup__title">${m.title}</p>
      </a>
    </div>`;

    L.circleMarker([m.lat, m.lng], {
      radius: 7,
      color: "#e8587a",
      fillColor: "#e8587a",
      fillOpacity: 0.85,
      weight: 2,
    })
      .bindPopup(popup, { maxWidth: 240 })
      .addTo(cluster);
  }

  cluster.addTo(map);

  if (markers.length > 0) {
    const bounds = L.latLngBounds(markers.map((m) => [m.lat, m.lng]));
    map.fitBounds(bounds, { padding: [40, 40] });
  } else {
    map.setView([10, 105], 5);
  }
</script>

<style>
  .page {
    display: flex;
    flex-direction: column;
    height: 100dvh;
    background: #111;
    color: #f5f5f3;
  }

  .main {
    flex: 1;
    width: 100%;
    min-height: 0;
    display: flex;
    flex-direction: column;
  }

  .nav-area {
    padding: 1.5rem 1.5rem 0;
  }

  /* ── Map ── */
  .map-container {
    flex: 1;
    min-height: 0;
    border-radius: 2px;
    overflow: hidden;
  }

  /* ── Leaflet dark overrides ── */
  :global(.leaflet-popup-content-wrapper) {
    background: #1e1e1e !important;
    color: #f5f5f3 !important;
    border-radius: 4px !important;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.6) !important;
    padding: 0 !important;
  }

  :global(.leaflet-popup-content) {
    margin: 0 !important;
    width: auto !important;
  }

  :global(.leaflet-popup-tip) {
    background: #1e1e1e !important;
  }

  :global(.leaflet-popup-close-button) {
    color: #888 !important;
    top: 6px !important;
    right: 8px !important;
  }

  :global(.leaflet-popup-close-button:hover) {
    color: #f5f5f3 !important;
  }

  :global(.leaflet-control-zoom a) {
    background: #1e1e1e;
    color: #f5f5f3;
    border-color: #333;
  }

  :global(.leaflet-control-zoom a:hover) {
    background: #2a2a2a;
    color: #fff;
  }

  :global(.leaflet-bar) {
    border: none;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
  }

  :global(.leaflet-control-attribution) {
    background: rgba(17, 17, 17, 0.7);
    color: #555;
    font-size: 0.6rem;
    padding: 1px 4px;
  }

  :global(.leaflet-control-attribution a) {
    color: #666;
  }

  /* ── Cluster icon ── */
  :global(.cluster-icon) {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: #e8587a;
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.8rem;
    font-weight: 600;
    font-family: "Exo 2", sans-serif;
    box-shadow: 0 2px 8px rgba(232, 88, 122, 0.4);
  }

  /* ── Popup content ── */
  :global(.map-popup) {
    width: 220px;
  }

  :global(.map-popup__link) {
    display: block;
    text-decoration: none;
    color: inherit;
  }

  :global(.map-popup__img) {
    display: block;
    width: 100%;
    height: 148px;
    object-fit: cover;
    border-radius: 4px 4px 0 0;
  }

  :global(.map-popup__title) {
    font-size: 0.78rem;
    line-height: 1.4;
    padding: 0.5rem 0.6rem 0.6rem;
    color: #f5f5f3 !important;
    margin: 0;
  }

  :global(.map-popup__link:hover .map-popup__title) {
    color: #f5f5f3;
  }

  /* ── Nav / Footer override for dark bg ── */
  .page :global(.nav),
  .page :global(.mobile-bar) {
    background: #111;
    color: #000;
  }

  .page :global(.nav a),
  .page :global(.nav__icon-btn) {
    color: #000;
  }

  .page :global(.footer a) {
    color: #f5f5f3;
  }

  /* ── Mobile ── */
  @media (max-width: 640px) {
    .nav-area {
      padding: 0.5rem 0.5rem 0;
    }
  }
</style>
