// scenes.jsx — Gold Bank flow animation scenes.
// Story beats (30s):
//   0-4   Beat 1: Producer appears, gold sourced.
//   4-9   Beat 2: Gold travels to Bank; verification (weight, quality, price).
//   9-13  Beat 3: mSSP issued back to Producer (gold-backed stablecoin).
//   13-18 Beat 4: Producer pays 4 recipients.
//   18-23 Beat 5: Recipients onboard as Gold Bank customers.
//   23-30 Beat 6: Flywheel — 24-node network, ongoing transfers pulse.

const W = 1920, H = 1080;
const CX = W / 2, CY = H / 2;

// ── Layout helpers ──────────────────────────────────────────────────────────
// 4 direct recipients in a square around the producer (beat 4), then migrate
// into an inner ring around the bank (beat 5-6).

// Positions in "producer frame" (4 recipients around producer during beat 4).
const PRODUCER_POS = { x: 560, y: CY };
function recipientAroundProducer(i, r = 210) {
  const angle = (-Math.PI / 2) + (i * (Math.PI * 2) / 4); // N, E, S, W
  return { x: PRODUCER_POS.x + Math.cos(angle) * r, y: PRODUCER_POS.y + Math.sin(angle) * r };
}
// Inner ring around the bank (8 nodes) — 4 direct recipients migrate here.
function innerRingPos(i, total = 8, r = 360) {
  const angle = (-Math.PI / 2) + (i * (Math.PI * 2) / total);
  return { x: CX + Math.cos(angle) * r, y: CY + Math.sin(angle) * r };
}
// Outer ring (16 nodes).
function outerRingPos(i, total = 16, r = 560) {
  const angle = (-Math.PI / 2) + ((i + 0.5) * (Math.PI * 2) / total);
  return { x: CX + Math.cos(angle) * r, y: CY + Math.sin(angle) * r };
}

// Recipient roster (4 direct from producer — staff, suppliers, tax, bills)
const RECIPIENTS = [
  { variant: 'worker',   label: 'STAFF',     sublabel: 'PAYROLL'  },
  { variant: 'person',   label: 'SUPPLIERS', sublabel: 'INVOICES' },
  { variant: 'merchant', label: 'TAX',       sublabel: 'NRA · SS' },
  { variant: 'family',   label: 'BILLS',     sublabel: 'UTILITIES'},
];

// Secondary recipients (2 per direct = 8) + 8 more on outer ring = 20.
const SECONDARY_VARIANTS = ['person', 'merchant', 'worker', 'family'];

// ── Camera: gently zooms out as the network grows ───────────────────────────
function useCamera() {
  const t = useTime();
  // Zoom curve: tight on producer early → centered + pulled back later.
  const zoom = interpolate(
    [0, 4, 9, 13, 18, 23, 30],
    [1.25, 1.15, 1.05, 1.0, 0.95, 0.82, 0.78],
    Easing.easeInOutCubic
  )(t);
  // Pan: follow the producer early, then recenter on bank.
  const panX = interpolate(
    [0, 4, 9, 13, 23],
    [-180, -160, -60, 0, 0],
    Easing.easeInOutCubic
  )(t);
  const panY = 0;
  return { zoom, panX, panY };
}

function CameraLayer({ children }) {
  const { zoom, panX, panY } = useCamera();
  return (
    <div style={{
      position: 'absolute', inset: 0,
      transform: `translate(${-panX}px, ${-panY}px) scale(${zoom})`,
      transformOrigin: '50% 50%',
      transition: 'none',
      willChange: 'transform',
    }}>
      {children}
    </div>
  );
}

// ── Backdrop: deep black + dot grid + corner glows ──────────────────────────
function Backdrop() {
  return (
    <>
      <div style={{ position: 'absolute', inset: 0, background: GB.bg }}/>
      <DotGrid opacity={0.04}/>
      <CornerGlow/>
    </>
  );
}

