// Act Va — THE SCRUBBER (live)
// Not a reprise — a real draggable scrubber. The presenter grabs the handle
// and walks it across one hundred years on stage. The garment cross-fades in
// response; the wall-label card updates; the year-marker follows the pointer.
//
// Interaction: click-drag the handle, click any year tick, or arrow-key left/
// right when the handle is focused. Scroll drives the playhead linearly
// across the century (1905 → 2022).

const { useState, useEffect, useRef, useMemo, useLayoutEffect } = window;

// 2004 Sharapova entry dropped 2026-04-29 click-economy pass — 2022 Raducanu
// is the contemporary anchor and Sharapova didn't introduce a new chapter.
// Five ticks: 1905, 1921, 1949, 1978, 2022.
const TIMELINE = [
  { year: 1905, title: 'Wool Bathing Costume',   designer: 'American · anonymous', img: 'assets/swim-wool-1905.png',    body: 'Twenty pounds when wet. Designed to fail at swimming on purpose — weighted with law.', tint: '#4a5a4a' },
  { year: 1921, title: 'Patou for Lenglen',      designer: 'Jean Patou',           img: 'assets/lenglen-1920s.png',     body: 'Sleeveless, knee-length, silk jersey. The hinge.',                                    tint: '#c9a84c' },
  { year: 1949, title: 'Tinling for Moran',      designer: 'Ted Tinling',          img: 'assets/tinling-1949.webp',    body: 'Four centimetres of visible lace. Tinling banned from Wimbledon until 1982.',         tint: '#b88a3a' },
  { year: 1978, title: 'Navratilova Pinstripe',  designer: 'Open Era',             img: 'assets/navratilova-1978.webp',body: 'Made for colour television. Wimbledon rewrote the whites rule the following year.',  tint: '#a6523b' },
  { year: 2022, title: 'Nike / Raducanu',        designer: 'Corset-quote',         img: 'assets/raducanu-2022.webp',   body: 'Seams quoting 19th-century corsetry. The hundred-year conversation, still open.',     tint: '#5e7a60' },
];

const YEAR_MIN = TIMELINE[0].year;
const YEAR_MAX = TIMELINE[TIMELINE.length - 1].year;
const YEAR_SPAN = YEAR_MAX - YEAR_MIN;

// Map a fractional position 0..1 to the TIMELINE entry whose year is nearest.
function nearestIndex(t) {
  const year = YEAR_MIN + t * YEAR_SPAN;
  let best = 0, bestDist = Infinity;
  for (let i = 0; i < TIMELINE.length; i++) {
    const d = Math.abs(TIMELINE[i].year - year);
    if (d < bestDist) { bestDist = d; best = i; }
  }
  return best;
}

function indexToFraction(i) {
  return (TIMELINE[i].year - YEAR_MIN) / YEAR_SPAN;
}

