/* global React */
// Animated rule explainers for Scoundrel
// Depends on cards.jsx (PlayingCard, etc.) and animations.jsx (Stage, Sprite, useTime)

const { useEffect, useState, useRef, useMemo } = React;

/* =========================================================
   Felt — the surface every animation plays on
   ========================================================= */
function Felt({ children, label, caption }) {
  return (
    <div className="felt-wrap">
      {label && <div className="felt-label">{label}</div>}
      <div className="felt">
        {children}
      </div>
      {caption && <div className="felt-caption">{caption}</div>}
    </div>
  );
}

/* Helper: smooth interpolate */
function lerp(a, b, t) { return a + (b - a) * t; }
function ease(t, kind = "out") {
  // cubic
  if (kind === "out") return 1 - Math.pow(1 - t, 3);
  if (kind === "in")  return t * t * t;
  if (kind === "io")  return t < 0.5 ? 4*t*t*t : 1 - Math.pow(-2*t + 2, 3) / 2;
  return t;
}
function range(t, a, b) {
  // returns 0..1 within [a,b], clamped
  if (t <= a) return 0;
  if (t >= b) return 1;
  return (t - a) / (b - a);
}

/* =========================================================
   AnimatedScene — owns its own playhead & loops
   Renders prop {t} (0..duration in seconds) and provides controls.
   ========================================================= */
function AnimatedScene({ duration, render, label, caption, height = 360 }) {
  const [t, setT] = useState(0);
  const [playing, setPlaying] = useState(true);
  const rafRef = useRef(0);
  const lastRef = useRef(0);

  useEffect(() => {
    if (!playing) return;
    function tick(now) {
      const last = lastRef.current || now;
      const dt = (now - last) / 1000;
      lastRef.current = now;
      setT(prev => {
        let next = prev + dt;
        if (next >= duration) next = 0; // loop
        return next;
      });
      rafRef.current = requestAnimationFrame(tick);
    }
    rafRef.current = requestAnimationFrame(tick);
    return () => { cancelAnimationFrame(rafRef.current); lastRef.current = 0; };
  }, [playing, duration]);

  return (
    <div className="scene-wrap">
      {label && <div className="felt-label">{label}</div>}
      <div className="felt" style={{ height }}>
        {render(t)}
      </div>
      <div className="scene-controls">
        <button onClick={() => setPlaying(p => !p)} className="scene-btn" aria-label={playing ? "Pause" : "Play"}>
          {playing ? (
            <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="5" width="4" height="14" rx="1"/><rect x="14" y="5" width="4" height="14" rx="1"/></svg>
          ) : (
            <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><polygon points="6,4 20,12 6,20"/></svg>
          )}
        </button>
        <div className="scrubber" onClick={(e) => {
          const r = e.currentTarget.getBoundingClientRect();
          const ratio = (e.clientX - r.left) / r.width;
          setT(Math.max(0, Math.min(1, ratio)) * duration);
          setPlaying(false);
        }}>
          <div className="scrubber-fill" style={{ width: `${(t / duration) * 100}%` }} />
        </div>
        <button onClick={() => { setT(0); setPlaying(true); }} className="scene-btn" aria-label="Restart">
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><polyline points="1 4 1 10 7 10"/><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/></svg>
        </button>
      </div>
      {caption && <div className="felt-caption">{caption}</div>}
    </div>
  );
}

/* =========================================================
   SCENE 1 — Forming a Room (flip 4, face 3, leave 1)
   Timeline (seconds):
     0.0  - deck on left
     0.5  - card 1 flies & flips
     1.2  - card 2
     1.9  - card 3
     2.6  - card 4   (room complete)
     3.6  - mark 3 as "must face"
     5.0  - 3 cards slide off (resolved)
     6.5  - leftover stays in place; new label "carries over"
     8.0  - end (loop)
   ========================================================= */