// ── Persistent bank node (appears at beat 2, stays to end) ──────────────────
function BankLayer() {
  const t = useTime();
  // Appears at 3.5s, stays visible
  if (t < 3.2) return null;
  const appear = clamp((t - 3.2) / 1.2, 0, 1);
  const ease = Easing.easeOutCubic(appear);
  const scale = 0.7 + 0.3 * ease;
  const opacity = ease;
  const glowPulse = 0.4 + 0.6 * Math.max(0, Math.sin((t - 3.5) * 1.2));
  return (
    <div style={{ opacity }}>
      <BankNode x={CX} y={CY} size={260} scale={scale} glow={t > 4.5 && t < 9.5}/>
      {/* bank label */}
      <div style={{
        position: 'absolute', left: CX, top: CY + 175,
        transform: 'translate(-50%, 0)',
        fontFamily: "'JetBrains Mono', ui-monospace, monospace",
        fontSize: 11, letterSpacing: '0.22em', color: GB.gold,
        opacity: opacity * 0.9,
        whiteSpace: 'nowrap',
      }}>
        ST. THERESA DEVELOPMENT BANK
      </div>
    </div>
  );
}

// ── Beat 1-3: Producer node (left side) ─────────────────────────────────────
function ProducerLayer() {
  const t = useTime();
  if (t > 23.5) return null;
  const appear = clamp(t / 1.2, 0, 1);
  const fade = 1 - clamp((t - 22) / 1.5, 0, 1);
  const opacity = Math.min(appear, fade);
  // Onboarded from beat 3 onwards (once mSSP issued, producer is a customer)
  const onboarded = t > 10;
  return (
    <Node
      x={PRODUCER_POS.x} y={PRODUCER_POS.y}
      variant="miner"
      label="PRODUCER"
      sublabel={onboarded ? '0420 · 1184 · 9302' : 'GOLD · JUBA'}
      onboarded={onboarded}
      accent
      opacity={opacity}
      size={92}
    />
  );
}

// ── Beat 1: Gold emerges from producer (0-4s) ───────────────────────────────
function Beat1_GoldEmerges() {
  const t = useTime();
  const active = t >= 0.5 && t < 4.5;
  if (!active) return null;
  // Gold rises from below producer, floats next to them
  const local = t - 0.5;
  const rise = Easing.easeOutCubic(clamp(local / 1.5, 0, 1));
  const x = PRODUCER_POS.x + 80;
  const y = PRODUCER_POS.y + 60 - rise * 40;
  const opacity = clamp(local / 0.4, 0, 1);
  const rot = Math.sin(local * 1.2) * 8;
  return <GoldNugget x={x} y={y} size={52} rot={rot} opacity={opacity}/>;
}

// ── Beat 2: Gold travels from producer to bank (4-6s) ───────────────────────
function Beat2_GoldTravels() {
  const t = useTime();
  if (t < 4.0 || t > 7.5) return null;
  const local = clamp((t - 4.0) / 2.2, 0, 1);
  const eased = Easing.easeInOutCubic(local);
  const path = arcPath(PRODUCER_POS.x + 80, PRODUCER_POS.y + 20, CX, CY, -0.2);
  const p = path(eased);
  const opacity = local < 0.95 ? 1 : 1 - (local - 0.95) / 0.05;
  const rot = eased * 540;
  return (
    <>
      {/* dotted arc in the wake */}
      <ArcLine
        x1={PRODUCER_POS.x + 80} y1={PRODUCER_POS.y + 20}
        x2={CX} y2={CY}
        curvature={-0.2}
        dash="3 8"
        opacity={0.4 * Math.max(0, 1 - Math.abs(local - 0.5) * 1.2)}
        color={GB.gold}
      />
      <GoldNugget x={p.x} y={p.y} size={48} rot={rot} opacity={opacity}/>
    </>
  );
}

