<script lang="ts">
  import { onMount, tick } from "svelte";
  import { createElement } from "react";
  import { createRoot, type Root } from "react-dom/client";
  import { MasonryPhotoAlbum, type Photo } from "react-photo-album";
  import { marked } from "marked";
  import { getBlockLayoutClasses } from "../../lib/block-layout";
  import type { ImageGalleryBlockData, ImageGalleryImage } from "../../lib/block-types";
  import { useTranslate } from "../../lib/translations";
  import { T } from "../../lib/translations";
  import "react-photo-album/masonry.css";

  const t = useTranslate();
  let { block }: { block: ImageGalleryBlockData } = $props();

  marked.setOptions({ gfm: true });

  const layoutClasses = $derived(getBlockLayoutClasses(block.layout));
  const descriptionHtml = $derived(
    block.description && typeof block.description === "string"
      ? (marked.parse(block.description) as string)
      : "",
  );
  const images = $derived(
    (block.images ?? []).filter((i): i is ImageGalleryImage => i != null && typeof i === "object"),
  );
  const withUrl = $derived(
    images
      .map((img) => ({ img, url: img.resolvedSrc ?? imageUrl(img) }))
      .filter((x): x is { img: ImageGalleryImage; url: string } => x.url != null),
  );

  /** Fallback, solange keine echten Pixelmaße bekannt sind (CMS liefert keine width/height). */
  const DEFAULT_W = 1600;
  const DEFAULT_H = 900;

  let galleryPhotos = $state<Photo[]>([]);

  let albumHost: HTMLDivElement | undefined = $state();
  let reactRoot: Root | null = null;

  function imageUrl(img: ImageGalleryImage): string | null {
    if (typeof img.src === "string") return img.src.startsWith("//") ? `https:${img.src}` : img.src;
    if (img.file?.url) return img.file.url.startsWith("//") ? `https:${img.file.url}` : img.file.url;
    return null;
  }

  function metaToPhotos(
    meta: { img: ImageGalleryImage; url: string }[],
    dims: { w: number; h: number }[],
  ): Photo[] {
    return meta.map(({ img, url }, i) => ({
      src: url,
      width: Math.max(1, dims[i]?.w ?? DEFAULT_W),
      height: Math.max(1, dims[i]?.h ?? DEFAULT_H),
      alt: img.description ?? "",
      title: img.description ?? undefined,
    }));
  }

  function loadImageSize(url: string): Promise<{ w: number; h: number }> {
    return new Promise((resolve) => {
      const im = new Image();
      im.onload = () =>
        resolve({
          w: im.naturalWidth || DEFAULT_W,
          h: im.naturalHeight || DEFAULT_H,
        });
      im.onerror = () => resolve({ w: DEFAULT_W, h: DEFAULT_H });
      im.src = url;
    });
  }

  $effect(() => {
    const meta = withUrl;
    if (meta.length < 2) {
      galleryPhotos = [];
      return;
    }

    const fallback = meta.map(() => ({ w: DEFAULT_W, h: DEFAULT_H }));
    galleryPhotos = metaToPhotos(meta, fallback);

    if (typeof window === "undefined") return;

    let cancelled = false;
    void Promise.all(meta.map(({ url }) => loadImageSize(url))).then((dims) => {
      if (cancelled) return;
      galleryPhotos = metaToPhotos(meta, dims);
    });

    return () => {
      cancelled = true;
    };
  });

  function renderAlbum() {
    if (!reactRoot || galleryPhotos.length < 2) return;
    reactRoot.render(
      createElement(MasonryPhotoAlbum, {
        photos: galleryPhotos,
        defaultContainerWidth: 1180,
        columns: (containerWidth: number) => {
          if (containerWidth < 480) return 2;
          if (containerWidth < 800) return 3;
          return 4;
        },
        spacing: 8,
        padding: 0,
        sizes: {
          size: "min(1180px, 100vw - 2rem)",
          sizes: [{ viewport: "(max-width: 1280px)", size: "calc(100vw - 2rem)" }],
        },
      }),
    );
  }

  onMount(() => {
    if (withUrl.length < 2) return;

    let cancelled = false;
    void tick().then(() => {
      if (cancelled || !albumHost) return;
      reactRoot = createRoot(albumHost);
      renderAlbum();
    });

    return () => {
      cancelled = true;
      reactRoot?.unmount();
      reactRoot = null;
    };
  });

  $effect(() => {
    galleryPhotos;
    if (withUrl.length < 2 || !reactRoot) return;
    renderAlbum();
  });
</script>

<div class={layoutClasses} data-block-type="image_gallery" data-block-slug={block._slug}>
  {#if descriptionHtml}
    <div class="markdown max-w-none prose prose-zinc mb-4">
      {@html descriptionHtml}
    </div>
  {/if}

  {#if withUrl.length === 0}
    <p class="text-sm text-stein-500">{t(T.image_gallery_empty)}</p>
  {:else if withUrl.length === 1}
    <figure class="m-0">
      <img
        src={withUrl[0].url}
        alt={withUrl[0].img.description ?? ""}
        class="w-full rounded-sm object-cover aspect-video"
        loading="eager"
      />
      {#if withUrl[0].img.description}
        <figcaption class="mt-1 text-sm text-stein-600">{withUrl[0].img.description}</figcaption>
      {/if}
    </figure>
  {:else}
    <div
      bind:this={albumHost}
      class="react-photo-album-host w-full min-h-48 [&_.react-photo-album]:max-w-none"
    ></div>
  {/if}
</div>
