"use client";

import Image from "next/image";
import { useCallback, useEffect, useRef, useState } from "react";
import { AnimatePresence, motion } from "framer-motion";
import { Stagger, StaggerItem } from "../ui/Stagger";
import { EASE_OUT_EXPO } from "@/lib/motion";
import type { Screenshot } from "@/lib/games";

type Props = {
  screenshots: Screenshot[];
  /** Marks the captures as work in progress. */
  wip?: boolean;
};

/**
 * Screenshot grid with a lightbox.
 *
 * Captions live under the thumbnail and on a solid bar inside the lightbox —
 * never floating over the image, which is far too bright to carry text.
 */
/**
 * Which way the next capture slides in. Stored rather than derived: after the
 * index has changed there is no way to tell whether it went up or wrapped
 * around from the last shot to the first.
 */
const slide = {
  enter: (direction: number) => ({ opacity: 0, x: direction * 48 }),
  center: { opacity: 1, x: 0 },
  exit: (direction: number) => ({ opacity: 0, x: direction * -48 }),
};

export function ScreenshotGallery({ screenshots, wip = true }: Props) {
  const [openIndex, setOpenIndex] = useState<number | null>(null);
  const [direction, setDirection] = useState(1);
  const triggersRef = useRef<(HTMLButtonElement | null)[]>([]);
  const dialogRef = useRef<HTMLDivElement>(null);

  const close = useCallback(() => {
    setOpenIndex((current) => {
      if (current !== null) triggersRef.current[current]?.focus();
      return null;
    });
  }, []);

  const step = useCallback(
    (delta: number) => {
      setDirection(delta);
      setOpenIndex((current) =>
        current === null
          ? current
          : (current + delta + screenshots.length) % screenshots.length,
      );
    },
    [screenshots.length],
  );

  // Keyboard controls + scroll lock while the lightbox is open.
  useEffect(() => {
    if (openIndex === null) return;

    const onKeyDown = (event: KeyboardEvent) => {
      if (event.key === "Escape") close();
      else if (event.key === "ArrowRight") step(1);
      else if (event.key === "ArrowLeft") step(-1);
    };

    document.addEventListener("keydown", onKeyDown);
    const previousOverflow = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    dialogRef.current?.focus();

    return () => {
      document.removeEventListener("keydown", onKeyDown);
      document.body.style.overflow = previousOverflow;
    };
  }, [openIndex, close, step]);

  if (screenshots.length === 0) return null;

  const active = openIndex === null ? null : screenshots[openIndex];

  return (
    <>
      <Stagger as="ul" step={0.06} className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
        {screenshots.map((shot, index) => (
          <StaggerItem as="li" key={shot.src}>
            <button
              type="button"
              ref={(node) => {
                triggersRef.current[index] = node;
              }}
              onClick={() => {
                setDirection(1);
                setOpenIndex(index);
              }}
              className="group block w-full text-left transition-transform duration-200 ease-out active:scale-[0.98] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent"
            >
              <span className="relative block aspect-video overflow-hidden rounded-2xl border border-border bg-surface-elevated transition-colors duration-300 group-hover:border-accent/60">
                <Image
                  src={shot.src}
                  alt={shot.caption}
                  fill
                  sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
                  className="object-cover transition-transform duration-700 ease-out group-hover:scale-[1.04]"
                />
                {wip && (
                  <span className="absolute left-3 top-3 bg-background/90 px-2.5 py-1 font-display text-[0.65rem] tracking-[0.2em] text-accent">
                    WIP
                  </span>
                )}
                {/* The label rises into place rather than appearing, so the
                    overlay reads as one gesture instead of a flash. */}
                <span
                  aria-hidden
                  className="absolute inset-0 flex items-center justify-center bg-background/50 opacity-0 transition-opacity duration-300 group-hover:opacity-100"
                >
                  <span className="translate-y-2 border border-accent px-4 py-2 font-display text-xs tracking-[0.25em] text-accent transition-transform duration-300 ease-out group-hover:translate-y-0">
                    VIEW
                  </span>
                </span>
              </span>
              <span className="mt-3 block text-sm text-foreground-muted transition-colors duration-300 group-hover:text-foreground">
                {shot.caption}
              </span>
            </button>
          </StaggerItem>
        ))}
      </Stagger>

      <AnimatePresence>
        {active && (
          <motion.div
            ref={dialogRef}
            role="dialog"
            aria-modal="true"
            aria-label={active.caption}
            tabIndex={-1}
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            transition={{ duration: 0.2 }}
            onClick={close}
            className="fixed inset-0 z-[100] flex flex-col items-center justify-center gap-4 bg-background/95 p-4 backdrop-blur-sm sm:p-8"
          >
            <motion.div
              initial={{ opacity: 0, scale: 0.96, y: 8 }}
              animate={{ opacity: 1, scale: 1, y: 0 }}
              exit={{ opacity: 0, scale: 0.98, y: 4 }}
              transition={{ duration: 0.3, ease: EASE_OUT_EXPO }}
              className="relative w-full max-w-6xl"
              onClick={(event) => event.stopPropagation()}
            >
              <div className="relative aspect-video w-full overflow-hidden rounded-t-2xl border border-border bg-surface-elevated">
                {/* Each capture slides in from the side you asked for. Without
                    it, stepping through a gallery of similar screenshots gives
                    no sense of having moved at all. */}
                <AnimatePresence initial={false} custom={direction}>
                  <motion.div
                    key={active.src}
                    custom={direction}
                    variants={slide}
                    initial="enter"
                    animate="center"
                    exit="exit"
                    transition={{ duration: 0.3, ease: EASE_OUT_EXPO }}
                    className="absolute inset-0"
                  >
                    <Image
                      src={active.src}
                      alt={active.caption}
                      fill
                      sizes="100vw"
                      className="object-contain"
                      priority
                    />
                  </motion.div>
                </AnimatePresence>
              </div>
              <div className="flex items-center justify-between gap-4 rounded-b-2xl border border-t-0 border-border bg-surface px-4 py-3">
                <p className="text-sm text-foreground-muted">
                  {active.caption}
                </p>
                <p className="shrink-0 font-display text-xs tracking-[0.25em] text-foreground-muted">
                  {(openIndex ?? 0) + 1} / {screenshots.length}
                </p>
              </div>
            </motion.div>

            {screenshots.length > 1 && (
              <div
                className="flex items-center gap-3"
                onClick={(event) => event.stopPropagation()}
              >
                <button
                  type="button"
                  onClick={() => step(-1)}
                  aria-label="Previous screenshot"
                  className="group border border-border px-5 py-2 font-display text-sm tracking-widest text-foreground transition duration-200 hover:border-accent hover:text-accent active:scale-95"
                >
                  <span
                    aria-hidden
                    className="inline-block transition-transform duration-300 ease-out group-hover:-translate-x-1"
                  >
                    ←
                  </span>
                </button>
                <button
                  type="button"
                  onClick={() => step(1)}
                  aria-label="Next screenshot"
                  className="group border border-border px-5 py-2 font-display text-sm tracking-widest text-foreground transition duration-200 hover:border-accent hover:text-accent active:scale-95"
                >
                  <span
                    aria-hidden
                    className="inline-block transition-transform duration-300 ease-out group-hover:translate-x-1"
                  >
                    →
                  </span>
                </button>
              </div>
            )}

            <button
              type="button"
              onClick={close}
              aria-label="Close gallery"
              className="absolute right-4 top-4 border border-border bg-background/80 px-4 py-2 font-display text-xs tracking-[0.25em] text-foreground transition duration-200 hover:border-accent hover:text-accent active:scale-95 sm:right-8 sm:top-8"
            >
              CLOSE ✕
            </button>
          </motion.div>
        )}
      </AnimatePresence>
    </>
  );
}