// ── Beat 2: Verification HUD (around bank, 6-9s) ────────────────────────────
function Beat2_Verification() {
  const t = useTime();
  if (t < 6.2 || t > 9.2) return null;
  const local = t - 6.2;
  const items = [
    { label: 'WEIGHT',  value: '12.480 g', at: 0.2 },
    { label: 'QUALITY', value: '22K · 91.6%',   at: 0.7 },
    { label: 'PRICE',   value: 'SSP 4.21M',     at: 1.2 },
    { label: 'AGREED',  value: '✓',             at: 1.8 },
  ];
  // Position verification chips in a vertical column to the right of the bank.
  const baseX = CX + 230;
  const baseY = CY - 80;
  return (
    <>
      {items.map((it, i) => {
        const delay = it.at;
        const appear = clamp((local - delay) / 0.3, 0, 1);
        const exit = clamp((local - 2.6) / 0.4, 0, 1);
        const opacity = appear * (1 - exit);
        const tx = (1 - appear) * 20;
        return (
          <div key={it.label} style={{
            position: 'absolute',
            left: baseX, top: baseY + i * 52,
            transform: `translate(${tx}px, 0)`,
            opacity,
            display: 'flex', alignItems: 'center', gap: 14,
            padding: '10px 16px',
            background: 'rgba(255,255,255,0.05)',
            border: `1px solid ${GB.border}`,
            borderRadius: 8,
            backdropFilter: 'blur(10px)',
            willChange: 'transform, opacity',
            minWidth: 260,
          }}>
            <div style={{
              fontFamily: "'JetBrains Mono', monospace",
              fontSize: 10, letterSpacing: '0.18em', color: GB.muted,
              width: 72,
            }}>{it.label}</div>
            <div style={{
              fontFamily: "'JetBrains Mono', monospace",
              fontSize: 15, color: it.value === '✓' ? GB.gold : GB.fg,
              fontVariantNumeric: 'tabular-nums',
              fontWeight: it.value === '✓' ? 600 : 500,
            }}>{it.value}</div>
          </div>
        );
      })}
    </>
  );
}

// ── Beat 3: mSSP issued back to producer (9-12.5s) ──────────────────────────
function Beat3_MsspIssued() {
  const t = useTime();
  if (t < 8.8 || t > 12.8) return null;
  const local = clamp((t - 8.8) / 2.6, 0, 1);
  const eased = Easing.easeInOutCubic(local);
  const path = arcPath(CX, CY, PRODUCER_POS.x + 80, PRODUCER_POS.y + 20, 0.2);
  const p = path(eased);
  const opacity = local < 0.92 ? clamp(local * 6, 0, 1) : 1 - (local - 0.92) / 0.08;
  return (
    <>
      <ArcLine
        x1={CX} y1={CY}
        x2={PRODUCER_POS.x + 80} y2={PRODUCER_POS.y + 20}
        curvature={0.2}
        dash="2 6"
        opacity={0.45 * Math.max(0, 1 - Math.abs(local - 0.5) * 1.3)}
        color={GB.gold}
      />
      <MsspChip x={p.x} y={p.y} size={42} opacity={opacity} pulse={local * 2}/>
    </>
  );
}