function FormingARoom() {
  const DURATION = 9;
  const FELT_W = 720, FELT_H = 360;

  const room = [
    { suit: "diamonds", value: 8 },   // weapon
    { suit: "clubs",    value: 5 },   // monster
    { suit: "hearts",   value: 7 },   // potion
    { suit: "spades",   value: 11 },  // monster (left behind)
  ];

  function render(t) {
    // Deck position
    const deckX = 60, deckY = FELT_H / 2 - 100;

    // 2x2 grid for room
    const slots = [
      { x: 240, y: 50 },
      { x: 400, y: 50 },
      { x: 240, y: 200 },
      { x: 400, y: 200 },
    ];

    // Indices 0..3 fly times
    const flyStarts = [0.4, 1.1, 1.8, 2.5];
    const flyDur = 0.55;

    // "Must face" highlight at 3.6
    const highlightStart = 3.6;
    const highlightEnd = 4.8;

    // Resolved (3 fade & slide off-bottom) at 5.0
    const resolveStart = 5.0;
    const resolveEnd = 6.0;

    // Show "carries over" emphasis on leftover at 6.6 → 8.0
    const carryStart = 6.6;
    const carryEnd = 8.0;

    return (
      <>
        {/* Deck back */}
        <div className="abs" style={{ left: deckX, top: deckY }}>
          <div style={{ position: "relative" }}>
            <div className="abs" style={{ left: 4, top: 4, opacity: 0.6 }}><PlayingCard flipped size="md" /></div>
            <div className="abs" style={{ left: 2, top: 2, opacity: 0.8 }}><PlayingCard flipped size="md" /></div>
            <PlayingCard flipped size="md" />
            <div style={{ marginTop: 8, fontFamily: "var(--font-sans)", fontSize: 11, letterSpacing: "0.18em", textTransform: "uppercase", color: "rgba(243,235,216,0.6)", textAlign: "center" }}>Dungeon</div>
          </div>
        </div>

        {/* Each card */}
        {room.map((c, i) => {
          const slot = slots[i];
          const fs = flyStarts[i];
          const flyT = ease(range(t, fs, fs + flyDur), "io");
          const x = lerp(deckX, slot.x, flyT);
          const y = lerp(deckY, slot.y, flyT);
          // flip after midway
          const flipped = flyT < 0.55;
          // tilt for fly
          const rot = (1 - flyT) * (i % 2 === 0 ? -10 : 10);

          // resolve slide for first 3 cards (i=0,1,2)
          let extraY = 0, fade = 1;
          if (i < 3) {
            const r = ease(range(t, resolveStart + i * 0.18, resolveEnd + i * 0.18), "in");
            extraY = r * 320;
            fade = 1 - r;
          }

          // leftover (i=3) — pulse during carry phase
          let scale = 1;
          if (i === 3 && t > carryStart) {
            const cT = range(t, carryStart, carryEnd);
            scale = 1 + Math.sin(cT * Math.PI * 3) * 0.04;
          }

          // hide before fly starts
          if (t < fs) return null;

          return (
            <div className="abs cardpos" key={i} style={{
              left: x, top: y + extraY,
              transform: `rotate(${rot}deg) scale(${scale})`,
              opacity: fade,
              transition: "opacity 0.2s",
            }}>
              <PlayingCard suit={c.suit} value={c.value} size="md" flipped={flipped} />
            </div>
          );
        })}

        {/* "Must face 3" highlight overlay rings */}
        {t >= highlightStart && t < resolveStart && (
          [0, 1, 2].map((i) => {
            const slot = slots[i];
            const localT = range(t, highlightStart + i * 0.12, highlightStart + i * 0.12 + 0.4);
            const op = Math.sin(localT * Math.PI) * 0.9;
            return (
              <div key={`r${i}`} className="abs ring" style={{
                left: slot.x - 6, top: slot.y - 6,
                width: 132, height: 178,
                opacity: op,
              }} />
            );
          })
        )}

        {/* Caption banners (slide from bottom) */}
        <CaptionBanner show={t > 0.2 && t < 3.4} text="Flip 4 cards from the dungeon to form a Room." />
        <CaptionBanner show={t >= 3.4 && t < 5.6} text="Resolve any 3 of the 4 cards, in any order." accent />
        <CaptionBanner show={t >= 6.4 && t < 8.4} text="The 4th card carries over to your next Room." />

        {/* Pointer arrow on leftover during carry */}
        {t >= carryStart && t < carryEnd && (
          <div className="abs" style={{ left: slots[3].x + 130, top: slots[3].y + 60 }}>
            <svg width="60" height="40" viewBox="0 0 60 40" fill="none">
              <path d="M58 20 L 8 20 M 18 8 L 8 20 L 18 32" stroke="#e6b955" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" />
            </svg>
          </div>
        )}
      </>
    );
  }

  return (
    <AnimatedScene
      duration={DURATION}
      render={render}
      height={FELT_H}
      label="Forming a Room"
      caption="Each turn you face a four-card Room. Resolve three; the fourth follows you."
    />
  );
}

