<script lang="ts">
  import { marked } from "$lib/markdown-safe";
  import { fade } from "svelte/transition";
  import { getBlockLayoutClasses } from "$lib/block-layout";
  import SectionHeader from "$lib/components/SectionHeader.svelte";
  import type { ImageGalleryBlockData, ImageGalleryImage } from "$lib/block-types";
  import "$lib/iconify-offline";
  import Icon from "@iconify/svelte";
  import { useTranslate } from "$lib/translations";
  import { T } from "$lib/translations";
  import RustyImage from "$lib/components/RustyImage.svelte";
  import { extractCmsImageField } from "$lib/rusty-image";

  const t = useTranslate();
  let { block }: { block: ImageGalleryBlockData } = $props();
  const actionRef = $derived(
    block.action && typeof block.action === "object" ? block.action : null,
  );

  /** Hover-Label aus image-Field — title|alt|caption|description, in dieser Reihenfolge. */
  function imageLabel(img: ImageGalleryImage): string {
    return (img.title ?? img.alt ?? img.caption ?? img.description ?? "").trim();
  }

  /** Dateiname für Download aus Asset-URL ableiten. */
  function downloadFilename(url: string): string {
    try {
      const u = new URL(url);
      const last = u.pathname.split("/").filter(Boolean).pop();
      return last ? decodeURIComponent(last) : "image";
    } catch {
      return "image";
    }
  }

  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) => {
        const field = extractCmsImageField(img);
        return field?.url
          ? { img, url: field.url, focal: field.focal ?? null }
          : null;
      })
      .filter(
        (
          x,
        ): x is {
          img: ImageGalleryImage;
          url: string;
          focal: { x: number; y: number } | null;
        } => x != null,
      ),
  );

  let currentIndex = $state(0);

  // Grid-Modal-State: -1 = closed, 0..n-1 = open at index.
  let modalIndex = $state<number>(-1);
  const modalOpen = $derived(modalIndex >= 0);

  function openModal(i: number) {
    modalIndex = i;
  }
  function closeModal() {
    modalIndex = -1;
  }
  function modalPrev() {
    if (modalIndex < 0) return;
    modalIndex = (modalIndex - 1 + withUrl.length) % withUrl.length;
  }
  function modalNext() {
    if (modalIndex < 0) return;
    modalIndex = (modalIndex + 1) % withUrl.length;
  }
  function onModalKey(e: KeyboardEvent) {
    if (!modalOpen) return;
    if (e.key === "Escape") {
      e.preventDefault();
      closeModal();
    } else if (e.key === "ArrowLeft") {
      modalPrev();
    } else if (e.key === "ArrowRight") {
      modalNext();
    }
  }

  function prev() {
    currentIndex = (currentIndex - 1 + withUrl.length) % withUrl.length;
  }

  function next() {
    currentIndex = (currentIndex + 1) % withUrl.length;
  }

  const SWIPE_THRESHOLD = 50;
  let startX = $state(0);
  let isDragging = $state(false);
  let dragDelta = $state(0);

  function onPointerDown(e: PointerEvent) {
    if (withUrl.length <= 1) return;
    startX = e.clientX;
    isDragging = true;
    dragDelta = 0;
    (e.currentTarget as HTMLElement).setPointerCapture?.(e.pointerId);
  }

  function onPointerMove(e: PointerEvent) {
    if (!isDragging) return;
    dragDelta = e.clientX - startX;
    if (e.pointerType === "touch" && Math.abs(dragDelta) > 10) e.preventDefault();
  }

  function onPointerUp() {
    if (!isDragging) return;
    if (Math.abs(dragDelta) > SWIPE_THRESHOLD) {
      if (dragDelta < 0) next();
      else prev();
    }
    isDragging = false;
    dragDelta = 0;
    startX = 0;
  }

  function onTouchStart(e: TouchEvent) {
    if (withUrl.length <= 1) return;
    startX = e.touches[0].clientX;
    isDragging = true;
    dragDelta = 0;
  }

  function onTouchMove(e: TouchEvent) {
    if (!isDragging || !e.touches[0]) return;
    const delta = e.touches[0].clientX - startX;
    dragDelta = delta;
    if (Math.abs(delta) > 10) e.preventDefault();
  }

  function onTouchEnd() {
    if (!isDragging) return;
    if (Math.abs(dragDelta) > SWIPE_THRESHOLD) {
      if (dragDelta < 0) next();
      else prev();
    }
    isDragging = false;
    dragDelta = 0;
    startX = 0;
  }