// ── Beat 4: Producer pays 4 recipients (13-18s) ─────────────────────────────
function Beat4_Payments() {
  const t = useTime();
  if (t < 12.8 || t > 23.5) return null;
  // Recipients appear, then mSSP chips travel from producer to each.
  return (
    <>
      {RECIPIENTS.map((r, i) => {
        const pos = recipientAroundProducer(i);
        const appearStart = 12.8 + i * 0.15;
        const appear = clamp((t - appearStart) / 0.6, 0, 1);
        // After beat 5 ends (~22s), direct recipients migrate to inner ring around bank.
        const migrateStart = 22.0;
        const migrateEnd = 23.5;
        const migrate = clamp((t - migrateStart) / (migrateEnd - migrateStart), 0, 1);
        const mEased = Easing.easeInOutCubic(migrate);
        const target = innerRingPos(i * 2, 8, 340);
        const x = pos.x + (target.x - pos.x) * mEased;
        const y = pos.y + (target.y - pos.y) * mEased;
        // Onboarded after beat 5 begins
        const onboarded = t > 18.5;
        // Label visible from appearance; sublabel swaps to account number once onboarded.
        const showLabel = appear > 0.3;
        return (
          <Node
            key={r.label}
            x={x} y={y}
            variant={r.variant}
            label={showLabel ? r.label : undefined}
            sublabel={showLabel ? (onboarded ? `ACCT · ${1100 + i * 137}` : r.sublabel) : undefined}
            onboarded={onboarded}
            size={64}
            opacity={Easing.easeOutCubic(appear)}
            scale={0.6 + 0.4 * Easing.easeOutBack(appear)}
          />
        );
      })}

      {/* mSSP travel chips from producer → each recipient */}
      {RECIPIENTS.map((r, i) => {
        const sendStart = 13.4 + i * 0.35;
        const sendDur = 1.4;
        const local = (t - sendStart) / sendDur;
        if (local < 0 || local > 1.05) return null;
        const eased = Easing.easeInOutCubic(clamp(local, 0, 1));
        const pos = recipientAroundProducer(i);
        const path = arcPath(PRODUCER_POS.x + 40, PRODUCER_POS.y, pos.x, pos.y, 0.15);
        const p = path(eased);
        const opacity = local < 0.9 ? clamp(local * 5, 0, 1) : 1 - (local - 0.9) / 0.1;
        return (
          <React.Fragment key={`chip-${i}`}>
            <ArcLine
              x1={PRODUCER_POS.x + 40} y1={PRODUCER_POS.y}
              x2={pos.x} y2={pos.y}
              curvature={0.15}
              dash="2 5"
              opacity={0.35 * Math.max(0, 1 - Math.abs(eased - 0.5) * 1.2)}
              color={GB.gold}
            />
            <MsspChip x={p.x} y={p.y} size={30} opacity={opacity} pulse={local}/>
          </React.Fragment>
        );
      })}
    </>
  );
}

// ── Beat 5: Onboarding pulse (18-22s) ───────────────────────────────────────
// Visualize each recipient getting a gold ring pulse as they onboard.
function Beat5_OnboardPulse() {
  const t = useTime();
  if (t < 18.3 || t > 22.2) return null;
  return (
    <>
      {RECIPIENTS.map((r, i) => {
        const pulseStart = 18.3 + i * 0.25;
        const local = (t - pulseStart);
        if (local < 0 || local > 1.8) return null;
        const p = clamp(local / 1.6, 0, 1);
        const pos = recipientAroundProducer(i);
        const radius = 50 + p * 70;
        const opacity = (1 - p) * 0.9;
        return (
          <div key={`pulse-${i}`} style={{
            position: 'absolute',
            left: pos.x, top: pos.y,
            width: radius * 2, height: radius * 2,
            transform: 'translate(-50%, -50%)',
            border: `1.5px solid ${GB.gold}`,
            borderRadius: '50%',
            opacity,
            pointerEvents: 'none',
          }}/>
        );
      })}
    </>
  );
}

