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

const collectionPageSchema = {
  "@context": "https://schema.org",
  "@type": "CollectionPage",
  name: "Photography by Adrian Altner",
  description:
    "A photostream of travel and street photography by Adrian Altner.",
  url: "https://adrian-altner.com/photos",
  author: {
    "@type": "Person",
    name: "Adrian Altner",
    url: "https://adrian-altner.com",
  },
};

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

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\/[^/]+\.json$/, "");
    return { sidecar, image: imageModule?.default, collectionSlug };
  })
  .filter(
    (
      p,
    ): p is {
      sidecar: PhotoSidecar;
      image: ImageMetadata;
      collectionSlug: string;
    } => !!p.image,
  )
  .sort(
    (a, b) =>
      new Date(b.sidecar.date).getTime() - new Date(a.sidecar.date).getTime(),
  );

const BATCH_SIZE = 15;
---

<BaseLayout
  title="Photography"
  description="A photostream of travel and street photography by Adrian Altner."
  navDesktopHidden={true}
  footerDark={true}
>
  <JsonLd schema={collectionPageSchema} slot="head" />
  <div class="page">
    

    <main class="main">
      <PhotosSubNav />
      <div class="photo-grid" id="photo-grid">
        {
          photos.map((photo, i) => {
            const ar = photo.image.width / photo.image.height;
            return (
              <div
                class="photo-item"
                data-ar={ar}
                data-batch={Math.floor(i / BATCH_SIZE)}
                data-hidden={i >= BATCH_SIZE ? "true" : undefined}
              >
                <a href={`/photos/collections/${photo.collectionSlug}/${photo.sidecar.id}`} class="photo-link">
                  <Image
                    src={photo.image}
                    alt={photo.sidecar.alt}
                    width={800}
                    height={Math.round(800 / ar)}
                    loading="lazy"
                    class="photo-img"
                  />
                </a>
              </div>
            );
          })
        }
      </div>

      <div id="sentinel" aria-hidden="true"></div>
      <div id="load-status" class="load-status" aria-live="polite"></div>
    </main>
  </div>
</BaseLayout>

<script>
  import justifiedLayout from "justified-layout";

  const BATCH_SIZE = 15;
  const TARGET_ROW_HEIGHT = 280;
  const BOX_SPACING = 8;

  const grid = document.getElementById("photo-grid")!;
  const allItems = Array.from(
    grid.querySelectorAll<HTMLElement>(".photo-item"),
  );
  const sentinel = document.getElementById("sentinel");
  const status = document.getElementById("load-status");

  let currentBatch = 0;
  const totalBatches =
    Math.ceil(
      allItems.filter((el) => el.hasAttribute("data-hidden")).length /
        BATCH_SIZE,
    ) + 1;

  function applyLayout() {
    if (window.innerWidth <= 640) {
      grid.style.height = "";
      grid.style.position = "";
      for (const item of allItems) {
        if (!item.hasAttribute("data-hidden")) {
          item.style.cssText = "";
        }
      }
      return;
    }

    const visibleItems = allItems.filter(
      (el) => !el.hasAttribute("data-hidden"),
    );
    const ratios = visibleItems.map((el) => Number(el.dataset.ar));

    const result = justifiedLayout(ratios, {
      containerWidth: grid.offsetWidth,
      targetRowHeight: TARGET_ROW_HEIGHT,
      boxSpacing: BOX_SPACING,
      containerPadding: 0,
    });

    grid.style.position = "relative";
    grid.style.height = `${result.containerHeight}px`;

    for (let i = 0; i < visibleItems.length; i++) {
      const box = result.boxes[i];
      const item = visibleItems[i];
      if (!box || !item) continue;
      item.style.position = "absolute";
      item.style.top = `${box.top}px`;
      item.style.left = `${box.left}px`;
      item.style.width = `${box.width}px`;
      item.style.height = `${box.height}px`;
    }
  }

  function revealNextBatch() {
    currentBatch++;
    const toReveal = allItems.filter(
      (el) =>
        el.hasAttribute("data-hidden") &&
        Number(el.dataset.batch) === currentBatch,
    );

    if (toReveal.length === 0) {
      sentinel?.remove();
      return;
    }

    if (status) status.textContent = "Loading more photos…";

    for (const el of toReveal) {
      el.removeAttribute("data-hidden");
      el.classList.add("is-visible");
    }

    applyLayout();

    if (currentBatch >= totalBatches) {
      sentinel?.remove();
    }
    if (status) status.textContent = "";
  }

  let resizeTimer: ReturnType<typeof setTimeout>;
  window.addEventListener("resize", () => {
    clearTimeout(resizeTimer);
    resizeTimer = setTimeout(applyLayout, 150);
  });

  applyLayout();

  if (sentinel) {
    const observer = new IntersectionObserver(
      (entries) => {
        if (entries[0]?.isIntersecting) {
          revealNextBatch();
        }
      },
      { rootMargin: "400px" },
    );
    observer.observe(sentinel);
  }
</script>

<style>
  .page {
    display: grid;
    grid-template-rows: auto 1fr auto;
    min-height: 100dvh;
    background: #111;
    color: #f5f5f3;
  }

  .main {
    max-width: var(--width-wide);
    margin: 0 auto;
    padding: 1.5rem;
    width: 100%;
  }

  /* ── Justified layout (positioned by JS) ── */
  .photo-grid {
    position: relative;
  }

  .photo-item {
    border-radius: 2px;
    overflow: hidden;
    transition: opacity 0.4s ease;
  }

  .photo-item[data-hidden] {
    display: none;
  }

  .photo-item.is-visible {
    animation: fadeIn 0.4s ease forwards;
  }

  @keyframes fadeIn {
    from {
      opacity: 0;
      transform: translateY(8px);
    }
    to {
      opacity: 1;
      transform: translateY(0);
    }
  }

  /* ── Photo card ── */
  .photo-link {
    display: block;
    width: 100%;
    height: 100%;
    overflow: hidden;
  }

  .photo-img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.4s ease;
  }

  .photo-link:hover .photo-img {
    transform: scale(1.03);
  }

  /* ── Sentinel / status ── */
  #sentinel {
    height: 1px;
    margin-top: 2rem;
  }

  .load-status {
    text-align: center;
    padding: 1rem;
    font-size: 0.85rem;
    color: #666;
    min-height: 2rem;
  }

  /* ── 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: simple column, natural aspect ratios ── */
  @media (max-width: 640px) {
    .main {
      padding: 0.25rem;
    }

    .photo-grid {
      display: flex;
      flex-direction: column;
      gap: 0.5rem;
      height: auto !important;
      position: static !important;
    }

    .photo-item {
      position: static !important;
      width: 100% !important;
      height: auto !important;
      top: auto !important;
      left: auto !important;
    }

    .photo-img {
      height: auto;
      object-fit: initial;
    }
  }
</style>
