"use client";

import { useEffect, useRef } from "react";
import { cn } from "@/lib/utils";
import { FOCUS_RING_CLASSES } from "@/components/ui/styles";
import styles from "./image-gallery.module.css";

export interface GalleryItem {
  src: string;
  alt: string;
  eventName?: string;
  href?: string;
}

function GalleryPanel({
  item,
  panelRef,
}: {
  item: GalleryItem;
  panelRef: (el: HTMLElement | null) => void;
}) {
  const hasCaption = Boolean(item.eventName || item.href);

  const content = (
    <>
      {/* eslint-disable-next-line @next/next/no-img-element -- dynamic/external URLs, not on next/image allowlist */}
      <img src={item.src} alt={item.alt} loading="lazy" />
      <div aria-hidden="true" className={styles.tint} />
      {hasCaption && (
        <div aria-hidden="true" className={styles.overlay}>
          <div>
            {item.eventName && (
              <p className="font-medium text-kolabora-neutral-white">{item.eventName}</p>
            )}
            {item.href && (
              <span className="text-sm font-medium text-kolabora-primary">View Event &rarr;</span>
            )}
          </div>
        </div>
      )}
    </>
  );

  return item.href ? (
    <a
      ref={panelRef}
      href={item.href}
      className={cn(styles.panel, FOCUS_RING_CLASSES)}
    >
      {content}
    </a>
  ) : (
    <div ref={panelRef} className={styles.panel}>
      {content}
    </div>
  );
}

/**
 * Expanding-image gallery. Desktop (hover-capable, >=768px): a horizontal
 * row where hovering a panel expands it (others shrink via `transition:
 * flex`) — pure CSS, no JS. Touch/no-hover or narrow viewports get the
 * *same* expand interaction, just rotated: a vertical, scrollable column
 * where whichever panel is nearest the box's vertical center is the
 * "active" (expanded) one — scroll position drives it instead of the
 * cursor. That's the one part CSS can't do alone: on every scroll tick,
 * find whichever panel's own center is closest to the gallery box's center
 * and toggle `.active` (see image-gallery.module.css) onto just that one.
 * A plain "closest panel wins" distance comparison, deliberately not an
 * IntersectionObserver with a percentage `rootMargin` — percentage
 * rootMargin support is inconsistent enough across engines that it could
 * silently never fire, leaving nothing marked active at all; this always
 * picks exactly one panel, on every frame, guaranteed. Both modes render
 * the exact same item order (top-to-bottom on mobile, left-to-right on
 * desktop) — no duplicated items, unlike the old marquee this replaced.
 */
export function ImageGallery({ items, className }: { items: GalleryItem[]; className?: string }) {
  const galleryRef = useRef<HTMLDivElement>(null);
  const panelRefs = useRef<Array<HTMLElement | null>>([]);

  useEffect(() => {
    const mql = window.matchMedia("(hover: none), (max-width: 767px)");
    if (!mql.matches) return;

    const root = galleryRef.current;
    if (!root) return;

    const panels = panelRefs.current.filter((el): el is HTMLElement => el !== null);
    if (panels.length === 0) return;

    let rafId = 0;
    function updateActive() {
      rafId = 0;
      const rootRect = root!.getBoundingClientRect();
      const centerY = rootRect.top + rootRect.height / 2;

      let closest: HTMLElement | null = null;
      let closestDistance = Infinity;
      for (const panel of panels) {
        const rect = panel.getBoundingClientRect();
        const distance = Math.abs(rect.top + rect.height / 2 - centerY);
        if (distance < closestDistance) {
          closestDistance = distance;
          closest = panel;
        }
      }
      panels.forEach((panel) => panel.classList.toggle(styles.active, panel === closest));
    }
    function onScroll() {
      if (!rafId) rafId = requestAnimationFrame(updateActive);
    }

    updateActive();
    root.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => {
      root.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onScroll);
      if (rafId) cancelAnimationFrame(rafId);
    };
  }, [items]);

  return (
    <div ref={galleryRef} className={cn(styles.gallery, className)}>
      <div className={styles.track}>
        {items.map((item, i) => (
          <GalleryPanel key={i} item={item} panelRef={(el) => (panelRefs.current[i] = el)} />
        ))}
      </div>
    </div>
  );
}