// ── Beat 6: Flywheel — secondary recipients + ongoing transfers (23-30s) ────
function Beat6_Flywheel() {
  const t = useTime();
  if (t < 22.5) return null;

  // Secondary nodes appear as the 4 direct recipients fan outward.
  // 20 additional nodes — 4 on inner ring + 16 on outer ring (+ 4 direct migrating = 24 total on network, +producer)
  const innerIndices = [1, 3, 5, 7]; // slots between migrated direct recipients (which take 0,2,4,6)
  const innerExtras = innerIndices.map((slot, i) => ({
    slot, pos: innerRingPos(slot, 8, 340),
    variant: SECONDARY_VARIANTS[i % SECONDARY_VARIANTS.length],
    fromDirect: i, // paired with direct recipient i
    delay: 23.2 + i * 0.12,
  }));
  const outerNodes = [];
  for (let i = 0; i < 16; i++) {
    outerNodes.push({
      pos: outerRingPos(i, 16, 560),
      variant: SECONDARY_VARIANTS[i % SECONDARY_VARIANTS.length],
      delay: 24.5 + (i * 0.08),
      fromInner: i % 8, // paired with an inner node
    });
  }

  return (
    <>
      {/* Inner extras */}
      {innerExtras.map((n, i) => {
        const appear = clamp((t - n.delay) / 0.5, 0, 1);
        const ease = Easing.easeOutCubic(appear);
        return (
          <Node
            key={`inner-${i}`}
            x={n.pos.x} y={n.pos.y}
            variant={n.variant}
            onboarded
            size={52}
            opacity={ease}
            scale={0.5 + 0.5 * ease}
          />
        );
      })}

      {/* Outer ring */}
      {outerNodes.map((n, i) => {
        const appear = clamp((t - n.delay) / 0.5, 0, 1);
        const ease = Easing.easeOutCubic(appear);
        return (
          <Node
            key={`outer-${i}`}
            x={n.pos.x} y={n.pos.y}
            variant={n.variant}
            onboarded
            size={44}
            opacity={ease * 0.92}
            scale={0.5 + 0.5 * ease}
          />
        );
      })}

      {/* Connecting arcs (faint constellation) */}
      <FlywheelConstellation innerExtras={innerExtras} outerNodes={outerNodes}/>

      {/* Pulsing mSSP transfers along random arcs */}
      <FlywheelTransfers innerExtras={innerExtras} outerNodes={outerNodes}/>
    </>
  );
}

function FlywheelConstellation({ innerExtras, outerNodes }) {
  const t = useTime();
  // Arcs from direct recipients (inner ring slot 0,2,4,6) → inner extras (1,3,5,7)
  // And from each inner node → two outer nodes.
  const arcs = [];
  // Direct → inner extras (pairs)
  RECIPIENTS.forEach((_, i) => {
    const from = innerRingPos(i * 2, 8, 340);
    const to = innerRingPos(i * 2 + 1, 8, 340);
    arcs.push({ from, to, delay: 23.4 + i * 0.1 });
  });
  // Inner (8 slots) → outer (16)
  for (let s = 0; s < 8; s++) {
    const from = innerRingPos(s, 8, 340);
    const to1 = outerRingPos(s * 2, 16, 560);
    const to2 = outerRingPos(s * 2 + 1, 16, 560);
    arcs.push({ from, to: to1, delay: 24.8 + s * 0.05 });
    arcs.push({ from, to: to2, delay: 24.9 + s * 0.05 });
  }

  return (
    <svg style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }} width="100%" height="100%">
      {arcs.map((a, i) => {
        const appear = clamp((t - a.delay) / 0.8, 0, 1);
        if (appear <= 0) return null;
        const dx = a.to.x - a.from.x, dy = a.to.y - a.from.y;
        const mx = (a.from.x + a.to.x) / 2, my = (a.from.y + a.to.y) / 2;
        const len = Math.sqrt(dx*dx + dy*dy) || 1;
        const curvature = 0.12;
        const nx = -dy / len, ny = dx / len;
        const cx = mx + nx * len * curvature;
        const cy = my + ny * len * curvature;
        return (
          <path key={i}
            d={`M ${a.from.x},${a.from.y} Q ${cx},${cy} ${a.to.x},${a.to.y}`}
            fill="none" stroke={GB.gold} strokeWidth="0.8"
            strokeDasharray="2 5" opacity={0.35 * appear}/>
        );
      })}
    </svg>
  );
}