function ActCenturyScrubberInner({ index, total, act }) {
  // The scrubber's entry animation should fire the instant the act pins; we
  // don't want the overtitle to hold back while the playhead already walks.
  const visible = true;
  const ref = useRef(null);

  // Scroll-driven playhead. ScrollBeats passes progress 0..1 through context;
  // we map that directly onto the timeline fraction. Manual drag still works
  // (for presenter interaction on stage) — it momentarily overrides the
  // scroll-driven value, and any scroll afterwards smoothly reclaims control.
  const scrollP = React.useContext(window.BeatCtx).p;
  const scrollT = Math.max(0, Math.min(1, scrollP));

  const [manualT, setManualT] = useState(null); // null = follow scroll
  const [dragging, setDragging] = useState(false);
  const railRef = useRef(null);

  // Any time scroll moves, give scroll authority back.
  useEffect(() => {
    if (!dragging && manualT !== null) {
      // Release manual override a beat after the user lets go, so the next
      // scroll gesture pulls the handle back onto the scroll track.
      const id = setTimeout(() => setManualT(null), 1200);
      return () => clearTimeout(id);
    }
  }, [scrollP, dragging, manualT]);

  const t = manualT !== null ? manualT : scrollT;

  // Drag handlers — pointer events for mouse + touch unification.
  useEffect(() => {
    if (!dragging) return;
    const updateFromClientX = (cx) => {
      const rail = railRef.current;
      if (!rail) return;
      const rect = rail.getBoundingClientRect();
      const f = Math.max(0, Math.min(1, (cx - rect.left) / rect.width));
      setManualT(f);
    };
    const move = (e) => updateFromClientX(e.clientX ?? e.touches?.[0]?.clientX);
    const up = () => setDragging(false);
    window.addEventListener('pointermove', move);
    window.addEventListener('pointerup', up);
    window.addEventListener('pointercancel', up);
    return () => {
      window.removeEventListener('pointermove', move);
      window.removeEventListener('pointerup', up);
      window.removeEventListener('pointercancel', up);
    };
  }, [dragging]);

  const activeIdx = nearestIndex(t);
  const active = TIMELINE[activeIdx];
  const liveYear = Math.round(YEAR_MIN + t * YEAR_SPAN);

  // Click the rail → jump the handle there.
  const onRailPointerDown = (e) => {
    if (!railRef.current) return;
    const rect = railRef.current.getBoundingClientRect();
    const f = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
    setManualT(f);
    setDragging(true);
  };

  // Keyboard when handle is focused
  const onKey = (e) => {
    if (e.key === 'ArrowLeft')  { e.preventDefault(); setManualT(Math.max(0, t - 0.02)); }
    if (e.key === 'ArrowRight') { e.preventDefault(); setManualT(Math.min(1, t + 0.02)); }
  };

  return (
    <ActFrame act={act} index={index} total={total} bg="var(--petrol-deep)">
      <ActSeam roman={act.roman} />
      <DeepLinkPin href={act.deepLink?.href} label={act.deepLink?.label} />

      <div ref={ref} style={{ paddingTop: 30, display: 'flex', flexDirection: 'column', minHeight: 'calc(100vh - 160px)' }}>
        {/* Overtitle */}
        <div style={{
          display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end',
          opacity: visible ? 1 : 0, transform: visible ? 'translateY(0)' : 'translateY(12px)',
          transition: 'all 1.2s cubic-bezier(0.16,1,0.3,1)',
        }}>
          <div>
            <span className="label-text" style={{ color: 'var(--sage)' }}>The Tour · Gallery № 01 · live scrubber</span>
            <h2 className="font-display" style={{
              fontSize: 'clamp(52px, 5.8vw, 88px)',
              lineHeight: 0.98, letterSpacing: '-0.02em',
              color: 'var(--parchment)', margin: '14px 0 0', textTransform: 'uppercase',
            }}>
              One hundred years of <span style={{ color: 'var(--gold)', fontStyle: 'italic' }}>women&rsquo;s wear</span>.
            </h2>
          </div>
          <div style={{ textAlign: 'right' }}>
            <div className="label-text" style={{ color: 'var(--taupe)' }}>Scroll to walk the century</div>
            <div className="font-editorial" style={{ fontSize: 14, color: 'var(--parchment-dim)', marginTop: 8, fontStyle: 'italic' }}>
              Or drag the handle · click a year · ← → keys
            </div>
          </div>
        </div>

        {/* Stage — cross-fading garments */}
        <div style={{
          position: 'relative', flex: 1,
          marginTop: 30, marginBottom: 24,
          minHeight: 440, overflow: 'hidden',
          opacity: visible ? 1 : 0,
          transition: 'opacity 1.4s ease 0.3s',
        }}>
          {TIMELINE.map((e, i) => (
            <div
              key={e.year}
              style={{
                position: 'absolute', inset: 0,
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                opacity: i === activeIdx ? 1 : 0,
                transform: i === activeIdx ? 'scale(1)' : 'scale(0.97)',
                transition: 'opacity 0.6s ease, transform 0.9s cubic-bezier(0.16,1,0.3,1)',
                pointerEvents: 'none',
              }}
            >
              <div aria-hidden style={{
                position: 'absolute', inset: 0,
                background: `radial-gradient(ellipse 50% 62% at 50% 50%, ${e.tint}2a, transparent 70%)`,
              }} />
              <img src={e.img} alt={e.title}
                style={{
                  maxHeight: '92%', maxWidth: '72%', objectFit: 'contain',
                  filter: 'drop-shadow(0 40px 60px rgba(0,0,0,0.55))',
                }}
              />
            </div>
          ))}

          {/* Live year, centered back */}
          <div style={{
            position: 'absolute', inset: 0,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            pointerEvents: 'none',
          }}>
            <span className="font-display" style={{
              fontSize: 'clamp(180px, 22vw, 360px)',
              color: active.tint, opacity: 0.08,
              letterSpacing: '-0.04em', lineHeight: 1,
              transition: 'color 0.8s ease',
            }}>
              {liveYear}
            </span>
          </div>

          {/* Floating label card, top-left */}
          <div style={{
            position: 'absolute', top: 24, left: 0, maxWidth: 380,
            padding: '22px 26px',
            background: 'rgba(7,50,66,0.7)',
            backdropFilter: 'blur(8px)',
            borderLeft: `2px solid ${active.tint}`,
            transition: 'border-color 0.6s ease',
          }}>
            <div className="label-text" style={{ color: active.tint, marginBottom: 8, transition: 'color 0.6s ease' }}>
              № 0{activeIdx + 1} · {active.designer}
            </div>
            <div className="font-display" style={{
              fontSize: 30, lineHeight: 1.05, letterSpacing: '-0.01em',
              color: 'var(--parchment)', textTransform: 'uppercase',
            }}>
              {active.title}
            </div>
            <div className="font-editorial" style={{
              fontSize: 14, lineHeight: 1.6, marginTop: 10,
              color: 'rgba(240,238,233,0.5)',
              fontStyle: 'italic', textWrap: 'pretty',
            }}>
              {active.body}
            </div>
          </div>
        </div>

        {/* The scrubber itself */}
        <div style={{
          opacity: visible ? 1 : 0,
          transition: 'opacity 1.2s ease 0.5s',
        }}>
          {/* Rail */}
          <div
            ref={railRef}
            onPointerDown={onRailPointerDown}
            style={{
              position: 'relative',
              height: 56,
              cursor: dragging ? 'grabbing' : 'grab',
              userSelect: 'none',
              touchAction: 'none',
            }}
          >
            {/* Rail line */}
            <div style={{
              position: 'absolute',
              left: 0, right: 0, top: '50%', height: 2,
              transform: 'translateY(-50%)',
              background: 'rgba(143,166,143,0.18)',
            }} />
            {/* Filled rail */}
            <div style={{
              position: 'absolute',
              left: 0, top: '50%', height: 2,
              transform: 'translateY(-50%)',
              width: `${t * 100}%`,
              background: `linear-gradient(to right, rgba(143,166,143,0.4), ${active.tint})`,
              transition: dragging ? 'none' : 'all 0.3s ease',
            }} />

            {/* Year ticks */}
            {TIMELINE.map((e, i) => {
              const frac = indexToFraction(i);
              const isActive = i === activeIdx;
              return (
                <button
                  key={e.year}
                  onClick={(ev) => { ev.stopPropagation(); setManualT(frac); }}
                  aria-label={`Jump to ${e.year}`}
                  style={{
                    position: 'absolute',
                    left: `${frac * 100}%`, top: '50%',
                    transform: 'translate(-50%, -50%)',
                    width: 14, height: 14, borderRadius: '50%',
                    border: `1px solid ${isActive ? e.tint : 'rgba(143,166,143,0.4)'}`,
                    background: isActive ? e.tint : 'var(--petrol-deep)',
                    boxShadow: isActive ? `0 0 16px ${e.tint}88` : 'none',
                    cursor: 'pointer',
                    padding: 0,
                    transition: 'all 0.3s ease',
                  }}
                />
              );
            })}

            {/* The handle */}
            <div
              role="slider"
              tabIndex={0}
              aria-valuemin={YEAR_MIN}
              aria-valuemax={YEAR_MAX}
              aria-valuenow={liveYear}
              onKeyDown={onKey}
              onPointerDown={(e) => { e.stopPropagation(); setDragging(true); }}
              style={{
                position: 'absolute',
                left: `${t * 100}%`, top: '50%',
                transform: 'translate(-50%, -50%)',
                width: 32, height: 32, borderRadius: '50%',
                background: active.tint,
                boxShadow: `0 0 32px ${active.tint}88, 0 8px 24px rgba(0,0,0,0.4)`,
                cursor: dragging ? 'grabbing' : 'grab',
                border: '2px solid var(--parchment)',
                transition: dragging ? 'none' : 'background 0.4s ease, box-shadow 0.4s ease, left 0.2s ease',
                outline: 'none',
              }}
            />

            {/* Year tooltip above the handle */}
            <div style={{
              position: 'absolute',
              left: `${t * 100}%`, top: -18,
              transform: 'translateX(-50%)',
              padding: '4px 10px',
              background: 'var(--petrol-deep)',
              border: `1px solid ${active.tint}`,
              fontFamily: 'var(--font-display)',
              fontSize: 14, fontWeight: 700, letterSpacing: '-0.01em',
              color: active.tint,
              pointerEvents: 'none',
              transition: 'border-color 0.4s ease, color 0.4s ease',
            }}>
              {liveYear}
            </div>
          </div>

          {/* Year labels row — anchored at tick positions */}
          <div style={{ position: 'relative', height: 28, marginTop: 8 }}>
            {TIMELINE.map((e, i) => {
              const frac = indexToFraction(i);
              const isActive = i === activeIdx;
              return (
                <div
                  key={e.year}
                  style={{
                    position: 'absolute',
                    left: `${frac * 100}%`,
                    transform: 'translateX(-50%)',
                    textAlign: 'center',
                  }}
                >
                  <div className="font-display" style={{
                    fontSize: isActive ? 22 : 16,
                    color: isActive ? e.tint : 'var(--taupe)',
                    opacity: isActive ? 1 : 0.55,
                    transition: 'all 0.4s ease',
                    lineHeight: 1,
                  }}>
                    {e.year}
                  </div>
                </div>
              );
            })}
          </div>

        </div>
      </div>
    </ActFrame>
  );
}

function ActCenturyScrubber({ index, total, act }) {
  return (
    <ScrollBeats beats={5} bg="var(--petrol-deep)">
      <ActCenturyScrubberInner index={index} total={total} act={act} />
    </ScrollBeats>
  );
}

window.ActCenturyScrubber = ActCenturyScrubber;