function CaptionBanner({ show, text, accent }) {
  return (
    <div
      className="caption-banner"
      style={{
        opacity: show ? 1 : 0,
        transform: `translateX(-50%) translateY(${show ? 0 : 10}px)`,
        background: accent ? "rgba(168,50,41,0.92)" : "rgba(15,11,8,0.88)",
        borderColor: accent ? "rgba(244,212,136,0.5)" : "rgba(198,154,63,0.4)",
      }}
    >
      {text}
    </div>
  );
}

/* =========================================================
   SCENE 2 — Fighting: barehanded vs with weapon
   Two columns: same monster, two outcomes
   ========================================================= */
function FightingComparison() {
  const DURATION = 7.5;

  function render(t) {
    // PHASE A: 0 - 1.2 setup (HP shows 20, monster appears)
    // PHASE B: 1.2 - 2.6  LEFT: barehanded fight resolves; HP drops to 9 (-11)
    // PHASE C: 2.6 - 4.0 weapon equips on right
    // PHASE D: 4.0 - 5.5 RIGHT: same monster, weapon absorbs 5; -6 dmg, HP 14
    // PHASE E: 5.5 - 7.5 hold result, label shows comparison

    const monsterValue = 11;
    const weaponValue = 5;

    // Left column health
    const leftHpStart = 20;
    const leftHpEnd = 20 - monsterValue; // 9
    const leftDamageT = ease(range(t, 1.4, 2.2), "out");
    const leftHp = Math.round(lerp(leftHpStart, leftHpEnd, leftDamageT));

    // Right health
    const rightHpStart = 20;
    const rightHpEnd = 20 - (monsterValue - weaponValue); // 14
    const rightDamageT = ease(range(t, 4.4, 5.2), "out");
    const rightHp = Math.round(lerp(rightHpStart, rightHpEnd, rightDamageT));

    // Monster appearance
    const monsterAppearL = ease(range(t, 0.3, 0.9), "out");
    const monsterAppearR = ease(range(t, 0.5, 1.1), "out");
    // Monster shake on impact (left)
    const shakeLeft = (t > 1.2 && t < 1.7) ? Math.sin((t - 1.2) * 60) * 4 : 0;
    // Monster fade on right after defeat
    const monsterFadeR = ease(range(t, 5.0, 5.6), "in");

    // Weapon slide-in on right
    const weaponInT = ease(range(t, 2.6, 3.5), "out");
    const weaponX = lerp(-100, 0, weaponInT);

    // Damage number flying up
    const leftDmgPop = range(t, 1.4, 2.4);
    const rightDmgPop = range(t, 4.4, 5.4);

    return (
      <div className="cmp-grid">
        {/* LEFT */}
        <div className="cmp-col">
          <div className="cmp-label">Barehanded</div>
          <HealthBar hp={leftHp} max={20} />
          <div className="cmp-stage">
            {/* Monster card */}
            <div className="abs" style={{
              left: "50%",
              top: 30,
              transform: `translateX(calc(-50% + ${shakeLeft}px)) scale(${monsterAppearL})`,
              opacity: monsterAppearL,
            }}>
              <PlayingCard suit="spades" value={monsterValue} size="md" />
            </div>
            {/* Hit flash */}
            {t > 1.2 && t < 1.55 && (
              <div className="abs hit-flash" style={{ left: "50%", top: 80, transform: "translateX(-50%)" }} />
            )}
            {/* Damage number */}
            {leftDmgPop > 0 && leftDmgPop < 1 && (
              <div className="abs dmg-pop" style={{
                left: "50%",
                top: lerp(120, 30, leftDmgPop),
                transform: `translateX(-50%) scale(${1 + leftDmgPop * 0.4})`,
                opacity: 1 - leftDmgPop,
              }}>−11</div>
            )}
          </div>
          <div className="cmp-math">
            <span className="m-token m-monster">11</span>
            <span className="m-op">→</span>
            <span className="m-token m-dmg">−11 HP</span>
          </div>
        </div>

        {/* RIGHT */}
        <div className="cmp-col">
          <div className="cmp-label">With a Weapon</div>
          <HealthBar hp={rightHp} max={20} />
          <div className="cmp-stage">
            {/* Weapon slides in first */}
            <div className="abs" style={{
              left: 30 + weaponX,
              top: 30,
              opacity: weaponInT,
            }}>
              <PlayingCard suit="diamonds" value={weaponValue} size="md" />
            </div>
            {/* Monster appears */}
            <div className="abs" style={{
              right: 30,
              top: 30,
              transform: `scale(${monsterAppearR})`,
              opacity: monsterAppearR * (1 - monsterFadeR),
            }}>
              <PlayingCard suit="spades" value={monsterValue} size="md" />
            </div>
            {/* Damage number */}
            {rightDmgPop > 0 && rightDmgPop < 1 && (
              <div className="abs dmg-pop sm" style={{
                left: "50%",
                top: lerp(140, 60, rightDmgPop),
                transform: `translateX(-50%) scale(${1 + rightDmgPop * 0.3})`,
                opacity: 1 - rightDmgPop,
              }}>−6</div>
            )}
            {/* "stack" indicator after defeat */}
            {t > 5.6 && (
              <div className="abs" style={{ left: 30, top: 30, opacity: 0.85 }}>
                <PlayingCard suit="diamonds" value={weaponValue} size="md" />
                <div className="abs" style={{ left: 16, top: -12 }}>
                  <PlayingCard suit="spades" value={monsterValue} size="sm" />
                </div>
              </div>
            )}
          </div>
          <div className="cmp-math">
            <span className="m-token m-monster">11</span>
            <span className="m-op">−</span>
            <span className="m-token m-weapon">5</span>
            <span className="m-op">=</span>
            <span className="m-token m-dmg">−6 HP</span>
          </div>
        </div>
      </div>
    );
  }

  return (
    <AnimatedScene
      duration={DURATION}
      render={render}
      height={460}
      label="Bare hands vs. weapons"
      caption="A weapon absorbs damage equal to its value. The rest hits you."
    />
  );
}