function FlywheelTransfers({ innerExtras, outerNodes }) {
  const t = useTime();
  // Schedule a handful of traveling chips — from inner nodes to outer nodes,
  // staggered so the network feels alive.
  const transfers = [];
  for (let k = 0; k < 12; k++) {
    const innerSlot = k % 8;
    const outerIdx = (k * 3) % 16;
    const start = 25.2 + k * 0.35;
    transfers.push({
      from: innerRingPos(innerSlot, 8, 340),
      to: outerRingPos(outerIdx, 16, 560),
      start,
      dur: 1.3,
    });
  }
  // Also bank → inner, to show continued issuance from the bank.
  for (let k = 0; k < 6; k++) {
    const innerSlot = (k * 3) % 8;
    transfers.push({
      from: { x: CX, y: CY },
      to: innerRingPos(innerSlot, 8, 340),
      start: 24.6 + k * 0.25,
      dur: 1.0,
    });
  }

  return (
    <>
      {transfers.map((tr, i) => {
        const local = (t - tr.start) / tr.dur;
        if (local < 0 || local > 1.05) return null;
        const eased = Easing.easeInOutCubic(clamp(local, 0, 1));
        const path = arcPath(tr.from.x, tr.from.y, tr.to.x, tr.to.y, 0.12);
        const p = path(eased);
        const opacity = local < 0.85 ? clamp(local * 4, 0, 1) : 1 - (local - 0.85) / 0.15;
        return <MsspChip key={`tr-${i}`} x={p.x} y={p.y} size={22} opacity={opacity * 0.9} pulse={local * 2}/>;
      })}
    </>
  );
}

// ── Captions (minimal, one per beat) ────────────────────────────────────────
function Captions({ show = true }) {
  if (!show) return null;
  return (
    <>
      <Sprite start={0.4} end={3.8}>
        <Caption kicker="01 · SOURCE" line="Gold is mined."/>
      </Sprite>
      <Sprite start={4.2} end={8.8}>
        <Caption kicker="02 · VERIFY" line="The bank assays and agrees a price."/>
      </Sprite>
      <Sprite start={9.0} end={12.6}>
        <Caption kicker="03 · ISSUE" line="mSSP is issued — a gold-backed stablecoin."/>
      </Sprite>
      <Sprite start={12.8} end={17.8}>
        <Caption kicker="04 · PAY" line="The producer pays in mSSP."/>
      </Sprite>
      <Sprite start={18.0} end={22.4}>
        <Caption kicker="05 · ONBOARD" line="Each recipient becomes a customer."/>
      </Sprite>
      <Sprite start={22.6} end={29.9}>
        <Caption kicker="06 · CIRCULATE" line="And mSSP keeps moving."/>
      </Sprite>
    </>
  );
}

// ── Top chrome: tiny wordmark and timer ─────────────────────────────────────
function TopChrome() {
  const t = useTime();
  return (
    <div style={{
      position: 'absolute', top: 40, left: 48, right: 48,
      display: 'flex', justifyContent: 'space-between', alignItems: 'center',
      pointerEvents: 'none', zIndex: 2,
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
        <img src="/animations/gold-bank-mark.svg" width="28" height="28" style={{ display: 'block' }}/>
        <div style={{
          fontFamily: 'Fraunces, Georgia, serif',
          fontSize: 20, color: GB.fg, fontWeight: 500, letterSpacing: '-0.01em',
        }}>
          St. Theresa<span style={{ color: GB.gold }}>.</span>
        </div>
      </div>
      <div style={{
        fontFamily: "'JetBrains Mono', monospace",
        fontSize: 10, letterSpacing: '0.24em', color: GB.muted,
      }}>
        HOW mSSP MOVES
      </div>
    </div>
  );
}

Object.assign(window, {
  W, H, CX, CY,
  Backdrop, BankLayer, ProducerLayer,
  Beat1_GoldEmerges, Beat2_GoldTravels, Beat2_Verification,
  Beat3_MsspIssued, Beat4_Payments, Beat5_OnboardPulse, Beat6_Flywheel,
  Captions, TopChrome, CameraLayer,
});