</script>

<svelte:window onkeydown={onModalKey} />

<div class={layoutClasses} data-block="ImageGallery" 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 block.variant === "grid"}
    {#if block.title}
      <div class="mb-3">
        <SectionHeader
          number={block.number}
          kicker={block.kicker}
          title={block.title}
          tag="h3"
          actionLabel={actionRef?.linkName}
          actionHref={actionRef?.url}
        />
      </div>
    {/if}
    <ul class="not-prose grid grid-cols-2 gap-2 sm:grid-cols-3 md:grid-cols-4">
      {#each withUrl as item, i}
        {@const label = imageLabel(item.img)}
        <li class="m-0 p-0">
          <button
            type="button"
            class="group relative block w-full aspect-square overflow-hidden rounded-xs bg-wald-100 p-2 cursor-zoom-in focus:outline-none focus:ring-2 focus:ring-wald-500"
            onclick={() => openModal(i)}
            aria-label={label || t(T.image_gallery_open_aria)}
          >
            <RustyImage
              src={item.url}
              focal={item.focal}
              width={400}
              aspect="1/1"
              fit="contain"
              quality={80}
              alt={label}
              class="w-full h-full object-contain transition-transform duration-300 group-hover:scale-105"
              loading={i < 4 ? "eager" : "lazy"}
            />
            {#if label}
              <span
                class="absolute inset-x-0 bottom-0 max-h-full overflow-y-auto px-2 py-1.5 text-left text-[10px] leading-tight text-white bg-gradient-to-t from-black/95 via-black/80 to-black/30 opacity-0 transition-opacity duration-200 group-hover:opacity-100 group-focus:opacity-100 line-clamp-2 group-hover:line-clamp-none group-focus:line-clamp-none"
              >
                {label}
              </span>
            {/if}
          </button>
        </li>
      {/each}
    </ul>

    {#if modalOpen}
      {@const cur = withUrl[modalIndex]}
      {@const curLabel = imageLabel(cur.img)}
      <div
        class="fixed inset-0 z-[100] flex items-center justify-center bg-black/85 p-4"
        role="dialog"
        aria-modal="true"
        aria-label={curLabel || t(T.image_gallery_modal_aria)}
        onclick={closeModal}
        transition:fade={{ duration: 150 }}
      >
        <div
          class="relative max-h-[90vh] max-w-[95vw] flex flex-col items-center gap-3"
          onclick={(e) => e.stopPropagation()}
          role="presentation"
        >
          <img
            src={cur.url}
            alt={curLabel}
            class="max-h-[80vh] max-w-full rounded-xs bg-stein-900 object-contain shadow-2xl"
            loading="eager"
          />
          <div class="flex w-full items-center justify-between gap-3 text-white">
            <div class="min-w-0 flex-1">
              {#if curLabel}
                <p class="truncate text-sm font-medium">{curLabel}</p>
              {/if}
              {#if withUrl.length > 1}
                <p class="text-xs text-white/60">{modalIndex + 1} / {withUrl.length}</p>
              {/if}
            </div>
            <a
              href={cur.url}
              download={downloadFilename(cur.url)}
              class="no-underline inline-flex h-9 w-9 items-center justify-center rounded-full text-white/80 hover:text-white hover:bg-white/10 focus:outline-none focus:ring-2 focus:ring-white/40"
              title={t(T.image_gallery_download)}
              aria-label={t(T.image_gallery_download)}
            >
              <Icon icon="lucide:download" class="text-xl" aria-hidden="true" />
            </a>
          </div>
          {#if withUrl.length > 1}
            <button
              type="button"
              class="absolute left-2 top-1/2 -translate-y-1/2 inline-flex h-9 w-9 items-center justify-center rounded-full bg-black/50 text-white hover:bg-black/70 focus:outline-none focus:ring-2 focus:ring-white/50"
              onclick={modalPrev}
              aria-label={t(T.image_gallery_prev_aria)}
            >
              <Icon icon="lucide:chevron-left" class="text-2xl" aria-hidden="true" />
            </button>
            <button
              type="button"
              class="absolute right-2 top-1/2 -translate-y-1/2 inline-flex h-9 w-9 items-center justify-center rounded-full bg-black/50 text-white hover:bg-black/70 focus:outline-none focus:ring-2 focus:ring-white/50"
              onclick={modalNext}
              aria-label={t(T.image_gallery_next_aria)}
            >
              <Icon icon="lucide:chevron-right" class="text-2xl" aria-hidden="true" />
            </button>
          {/if}
          <button
            type="button"
            class="absolute -top-2 -right-2 inline-flex h-8 w-8 items-center justify-center rounded-full bg-white text-stein-900 shadow-md hover:bg-stein-100 focus:outline-none focus:ring-2 focus:ring-white/50"
            onclick={closeModal}
            aria-label={t(T.image_gallery_close)}
          >
            <Icon icon="lucide:x" class="text-lg" aria-hidden="true" />
          </button>
        </div>
      </div>
    {/if}
  {:else if withUrl.length === 1}
    <figure class="m-0">
      <RustyImage
        src={withUrl[0].url}
        focal={withUrl[0].focal}
        width={1280}
        aspect="16/9"
        fit="cover"
        quality={80}
        alt={withUrl[0].img.description ?? ""}
        class="w-full rounded-xs 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 class="group relative">
      <div
        class="relative overflow-hidden rounded-xs bg-stein-100 aspect-video touch-pan-y cursor-grab active:cursor-grabbing"
        role="region"
        aria-label={t(T.image_gallery_swipe_aria)}
        onpointerdown={onPointerDown}
        onpointermove={onPointerMove}
        onpointerup={onPointerUp}
        onpointercancel={onPointerUp}
        ontouchstart={onTouchStart}
        ontouchmove={onTouchMove}
        ontouchend={onTouchEnd}
        ontouchcancel={onTouchEnd}
      >
        {#key currentIndex}
          {@const current = withUrl[currentIndex]}
          <figure
            class="m-0 absolute inset-0"
            transition:fade={{ duration: 200 }}
          >
            <RustyImage
              src={current.url}
              focal={current.focal}
              width={1280}
              aspect="16/9"
              fit="cover"
              quality={80}
              alt={current.img.description ?? ""}
              class="w-full h-full object-cover"
              loading={currentIndex === 0 ? "eager" : "lazy"}
            />
            {#if current.img.description}
              <figcaption
                class="absolute bottom-0 left-0 right-0 py-2 px-3 text-xs text-white bg-black/50 rounded-b-sm transition-opacity lg:opacity-0 lg:group-hover:opacity-100"
              >
                {current.img.description}
              </figcaption>
            {/if}
          </figure>
        {/key}
      </div>

      <button
        type="button"
        class="absolute left-2 top-1/2 -translate-y-1/2 w-6 h-6 flex items-center justify-center rounded-full bg-black/50 text-white hover:bg-black/70 transition-opacity focus:outline-none focus:ring-2 focus:ring-wald-500 lg:opacity-0 lg:group-hover:opacity-100"
        aria-label={t(T.image_gallery_prev_aria)}
        onclick={prev}
      >
        <Icon icon="lucide:chevron-left" class="text-2xl" aria-hidden="true" />
      </button>
      <button
        type="button"
        class="absolute right-2 top-1/2 -translate-y-1/2 w-6 h-6 flex items-center justify-center rounded-full bg-black/50 text-white hover:bg-black/70 transition-opacity focus:outline-none focus:ring-2 focus:ring-wald-500 lg:opacity-0 lg:group-hover:opacity-100"
        aria-label={t(T.image_gallery_next_aria)}
        onclick={next}
      >
        <Icon icon="lucide:chevron-right" class="text-2xl" aria-hidden="true" />
      </button>

      <div class="flex justify-center gap-1.5 mt-2" role="tablist" aria-label={t(T.image_gallery_position_aria)}>
        {#each withUrl as _, i}
          <button
            type="button"
            role="tab"
            aria-label={t(T.image_gallery_slide_aria, { index: i + 1 })}
            aria-selected={i === currentIndex}
            class="w-2 h-2 rounded-full transition-colors {i === currentIndex
              ? 'bg-wald-600'
              : 'bg-stein-300 hover:bg-stein-400'}"
            onclick={() => (currentIndex = i)}
          ></button>
        {/each}
      </div>
    </div>
  {/if}
</div>