function HealthBar({ hp, max }) {
  const pct = (hp / max) * 100;
  const lowering = hp < max * 0.6;
  return (
    <div className="hp-row">
      <div className="hp-icon">
        <svg width="18" height="18" viewBox="0 0 24 24" fill="#a83229"><path d="M12 21s-7-4.35-9.5-9C.5 7.5 4 3 8 3c2 0 3 1 4 2 1-1 2-2 4-2 4 0 7.5 4.5 5.5 9C19 16.65 12 21 12 21z"/></svg>
        <span className="hp-num">{hp}</span>
      </div>
      <div className="hp-bar">
        <div className="hp-fill" style={{ width: `${pct}%`, background: lowering ? "#a83229" : "#4f6b3a" }} />
      </div>
    </div>
  );
}

/* =========================================================
   SCENE 3 — Weapon Degradation
   ========================================================= */
function WeaponDegradation() {
  const DURATION = 11;

  function render(t) {
    // Weapon: 5 of Diamonds, persistent on left
    // Encounter sequence:
    //   1.0 - 2.5  Queen (12) appears, weapon kills her (no last-monster constraint yet)
    //              -> stack on weapon, "last killed: 12"
    //   3.0 - 4.5  6 of Clubs appears, 6 ≤ 12, weapon kills it
    //              -> stack, "last killed: 6"
    //   5.0 - 7.0  Queen of Spades (12) appears
    //              -> 12 > 6, weapon CAN'T be used. red X on weapon
    //   7.0 - 9.0  message: "Must fight barehanded for 12"
    //   9.0 - 11   reset/hold

    const weaponX = 60, weaponY = 80;
    const monsterX = 360;

    // Stack "last killed" tracker
    let lastKilled = null;
    if (t >= 2.4 && t < 4.2) lastKilled = 12;
    else if (t >= 4.2 && t < 11) lastKilled = 6;

    // Helper to draw a monster encounter
    function monster(suit, value, startT, killT, exitT) {
      const appear = ease(range(t, startT, startT + 0.6), "out");
      const slideToWeapon = ease(range(t, killT, killT + 0.5), "in");
      // After kill, monster lands ONTO the weapon stack
      const finalX = weaponX + 18 + (lastKilledStackOffset(value));
      const finalY = weaponY - 8;
      const x = lerp(monsterX, finalX, slideToWeapon);
      const y = lerp(80, finalY, slideToWeapon);
      const op = appear * (t > exitT ? 0 : 1);
      const sc = appear * (1 - slideToWeapon * 0.25);
      return { x, y, op, sc, slideToWeapon };
    }

    function lastKilledStackOffset(v) {
      // simple: small horizontal offset
      return v === 12 ? 0 : 12;
    }

    // Three encounter windows
    const m1 = monster("spades", 12, 1.0, 2.0, 4.5);
    const m2 = monster("clubs",  6,  3.0, 4.0, 11);

    // Third encounter — Queen of spades — does NOT slide to weapon (refused)
    const m3App = ease(range(t, 5.0, 5.6), "out");
    const m3Shake = (t > 5.7 && t < 6.0) ? Math.sin((t - 5.7) * 40) * 3 : 0;
    const m3Reject = range(t, 6.2, 7.5);
    const m3X = lerp(monsterX, monsterX, 1) + m3Shake;
    const m3Op = m3App * (1 - ease(m3Reject, "in"));

    // Stacked monsters retained on weapon
    const showStack1 = t >= 2.5 && t < 11;
    const showStack2 = t >= 4.5 && t < 11;

    return (
      <>
        {/* Weapon (always present) */}
        <div className="abs" style={{ left: weaponX, top: weaponY }}>
          <PlayingCard suit="diamonds" value={5} size="md" />
        </div>

        {/* "Last killed" badge above weapon */}
        {lastKilled !== null && (
          <div className="abs lk-badge" style={{ left: weaponX - 4, top: weaponY - 36 }}>
            Last killed: <strong>{lastKilled}</strong>
          </div>
        )}

        {/* Stacked monster 1 — Queen */}
        {showStack1 && (
          <div className="abs" style={{ left: weaponX + 20, top: weaponY - 14 }}>
            <PlayingCard suit="spades" value={12} size="md" />
          </div>
        )}
        {/* Stacked monster 2 — 6 */}
        {showStack2 && (
          <div className="abs" style={{ left: weaponX + 38, top: weaponY - 28 }}>
            <PlayingCard suit="clubs" value={6} size="md" />
          </div>
        )}

        {/* Encounter 1 - Queen flies in then onto weapon */}
        {t >= 1.0 && t < 2.5 && (
          <div className="abs" style={{
            left: m1.x, top: m1.y,
            opacity: m1.op,
            transform: `scale(${m1.sc})`,
          }}>
            <PlayingCard suit="spades" value={12} size="md" />
          </div>
        )}

        {/* Encounter 2 - 6 */}
        {t >= 3.0 && t < 4.5 && (
          <div className="abs" style={{
            left: m2.x, top: m2.y,
            opacity: m2.op,
            transform: `scale(${m2.sc})`,
          }}>
            <PlayingCard suit="clubs" value={6} size="md" />
          </div>
        )}

        {/* Encounter 3 - Queen rejected */}
        {t >= 5.0 && t < 7.5 && (
          <div className="abs" style={{
            left: m3X, top: 80,
            opacity: m3Op,
            transform: `scale(${m3App})`,
          }}>
            <PlayingCard suit="spades" value={12} size="md" />
            {/* Red X over weapon */}
            {t >= 6.0 && t < 7.5 && (
              <div className="abs reject-x" style={{ left: -300, top: 30 }}>
                <svg width="80" height="80" viewBox="0 0 80 80">
                  <line x1="14" y1="14" x2="66" y2="66" stroke="#d44" strokeWidth="6" strokeLinecap="round" />
                  <line x1="66" y1="14" x2="14" y2="66" stroke="#d44" strokeWidth="6" strokeLinecap="round" />
                </svg>
              </div>
            )}
          </div>
        )}

        {/* Captions */}
        <CaptionBanner show={t > 0.2 && t < 2.6}  text="Your 5 of Diamonds — fresh, can fight any monster." />
        <CaptionBanner show={t >= 2.6 && t < 4.6} text="Killed a Queen (12). The 6 is fine — 6 ≤ 12." />
        <CaptionBanner show={t >= 4.6 && t < 6.4} text="Killed the 6. Now last-killed is 6." accent={false} />
        <CaptionBanner show={t >= 6.4 && t < 9.0} text="Another Queen? 12 > 6 — your weapon refuses." accent />
        <CaptionBanner show={t >= 9.0 && t < 11}  text="Fight the Queen barehanded for 12 damage." />
      </>
    );
  }

  return (
    <AnimatedScene
      duration={DURATION}
      render={render}
      height={400}
      label="Weapon Degradation"
      caption="The most important rule: each monster a weapon kills must be ≤ the last."
    />
  );
}

