/* StackCarousel — вертикальная "картотека" проектов.
 *
 * Используется в разделах Works (графический дизайн) и Photo (арт-дирекшн).
 * Карточки уложены вертикальным стеком: активная в центре, чуть крупнее и
 * ярче, соседние сверху/снизу выглядывают и слегка приглушены, дальние —
 * быстро уходят в фон. Управление: колесо мыши / тачпад, стрелки ↑↓ /
 * PageUp/PageDown / Home/End, клик по соседней карточке (центрирует её),
 * клик по активной — открывает кейс. Без автопрокрутки.
 *
 * Контракт:
 *   items: [{ id, idx, tone, mediaId, title, year, client, kind? }]
 *   onOpen(i) — клик по активной (или Enter)
 *   ctaLabel — мелкая моно-кнопка справа на активной карточке
 *   railLabel — подпись на боковой шкале (раздел)
 *   itemKind — префикс для data-ed (например 'proj' или 'ph')
 */

const {
  useState: useStateStk, useEffect: useEffectStk,
  useRef:   useRefStk,   useCallback: useCallbackStk,
} = React;
const edstk = window.ed;
const { MediaSlot: MSlotStk } = window;

function StackCarousel({ items, lang, onOpen, ctaLabel, railLabel, itemKind }) {
  const N = items.length;
  const [active, setActive] = useStateStk(0);
  const rootRef    = useRefStk(null);
  const wheelAcc   = useRefStk(0);
  const lastWheel  = useRefStk(0);
  const lockedTill = useRefStk(0);

  const go = useCallbackStk((nextIdx) => {
    const i = ((nextIdx % N) + N) % N;
    setActive(i);
  }, [N]);

  const next = useCallbackStk(() => go(active + 1), [active, go]);
  const prev = useCallbackStk(() => go(active - 1), [active, go]);

  // Wheel: одна порция жеста = ровно одна карточка.
  //   • Большой порог (110) отсекает мелкие микро-движения трекпада.
  //   • После шага — рефракторный период 450мс: все wheel-события глушатся,
  //     накопитель не растёт. Это съедает momentum-инерцию тачпада, которая
  //     иначе сразу же набирала бы порог и проскакивала через ещё одну карту.
  //   • Большая пауза между событиями (>180мс) трактуется как «новый жест» и
  //     сбрасывает накопитель — то есть, если пользователь прокручивает
  //     спокойно, маленькие deltaY не суммируются вечно в фоне.
  useEffectStk(() => {
    const el = rootRef.current;
    if (!el) return undefined;

    const THRESH = 110;
    const REFRACTORY = 450;
    const GESTURE_GAP = 180;

    const onWheel = (e) => {
      e.preventDefault();
      const now = performance.now ? performance.now() : Date.now();

      // Внутри рефрактора всё гасим — это убивает momentum-хвост жеста.
      if (now < lockedTill.current) {
        wheelAcc.current = 0;
        lastWheel.current = now;
        return;
      }

      // Большой разрыв = новый жест, начинаем счёт с нуля.
      if (now - lastWheel.current > GESTURE_GAP) wheelAcc.current = 0;
      lastWheel.current = now;

      const dy = Math.abs(e.deltaY) >= Math.abs(e.deltaX) ? e.deltaY : e.deltaX;
      wheelAcc.current += dy;

      if (wheelAcc.current >= THRESH) {
        wheelAcc.current = 0;
        lockedTill.current = now + REFRACTORY;
        go(active + 1);
      } else if (wheelAcc.current <= -THRESH) {
        wheelAcc.current = 0;
        lockedTill.current = now + REFRACTORY;
        go(active - 1);
      }
    };

    el.addEventListener('wheel', onWheel, { passive: false });
    return () => el.removeEventListener('wheel', onWheel);
  }, [active, go]);

  // Клавиатура.
  useEffectStk(() => {
    const onKey = (e) => {
      // Не перехватываем, если пользователь редактирует текст.
      const t = e.target;
      if (t && (t.isContentEditable || /^(INPUT|TEXTAREA)$/.test(t.tagName))) return;
      if (e.key === 'ArrowDown' || e.key === 'PageDown') { e.preventDefault(); next(); }
      else if (e.key === 'ArrowUp' || e.key === 'PageUp') { e.preventDefault(); prev(); }
      else if (e.key === 'Home') { e.preventDefault(); go(0); }
      else if (e.key === 'End')  { e.preventDefault(); go(N - 1); }
      else if (e.key === 'Enter' && document.activeElement === rootRef.current) {
        onOpen && onOpen(active);
      }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [active, N, next, prev, go, onOpen]);

  // Touch (мобильные).
  useEffectStk(() => {
    const el = rootRef.current;
    if (!el) return undefined;
    let y0 = 0, dy = 0;
    const onStart = (e) => { y0 = e.touches[0].clientY; dy = 0; };
    const onMove  = (e) => { dy = e.touches[0].clientY - y0; };
    const onEnd   = () => { if (dy < -45) next(); else if (dy > 45) prev(); };
    el.addEventListener('touchstart', onStart, { passive: true });
    el.addEventListener('touchmove',  onMove,  { passive: true });
    el.addEventListener('touchend',   onEnd);
    return () => {
      el.removeEventListener('touchstart', onStart);
      el.removeEventListener('touchmove',  onMove);
      el.removeEventListener('touchend',   onEnd);
    };
  }, [next, prev]);

  // Фокусируемся при монтировании, чтобы стрелки работали без клика.
  useEffectStk(() => {
    const el = rootRef.current;
    if (el && document.activeElement === document.body) {
      try { el.focus({ preventScroll: true }); } catch { try { el.focus(); } catch {} }
    }
  }, []);

  const onCardClick = (i) => {
    if (i === active) { onOpen && onOpen(active); }
    else go(i);
  };

  // Локальная подпись хинта.
  const hint = lang === 'ru' ? 'СКРОЛЛ · ↑ ↓ · КЛИК' : 'SCROLL · ↑ ↓ · CLICK';

  return (
    <>
      {/* Мини-хедер над сценой: подпись раздела, счётчик и подсказка */}
      <div className="stack-meta pf-mono">
        <span className="stack-meta-label">{railLabel}</span>
        <span className="stack-meta-rule" aria-hidden="true"></span>
        <span className="stack-meta-count">
          <span className="stack-meta-now pf-num">{String(active + 1).padStart(2, '0')}</span>
          <span className="pf-em"> / {String(N).padStart(2, '0')}</span>
        </span>
        <span className="stack-meta-hint pf-em">{hint}</span>
      </div>

      <div className="stack" ref={rootRef} tabIndex={0} role="region" aria-roledescription="carousel">
        <div className="stack-stage">
        {items.map((it, i) => {
          const off = i - active;
          const abs = Math.abs(off);
          if (abs > 4) return null;

          // Шаг по вертикали — % высоты карточки. 64% даёт уверенный peek
          // соседних карточек сверху/снизу, как в Jitter-картотеке.
          const STEP = 64;
          const ty = off * STEP;
          const scale =
            abs === 0 ? 1.0 :
            abs === 1 ? 0.92 :
            abs === 2 ? 0.84 :
            abs === 3 ? 0.76 : 0.68;
          const opacity =
            abs === 0 ? 1 :
            abs === 1 ? 0.55 :
            abs === 2 ? 0.22 :
            abs === 3 ? 0.08 : 0;
          const z = 100 - abs;

          return (
            <article
              key={it.id}
              className="stack-card"
              data-active={abs === 0 ? '1' : '0'}
              data-side={off < 0 ? 'up' : off > 0 ? 'down' : 'mid'}
              style={{
                transform: `translate(-50%, calc(-50% + ${ty}%)) scale(${scale})`,
                opacity,
                zIndex: z,
                pointerEvents: abs > 2 ? 'none' : 'auto',
              }}
              onClick={() => onCardClick(i)}
              aria-hidden={abs === 0 ? 'false' : 'true'}
              aria-current={abs === 0 ? 'true' : undefined}
            >
              {/* Центр: обложка + моно-пилюли в углах */}
              <div className="stack-card-media">
                <MSlotStk
                  id={it.mediaId}
                  lang={lang}
                  tone={it.tone}
                  className="stack-card-img"
                  label={`P/${it.idx}`}
                />
                <div className="stack-card-tl pf-mono">
                  <span className="stack-card-tl-idx">{it.idx}</span>
                  <span className="stack-card-tl-rule" aria-hidden="true"></span>
                  <span
                    className="stack-card-tl-title"
                    {...edstk(it.titleEditId || `${itemKind}-${it.id}-title`)}
                  >
                    {it.title}
                  </span>
                </div>
                <div
                  className="stack-card-bl pf-mono"
                  {...edstk(it.kindEditId || `${itemKind}-${it.id}-stack-r`)}
                >
                  {it.kind}
                </div>
                {abs === 0 && (
                  <div className="stack-card-cta pf-mono" aria-hidden="true">
                    {ctaLabel} →
                  </div>
                )}
              </div>

              {/* Правая edge-колонка: idx сверху, год вертикально, ↘ снизу */}
              <div className="stack-card-edge">
                <div className="stack-card-edge-mark pf-mono">{it.idx}</div>
                <div className="stack-card-edge-meta">
                  <span className="stack-card-edge-arrow" aria-hidden="true">▼</span>
                  <span
                    className="stack-card-edge-text pf-mono"
                    {...edstk(`${itemKind}-${it.id}-year`)}
                  >
                    {it.year}
                  </span>
                </div>
                <div className="stack-card-edge-corner" aria-hidden="true">→</div>
              </div>
            </article>
          );
        })}
      </div>

      {/* Стрелки */}
      <button className="stack-nav stack-nav-up"   onClick={prev} aria-label="Previous">
        <span className="pf-mono">↑</span>
      </button>
      <button className="stack-nav stack-nav-down" onClick={next} aria-label="Next">
        <span className="pf-mono">↓</span>
      </button>
      </div>
    </>
  );
}

window.StackCarousel = StackCarousel;
