'use client';

import { useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { IconX, IconChevronLeft, IconChevronRight } from '@tabler/icons-react';
import { usePanZoom } from '@/hooks/usePanZoom';
import type { Screenshot } from '@/constants/projects';

type ImageViewerProps = {
  items: Screenshot[];
  index: number;
  onIndexChange: (i: number) => void;
  onClose: () => void;
};

/** Caption to display — falls back to the required `alt` text. */
const captionOf = (s: Screenshot) => s.caption ?? s.alt;

/**
 * Full-bleed fullscreen image viewer with pan / pinch / wheel zoom, keyboard
 * navigation, and a thumbnail rail (vertical on desktop, horizontal at the
 * bottom on mobile). Borrowed from imagelab-gpu's FullscreenImage, trimmed to
 * the parts this portfolio needs — no slideshow.
 */
export default function ImageViewer({ items, index, onIndexChange, onClose }: ImageViewerProps) {
  // Natural size is tagged with the index it was measured for, so it reads as
  // null until the *current* image's onLoad fires — no reset effect needed.
  const [sized, setSized] = useState<{ i: number; w: number; h: number } | null>(null);
  const stageRef = useRef<HTMLDivElement>(null);
  const activeThumbRef = useRef<HTMLButtonElement>(null);

  const len = items.length;
  const item = items[index];
  const prev = () => onIndexChange((index - 1 + len) % len);
  const next = () => onIndexChange((index + 1) % len);

  const { zoom, offset, reframing, handlers } = usePanZoom(stageRef, {
    resetKey: index,
    onSwipe: (dir) => onIndexChange((index + dir + len) % len),
    onTap: onClose,
  });

  // Lock body scroll while the viewer is open.
  useEffect(() => {
    const original = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => { document.body.style.overflow = original; };
  }, []);

  // Keyboard navigation: arrows to move, Esc to close.
  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if (e.key === 'Escape') onClose();
      else if (e.key === 'ArrowLeft') onIndexChange((index - 1 + len) % len);
      else if (e.key === 'ArrowRight') onIndexChange((index + 1) % len);
    };
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, [index, len, onClose, onIndexChange]);

  // Keep the active thumbnail scrolled into view.
  useEffect(() => {
    activeThumbRef.current?.scrollIntoView({ block: 'nearest', inline: 'nearest' });
  }, [index]);

  if (!item) return null;

  const url = item.src;

  return createPortal(
    <div className="fixed inset-0 z-[80] flex flex-col overflow-hidden bg-black md:flex-row">
      {/* Ambient backdrop sampled from the current image via heavy blur. */}
      <div className="pointer-events-none absolute inset-0 z-0">
        {/* eslint-disable-next-line @next/next/no-img-element */}
        <img
          src={url}
          alt=""
          aria-hidden
          className="h-full w-full scale-125 object-cover opacity-40"
          style={{ filter: 'blur(60px)' }}
        />
        <div className="absolute inset-0 bg-black/60" />
      </div>

      {/* Stage — owns the pan/zoom gestures. */}
      <div
        ref={stageRef}
        className="relative z-10 min-h-0 flex-1 overflow-hidden"
        style={{ touchAction: 'none' }}
        onWheel={handlers.onWheel}
        onPointerDown={handlers.onPointerDown}
        onPointerMove={handlers.onPointerMove}
        onPointerUp={handlers.onPointerUp}
        onPointerCancel={handlers.onPointerUp}
        onClick={handlers.onClick}
      >
        <div className="pointer-events-none absolute inset-0 flex items-center justify-center p-4">
          {/* eslint-disable-next-line @next/next/no-img-element */}
          <img
            src={url}
            alt={item.alt}
            draggable={false}
            onLoad={(e) => setSized({ i: index, w: e.currentTarget.naturalWidth, h: e.currentTarget.naturalHeight })}
            className="max-h-full max-w-full select-none rounded-md shadow-[0_20px_70px_rgba(0,0,0,0.7)]"
            style={{
              transform: `translate(${offset.x}px, ${offset.y}px) scale(${zoom})`,
              transition: reframing ? 'transform 250ms cubic-bezier(0.22, 1, 0.36, 1)' : undefined,
            }}
          />
        </div>

        {/* Prev / next */}
        {len > 1 && (
          <>
            <button
              type="button"
              aria-label="Previous image"
              onClick={(e) => { e.stopPropagation(); prev(); }}
              className="absolute left-4 top-1/2 z-10 flex h-12 w-12 -translate-y-1/2 items-center justify-center rounded-full bg-black/40 text-white/80 backdrop-blur-md transition-colors hover:bg-black/60 hover:text-white"
            >
              <IconChevronLeft size={24} />
            </button>
            <button
              type="button"
              aria-label="Next image"
              onClick={(e) => { e.stopPropagation(); next(); }}
              className="absolute right-4 top-1/2 z-10 flex h-12 w-12 -translate-y-1/2 items-center justify-center rounded-full bg-black/40 text-white/80 backdrop-blur-md transition-colors hover:bg-black/60 hover:text-white"
            >
              <IconChevronRight size={24} />
            </button>
          </>
        )}

        {/* Close */}
        <button
          type="button"
          aria-label="Close viewer"
          onClick={(e) => { e.stopPropagation(); onClose(); }}
          className="absolute right-4 top-4 z-10 flex h-11 w-11 items-center justify-center rounded-full bg-black/40 text-white/80 backdrop-blur-md transition-colors hover:bg-black/60 hover:text-white"
        >
          <IconX size={18} />
        </button>

        {/* Page counter + dimensions */}
        <div className="pointer-events-none absolute left-4 top-4 z-10 flex items-center gap-2 rounded-full bg-black/40 px-3 py-1.5 font-mono text-[12px] text-white/70 backdrop-blur-md">
          <span>{index + 1} / {len}</span>
        </div>

        {/* Caption */}
        <div className="pointer-events-none absolute inset-x-0 bottom-4 z-10 flex justify-center px-16">
          <p className="max-w-2xl rounded-full bg-black/40 px-4 py-1.5 text-center text-sm text-white/90 backdrop-blur-md">
            {captionOf(item)}
          </p>
        </div>
      </div>

      {/* Thumbnail rail — horizontal at the bottom on mobile, vertical on the right on desktop. */}
      {len > 1 && (
        <div className="z-10 flex shrink-0 gap-2 overflow-x-auto border-t border-white/10 bg-black/40 p-3 backdrop-blur-md md:w-32 md:flex-col md:overflow-x-visible md:overflow-y-auto md:border-l md:border-t-0">
          {items.map((shot, i) => {
            const isActive = i === index;
            return (
              <button
                key={shot.src}
                ref={isActive ? activeThumbRef : null}
                type="button"
                aria-label={`View ${captionOf(shot)}`}
                aria-current={isActive}
                onClick={() => onIndexChange(i)}
                className={`relative shrink-0 overflow-hidden rounded-md border transition-all md:w-full ${
                  isActive
                    ? 'border-white/80 opacity-100 ring-2 ring-white/60'
                    : 'border-white/10 opacity-60 hover:opacity-100'
                }`}
              >
                {/* eslint-disable-next-line @next/next/no-img-element */}
                <img
                  src={shot.src}
                  alt=""
                  aria-hidden
                  className="h-16 w-24 object-cover md:h-20 md:w-full"
                />
              </button>
            );
          })}
        </div>
      )}
    </div>,
    document.body,
  );
}