/* =========================================================
   SCENE 4 — Health Potions (one per turn cap)
   ========================================================= */
function HealthPotions() {
  const DURATION = 8;

  function render(t) {
    // HP starts at 12. Two potions in same room.
    // 0.5 - 2.0   first potion (7) appears, drinks, hp 12 -> 19
    // 2.5 - 4.5   second potion (5) appears, drinks BUT discarded with X overlay
    // 5.0 - 7.0   reminder caption
    const startHp = 12;
    const afterFirst = 19;

    const drinkT = ease(range(t, 1.0, 1.8), "out");
    const hp = Math.round(lerp(startHp, afterFirst, drinkT));

    const p1Appear = ease(range(t, 0.4, 0.9), "out");
    const p1Consume = ease(range(t, 1.0, 1.6), "in");
    const p1Y = lerp(80, 30, p1Consume);
    const p1Op = p1Appear * (1 - p1Consume);

    const p2Appear = ease(range(t, 2.6, 3.2), "out");
    const p2X = ease(range(t, 4.0, 4.6), "in");

    return (
      <>
        <div className="abs" style={{ left: 30, top: 30 }}>
          <HealthBar hp={hp} max={20} />
        </div>

        {/* Potion 1 */}
        {t >= 0.4 && t < 1.7 && (
          <div className="abs" style={{
            left: 200, top: p1Y,
            opacity: p1Op,
            transform: `scale(${p1Appear})`,
          }}>
            <PlayingCard suit="hearts" value={7} size="md" />
          </div>
        )}
        {/* heal sparkle */}
        {t > 1.5 && t < 2.0 && (
          <div className="abs" style={{ left: 60, top: 50, opacity: 1 - range(t, 1.5, 2.0) }}>
            <span className="heal-pop">+7</span>
          </div>
        )}

        {/* Potion 2 */}
        {t >= 2.6 && t < 5.0 && (
          <div className="abs" style={{
            left: lerp(420, 600, p2X), top: 60,
            opacity: p2Appear * (1 - p2X),
            transform: `scale(${p2Appear})`,
          }}>
            <PlayingCard suit="hearts" value={5} size="md" />
            {t > 3.2 && (
              <div className="abs discard-x" style={{ left: 0, top: 0 }}>
                <svg width="120" height="178" viewBox="0 0 120 178">
                  <rect x="3" y="3" width="114" height="172" rx="8" fill="rgba(15,11,8,0.55)" />
                  <line x1="20" y1="30" x2="100" y2="148" stroke="#d44" strokeWidth="6" strokeLinecap="round" />
                  <line x1="100" y1="30" x2="20" y2="148" stroke="#d44" strokeWidth="6" strokeLinecap="round" />
                </svg>
              </div>
            )}
          </div>
        )}

        <CaptionBanner show={t > 0.2 && t < 2.4}  text="A Heart restores HP equal to its value (max 20)." />
        <CaptionBanner show={t >= 2.4 && t < 5.0} text="Only one potion per Room — extras are discarded." accent />
        <CaptionBanner show={t >= 5.0 && t < 8}   text="So drink wisely — and choose which to leave for next room." />
      </>
    );
  }

  return (
    <AnimatedScene
      duration={DURATION}
      render={render}
      height={320}
      label="Health Potions"
      caption="One drink per Room. The rest go to the discard pile."
    />
  );
}

