import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react';
import { useSwipeable } from 'react-swipeable';
import { useIsMobile } from '../../hooks/useIsMobile';
import { getRegionAccent } from './MiniMap';
import './HeroLightbox.css';

/* ─────────────────────────────────────────────
   Helpers
───────────────────────────────────────────── */
const STRIP_LIMIT = 36;
const THUMB_SIZE = 52;
const THUMB_SIZE_MOBILE = 44;
const ANIM_MS = 350;

function getStripRange(total, active) {
  if (total <= STRIP_LIMIT) return [0, total];
  const half = Math.floor(STRIP_LIMIT / 2);
  let start = active - half;
  if (start < 0) start = 0;
  let end = start + STRIP_LIMIT;
  if (end > total) {
    end = total;
    start = Math.max(0, end - STRIP_LIMIT);
  }
  return [start, end];
}

const prefersReduced = () =>
  typeof window !== 'undefined' &&
  window.matchMedia('(prefers-reduced-motion: reduce)').matches;

/* ─────────────────────────────────────────────
   HeroLightbox
───────────────────────────────────────────── */
export default function HeroLightbox({
  photos,
  selectedIndex,
  albumName,
  regionId,
  sourceRect,
  hasNext,
  loadingMore,
  onClose,
  onNavigate,
  onLoadMore,
  onCoverChange,
}) {
  const isMobile = useIsMobile();
  const [phase, setPhase] = useState('enter');
  const [slideDir, setSlideDir] = useState(null);
  const [slideToken, setSlideToken] = useState(0);
  const pendingAdvanceRef = useRef(false);
  const stripRef = useRef(null);
  const prevOverflowRef = useRef('');
  const [coverStatus, setCoverStatus] = useState(null); // 'saving' | 'done' | 'error'
  const coverTimerRef = useRef(null);
  const accent = useMemo(() => getRegionAccent(regionId), [regionId]);
  const reduced = useMemo(() => prefersReduced(), []);
  const animMs = reduced ? 0 : ANIM_MS;

  /* — Phase transitions — */
  useEffect(() => {
    // enter → open via double rAF
    let raf1, raf2;
    raf1 = requestAnimationFrame(() => {
      raf2 = requestAnimationFrame(() => setPhase('open'));
    });
    return () => {
      cancelAnimationFrame(raf1);
      cancelAnimationFrame(raf2);
    };
  }, []);

  /* — Body scroll lock — */
  useEffect(() => {
    prevOverflowRef.current = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => {
      document.body.style.overflow = prevOverflowRef.current;
    };
  }, []);

  /* — Pending advance after load more — */
  useEffect(() => {
    if (pendingAdvanceRef.current && !loadingMore) {
      pendingAdvanceRef.current = false;
      goNext();
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [loadingMore, photos.length]);

  /* — Auto-center active thumb — */
  useEffect(() => {
    if (!stripRef.current) return;
    const thumbSize = isMobile ? THUMB_SIZE_MOBILE : THUMB_SIZE;
    const gap = 4;
    const stripW = stripRef.current.offsetWidth;
    const scrollTarget =
      selectedIndex * (thumbSize + gap) - stripW / 2 + thumbSize / 2;
    stripRef.current.scrollTo({ left: scrollTarget, behavior: reduced ? 'auto' : 'smooth' });
  }, [selectedIndex, isMobile, reduced]);

  /* — Close handler — */
  const handleClose = useCallback(() => {
    setPhase('exit');
    setTimeout(onClose, animMs);
  }, [onClose, animMs]);

  /* — Navigation — */
  const goPrev = useCallback(() => {
    if (selectedIndex <= 0) return;
    setSlideDir('prev');
    setSlideToken((t) => t + 1);
    onNavigate(selectedIndex - 1);
  }, [selectedIndex, onNavigate]);

  const goNext = useCallback(() => {
    if (selectedIndex >= photos.length - 1) {
      if (hasNext) {
        pendingAdvanceRef.current = true;
        onLoadMore?.();
      }
      return;
    }
    setSlideDir('next');
    setSlideToken((t) => t + 1);
    onNavigate(selectedIndex + 1);
  }, [selectedIndex, photos.length, hasNext, onNavigate, onLoadMore]);

  /* — Keyboard — */
  useEffect(() => {
    const handler = (e) => {
      if (e.key === 'Escape') handleClose();
      else if (e.key === 'ArrowLeft') goPrev();
      else if (e.key === 'ArrowRight') goNext();
    };
    window.addEventListener('keydown', handler);
    return () => window.removeEventListener('keydown', handler);
  }, [handleClose, goPrev, goNext]);

  /* — Swipe — */
  const swipeHandlers = useSwipeable({
    onSwipedLeft: goNext,
    onSwipedRight: goPrev,
    onSwipedDown: (e) => {
      if (e.absY > 100) handleClose();
    },
    trackMouse: false,
    delta: 30,
  });

  /* — Set as album cover — */
  const handleSetCover = useCallback(async () => {
    const p = photos[selectedIndex];
    if (!p || !albumName || coverStatus === 'saving') return;
    const filename = p.file || p.filename || p.name || '';
    if (!filename) return;
    setCoverStatus('saving');
    try {
      const res = await fetch(`/api/travel/albums/${encodeURIComponent(albumName)}/cover`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ filename }),
      });
      if (!res.ok) throw new Error(`${res.status}`);
      setCoverStatus('done');
      onCoverChange?.();
    } catch {
      setCoverStatus('error');
    }
    if (coverTimerRef.current) clearTimeout(coverTimerRef.current);
    coverTimerRef.current = setTimeout(() => setCoverStatus(null), 2000);
  }, [selectedIndex, photos, albumName, coverStatus, onCoverChange]);

  /* — Current photo — */
  const photo = photos[selectedIndex];
  if (!photo) return null;

  const thumbSz = isMobile ? THUMB_SIZE_MOBILE : THUMB_SIZE;
  const [stripStart, stripEnd] = getStripRange(photos.length, selectedIndex);
  const stripPhotos = photos.slice(stripStart, stripEnd);

  const slideClass =
    slideDir === 'next'
      ? 'hero-lb__slide--next'
      : slideDir === 'prev'
        ? 'hero-lb__slide--prev'
        : '';

  return (
    <div
      className={`hero-lb hero-lb--${phase}`}
      {...swipeHandlers}
      role="dialog"
      aria-modal="true"
      aria-label="Photo viewer"
    >
      {/* Backdrop */}
      <div className="hero-lb__backdrop" onClick={handleClose} />

      {/* Inner */}
      <div className="hero-lb__inner">
        {/* Top bar */}
        <div className="hero-lb__topbar">
          <span className="hero-lb__counter">
            <span className="hero-lb__counter-cur" style={{ color: accent }}>
              {selectedIndex + 1}
            </span>
            {' / '}
            {photos.length}
          </span>
          <button
            className={`hero-lb__cover-btn${coverStatus === 'done' ? ' hero-lb__cover-btn--done' : coverStatus === 'error' ? ' hero-lb__cover-btn--error' : ''}`}
            onClick={handleSetCover}
            disabled={coverStatus === 'saving'}
            title="이 사진을 앨범 커버로 지정"
          >
            {coverStatus === 'saving' ? '저장 중…'
              : coverStatus === 'done' ? '커버 지정됨'
              : coverStatus === 'error' ? '실패'
              : '커버로 지정'}
          </button>
          <button
            className="hero-lb__close"
            onClick={handleClose}
            aria-label="Close"
          >
            ×
          </button>
        </div>

        {/* Main photo area */}
        <div className="hero-lb__stage">
          {/* Left arrow */}
          {!isMobile && selectedIndex > 0 && (
            <button className="hero-lb__arrow hero-lb__arrow--left" onClick={goPrev} aria-label="Previous">
              ‹
            </button>
          )}

          {/* Photo */}
          <img
            key={slideToken}
            className={`hero-lb__photo ${slideClass}`}
            src={photo.url || photo.src}
            alt={photo.filename || photo.name || ''}
            draggable={false}
          />

          {/* Right arrow */}
          {!isMobile && selectedIndex < photos.length - 1 && (
            <button className="hero-lb__arrow hero-lb__arrow--right" onClick={goNext} aria-label="Next">
              ›
            </button>
          )}

          {/* Loading spinner for load-more */}
          {!isMobile && loadingMore && selectedIndex >= photos.length - 1 && (
            <button className="hero-lb__arrow hero-lb__arrow--right hero-lb__arrow--loading" disabled aria-label="Loading">
              <span className="hero-lb__spinner" />
            </button>
          )}
        </div>

        {/* Meta */}
        <div className="hero-lb__meta">
          <span className="hero-lb__meta-album">{albumName}</span>
          {' · '}
          <span className="hero-lb__meta-file">{photo.filename || photo.name || ''}</span>
        </div>

        {/* Thumbnail strip */}
        <div className="hero-lb__strip" ref={stripRef}>
          {stripPhotos.map((p, i) => {
            const realIdx = stripStart + i;
            const isActive = realIdx === selectedIndex;
            return (
              <button
                key={p.id || realIdx}
                className={`hero-lb__thumb${isActive ? ' hero-lb__thumb--active' : ''}`}
                style={{
                  width: thumbSz,
                  height: thumbSz,
                  borderColor: isActive ? '#f5e6c8' : 'transparent',
                }}
                onClick={() => {
                  setSlideDir(realIdx > selectedIndex ? 'next' : 'prev');
                  setSlideToken((t) => t + 1);
                  onNavigate(realIdx);
                }}
                aria-label={`Photo ${realIdx + 1}`}
              >
                <img
                  src={p.thumbUrl || p.thumb || p.url || p.src}
                  alt=""
                  draggable={false}
                />
              </button>
            );
          })}
        </div>
      </div>
    </div>
  );
}