/* =========================================================
   SCENE 5 — A Full Sample Turn
   ========================================================= */
function FullTurn() {
  const DURATION = 14;

  function render(t) {
    // 0 - 1.5    Room of 4 cards is dealt
    //              [diamonds 6 (weapon), spades 8, hearts 6, clubs 4]
    // 2.0 - 3.5  Pick up weapon (slides to player slot)
    // 4.0 - 5.5  Fight 4 of Clubs — weapon absorbs all (6-4 ≥ 0) -> stack on weapon
    // 6.0 - 7.5  Drink 6 of Hearts -> hp +6
    // 8.0 - 10   Leftover (8 spades) is highlighted as "carries over"
    // 10 - 11.5  Slide off resolved cards
    // 12 - 14    Next-turn preview: 8 of spades stays, 3 new cards appear

    const slots = [
      { x: 220, y: 30 },
      { x: 360, y: 30 },
      { x: 220, y: 175 },
      { x: 360, y: 175 },
    ];
    const playerWeaponSlot = { x: 80, y: 250 };
    const playerHpStart = 17;

    const cards = [
      { suit: "diamonds", value: 6 },
      { suit: "spades",   value: 8 },
      { suit: "hearts",   value: 6 },
      { suit: "clubs",    value: 4 },
    ];

    const dealStarts = [0.1, 0.45, 0.8, 1.15];

    // Movements
    const wEquip = ease(range(t, 2.0, 3.0), "out");
    const fightStart = 4.0, fightEnd = 5.0;
    const fightT = ease(range(t, fightStart, fightEnd), "in");
    const monsterDefeated = t >= fightEnd;
    const drinkStart = 6.0, drinkEnd = 7.0;
    const drinkT = ease(range(t, drinkStart, drinkEnd), "in");
    const heartConsumed = t >= drinkEnd;

    // HP
    const hp = heartConsumed ? Math.min(20, playerHpStart + 6) : playerHpStart;

    // resolved cards slide off at 10-11
    const resolveT = ease(range(t, 10, 11.2), "in");

    // Phase: leftover highlight
    const carryHL = (t >= 8 && t < 10) ? 1 : 0;
    const carryPulse = carryHL * (0.6 + 0.4 * Math.abs(Math.sin(t * 4)));

    // Reset for next turn
    const newDealT = ease(range(t, 12.0, 13.5), "out");
    const newCards = [
      { suit: "hearts", value: 9, slot: 0 },
      { suit: "diamonds", value: 7, slot: 2 },
      { suit: "clubs", value: 10, slot: 3 },
    ];

    return (
      <>
        {/* HP bar */}
        <div className="abs" style={{ left: 30, top: 12 }}>
          <HealthBar hp={hp} max={20} />
        </div>

        {/* Deck back */}
        <div className="abs" style={{ left: 30, top: 80 }}>
          <PlayingCard flipped size="md" />
          <div style={{ marginTop: 6, fontFamily: "var(--font-sans)", fontSize: 10, letterSpacing: "0.16em", textTransform: "uppercase", color: "rgba(243,235,216,0.5)", textAlign: "center" }}>40 left</div>
        </div>

        {/* Deal animation for original 4 */}
        {cards.map((c, i) => {
          const fs = dealStarts[i];
          const flyT = ease(range(t, fs, fs + 0.5), "io");
          const slot = slots[i];

          // Weapon (i=0) leaves to player slot at 2.0
          let x = lerp(40, slot.x, flyT);
          let y = lerp(90, slot.y, flyT);
          let rot = (1 - flyT) * (i % 2 === 0 ? -10 : 10);
          let op = 1;
          let scale = 1;
          let flipped = flyT < 0.55;

          if (i === 0) {
            // weapon equips down
            x = lerp(slot.x, playerWeaponSlot.x, wEquip);
            y = lerp(slot.y, playerWeaponSlot.y, wEquip);
            // overrides above only after fly is done
            if (t < dealStarts[i] + 0.5) {
              x = lerp(40, slot.x, flyT);
              y = lerp(90, slot.y, flyT);
            }
          }

          if (i === 3) {
            // monster (clubs 4) flies onto weapon during fight
            const slideX = lerp(slot.x, playerWeaponSlot.x + 18, fightT);
            const slideY = lerp(slot.y, playerWeaponSlot.y - 12, fightT);
            if (t >= fightStart) { x = slideX; y = slideY; }
            // After slot reached, sit on stack
            if (t >= fightEnd) { x = playerWeaponSlot.x + 18; y = playerWeaponSlot.y - 12; }
          }

          if (i === 2) {
            // hearts 6 - drinks: scales & fades
            if (t >= drinkStart) {
              const dY = lerp(slot.y, slot.y - 60, drinkT);
              const dOp = 1 - drinkT;
              y = dY;
              op = dOp;
            }
            if (heartConsumed) op = 0;
          }

          if (i === 1) {
            // 8 of spades - carries over. During 10-11.2, stays put. Otherwise slot.x.
            // On reset (12+), it remains.
            if (carryHL) {
              scale = 1 + (Math.sin(t * 6) * 0.04);
            }
            if (t >= 12) {
              // stays in slot 1
            }
          }

          // resolved slide (cards 0,2,3 leave at end)
          if (resolveT > 0 && i !== 1) {
            // i=0 weapon stays equipped (don't slide off)
            if (i === 2 || i === 3) {
              op = (1 - resolveT) * op;
              y += resolveT * 200;
            }
          }

          if (t < fs) return null;
          if (i === 0 && t >= 12 && false) return null; // weapon stays equipped

          return (
            <div className="abs" key={`o${i}`} style={{
              left: x, top: y,
              opacity: op,
              transform: `rotate(${rot}deg) scale(${scale})`,
            }}>
              <PlayingCard suit={c.suit} value={c.value} size="md" flipped={flipped} />
            </div>
          );
        })}

        {/* Player weapon label */}
        {t >= 3.0 && (
          <div className="abs" style={{ left: playerWeaponSlot.x - 4, top: playerWeaponSlot.y - 22, fontFamily: "var(--font-sans)", fontSize: 10, letterSpacing: "0.18em", textTransform: "uppercase", color: "rgba(244,212,136,0.7)" }}>
            Equipped
          </div>
        )}

        {/* New cards for next turn */}
        {newCards.map((nc, j) => {
          const slot = slots[nc.slot];
          const fs = 12.0 + j * 0.3;
          const flyT = ease(range(t, fs, fs + 0.5), "io");
          if (t < fs) return null;
          const x = lerp(40, slot.x, flyT);
          const y = lerp(90, slot.y, flyT);
          const rot = (1 - flyT) * (j % 2 === 0 ? -8 : 8);
          const flipped = flyT < 0.55;
          return (
            <div className="abs" key={`n${j}`} style={{
              left: x, top: y,
              transform: `rotate(${rot}deg)`,
            }}>
              <PlayingCard suit={nc.suit} value={nc.value} size="md" flipped={flipped} />
            </div>
          );
        })}

        {/* Carry-over ring on leftover */}
        {carryHL > 0 && (
          <div className="abs ring" style={{
            left: slots[1].x - 6, top: slots[1].y - 6,
            width: 132, height: 178,
            opacity: carryPulse * 0.9,
          }} />
        )}

        {/* "+6" heal pop */}
        {t > drinkEnd - 0.2 && t < drinkEnd + 0.8 && (
          <div className="abs heal-pop-wrap" style={{ left: 130, top: 30, opacity: 1 - range(t, drinkEnd, drinkEnd + 0.8) }}>
            <span className="heal-pop">+6</span>
          </div>
        )}

        {/* Captions */}
        <CaptionBanner show={t > 0.2 && t < 2.0}    text="A new Room: a weapon, a monster, a potion, and another monster." />
        <CaptionBanner show={t >= 2.0 && t < 4.0}   text="Equip the weapon — the 6 of Diamonds." />
        <CaptionBanner show={t >= 4.0 && t < 6.0}   text="The 4 of Clubs hits for 4 — your weapon absorbs all of it." />
        <CaptionBanner show={t >= 6.0 && t < 8.0}   text="Drink the 6 of Hearts. +6 health (capped at 20)." />
        <CaptionBanner show={t >= 8.0 && t < 11.5}  text="Leave the 8 of Spades for the next Room." accent />
        <CaptionBanner show={t >= 11.5 && t < 14}   text="Three new cards. The 8 of Spades is still here." />
      </>
    );
  }

  return (
    <AnimatedScene
      duration={DURATION}
      render={render}
      height={460}
      label="A Full Turn, Start to Finish"
      caption="A complete turn: deal a Room, equip a weapon, fight, heal, and carry one card forward."
    />
  );
}

Object.assign(window, {
  FormingARoom, FightingComparison, WeaponDegradation, HealthPotions, FullTurn,
  CaptionBanner, HealthBar, AnimatedScene, Felt,
});
