// crovi-scene3.jsx — Audit scene.

// Identity fallback for the palette transformer; bare `T` is stripped by
// Babel's TS preset because it looks like a type parameter.
const tt = (typeof window !== 'undefined' && typeof window.T === 'function')
  ? window.T
  : (s) => s;

//
// Continuity: scene 2 ends with the camera zoomed onto the 4 sample-side keepers
// at world (COL_LEAF=920, varying y) under a 1.55× zoom focused at (1050, cy-90).
// Scene 3 picks up those EXACT leaves — same data, same chips, same screen
// positions — and reflows them into the audit column on the left while the
// audit-agent card materializes on the right.
//
// Beat A (0.0–0.85s): leaves slide+scale from scene-2 end positions into the
//                     scene-3 column on the left. No fresh leaves spawn.
// Beat B (0.9–1.6s):  audit agent card enters from the right.
// Beat C (1.7–5.2s):  three sequential checks unfold inside the card.
// Beat D (5.6–7.0s):  CHTN + NDRI dim out (failed); MD Anderson + Karolinska
//                     stay (passed). Audit card holds at right-side geometry
//                     so scene 4 can morph from THIS exact card.

// ── Geometry (column on the left, audit card on the right) ────────────────
const S3_LEAF_W   = 260;
const S3_LEAF_H   = 88;
const S3_LEAF_GAP = 24;
const S3_COL_X    = 380;                            // chip LEFT edge
const S3_COL_CX   = S3_COL_X + S3_LEAF_W / 2;       // 510

// ── Scene-2 end-frame inheritance ─────────────────────────────────────────
// Scene 2 final camera: fx=1050, fy=cy-203, s=1.55. World transform applied is
// translate(-90, 203) scale(1.55) about origin (1050, cy-203). Keeper leaves
// live at world (920, cy + dy), and each LeafChip has `transform:
// translate(0, -50%)`, so the chip's natural pre-transform center is at
// world (920 + 130, leaf_y) = (1050, leaf_y).
// Screen position for a world point (X, Y) under that camera:
//   sx = (X - 1050) * 1.55 + 1050 + (-90) = (X - 1050) * 1.55 + 960
//   sy = (Y -  337) * 1.55 +  337 + (203) = (Y -  337) * 1.55 + 540
// World center X = 1050 → screen center X = 960.
const S2_END_SCALE   = 1.55;
const S2_END_LEAF_CX = 960;      // chip center X on screen at scene-2 end
const S2_END_FY      = 337;      // camera focal Y at scene-2 end (= cy - 203)
// World Y per keeper. With biobanks_y = cy - 220*1.30 = cy - 286 and amcs_y = cy
// (scene 2's full-vSpread layout), and leaf spacing 80:
//   Biobanks j=0 (CHTN):       cy - 286 - 80 = cy - 366 = 174
//   Biobanks j=1 (NDRI):       cy - 286            = 254
//   AMCs    j=0 (MD Anderson): cy - 1.5*80         = 420
//   AMCs    j=1 (Karolinska):  cy - 0.5*80         = 500
const S2_END_LEAF_WORLD_Y = [174, 254, 420, 500];
const S2_END_LEAF_CY = S2_END_LEAF_WORLD_Y.map(y => (y - S2_END_FY) * S2_END_SCALE + 540);

// ── The four leaves: SAME data as scene 2's surviving keepers, SAME order
// (CHTN, NDRI from Biobanks; MD Anderson, Karolinska from AMCs). Two pass
// the audit, two fail — matching the "2 of 4" result line.
const SAMPLES = [
  { id: 's1', name: 'CHTN · Tissue', price: '€38k', region: 'US', tat: '8w',
    sub: 'BIOBANK · US', pub: false, pass: false },
  { id: 's2', name: 'NDRI',          price: '€32k', region: 'US', tat: '7w',
    sub: 'BIOBANK · US', pub: false, pass: false },
  { id: 's3', name: 'MD Anderson',   price: '€36k', region: 'US', tat: '10w',
    sub: 'AMC · US',     pub: true,  pass: true  },
  { id: 's4', name: 'Karolinska',    price: '€34k', region: 'EU', tat: '11w',
    sub: 'AMC · EU',     pub: true,  pass: true  },
];

const CHECKS = [
  { label: 'Checking collection protocol',  start: 1.7, dur: 1.1, kind: 'email' },
  { label: 'Verifying storage conditions',  start: 2.9, dur: 1.1, kind: 'chat'  },
  { label: 'Confirming metadata endpoints', start: 4.1, dur: 1.1, kind: 'loop'  },
];

const INTRO_DUR = 0.85;

function Scene3Audit() {
  const { localTime: t } = useSprite();
  const cy = STAGE_H / 2;

  // Column geometry on the left
  const sampleColH = SAMPLES.length * S3_LEAF_H + (SAMPLES.length - 1) * S3_LEAF_GAP;
  const sampleStartY = cy - sampleColH / 2;
  const colCenterY = (i) => sampleStartY + i * (S3_LEAF_H + S3_LEAF_GAP) + S3_LEAF_H / 2;

  // Audit card on the right
  const auditW = 820;
  const auditH = 540;
  const auditX = STAGE_W / 2 + 320;     // 1280 — center
  const auditY = cy;

  // Beat A: intro tween from scene-2 end → scene-3 column
  const introT = clamp(t / INTRO_DUR, 0, 1);
  const introE = Easing.easeInOutCubic(introT);

  // Audit card entry (after leaves arrive)
  const auditP = Easing.easeOutCubic(clamp((t - 0.9) / 0.7, 0, 1));

  // Failed-leaf collapse phase
  const collapseStart = 5.6;

  return (
    <div style={{ position: 'absolute', inset: 0, background: C.ink, overflow: 'hidden', fontFamily: C.body }}>

      {/* faint bg wordmark */}
      <div style={{
        position: 'absolute', left: 0, right: 0, bottom: -160,
        textAlign: 'center', fontFamily: C.display, fontStyle: 'italic',
        fontSize: 480, color: C.ink2, opacity: 0.30,
        letterSpacing: '-0.04em', lineHeight: 1, pointerEvents: 'none',
      }}>crovi</div>

      {/* connectors: leaves on left → sourcing agent on right (only after intro settles) */}
      <svg style={{ position: 'absolute', inset: 0, pointerEvents: 'none', overflow: 'visible' }}>
        {SAMPLES.map((s, i) => {
          const sy = colCenterY(i);
          const sx = S3_COL_X + S3_LEAF_W;
          const tx = auditX - auditW / 2;
          const path = `M ${sx} ${sy} L ${tx} ${sy}`;
          // draw only after leaves have settled into the column
          const drawP = clamp((t - INTRO_DUR - i * 0.05) / 0.55, 0, 1);
          const failed = !s.pass && t > collapseStart;
          const dim = failed ? clamp(1 - (t - collapseStart) / 0.6, 0, 1) : 1;
          return (
            <path key={s.id} d={path}
              stroke={C.brandHi}
              strokeWidth={1.2}
              fill="none"
              strokeDasharray="4 4"
              opacity={drawP * 0.55 * dim}
            />
          );
        })}
      </svg>

      {/* sample leaves — SAME chips as scene 2's keepers, sliding from
          their zoomed end-of-scene-2 position into the audit column. */}
      {SAMPLES.map((s, i) => {
        // Tween: at introE=0, chip sits at scene-2 end position+scale.
        // At introE=1, chip sits at column position+scale 1.0.
        const targetCx = S3_COL_CX;
        const targetCy = colCenterY(i);
        const startCx  = S2_END_LEAF_CX;
        const startCy  = S2_END_LEAF_CY[i];
        const cxNow = startCx + (targetCx - startCx) * introE;
        const cyNow = startCy + (targetCy - startCy) * introE;
        const sNow  = S2_END_SCALE + (1.0 - S2_END_SCALE) * introE;

        const failed = !s.pass && t > collapseStart;
        const failK = failed ? Easing.easeInCubic(clamp((t - collapseStart) / 0.6, 0, 1)) : 0;
        const passGlow = s.pass && t > collapseStart + 0.2 ? clamp((t - collapseStart - 0.2) / 0.5, 0, 1) : 0;

        // Standby breath: slow pulse while checks are running, stops at collapse.
        // Only kicks in once leaves have settled into column (introT === 1).
        const standby = introT >= 1 && t > 1.5 && t < collapseStart;
        const breathT = standby ? Math.sin(((t - 1.5 + i * 0.35) / 2.0) * Math.PI * 2) * 0.5 + 0.5 : 0;
        const breathFade = standby ? clamp((collapseStart - t) / 0.3, 0, 1) : 0;
        const breath = breathT * breathFade;

        // Default border matches scene 2's keeper leaves (leafHeat=0, no winner glow).
        const borderColor = passGlow > 0
          ? C.brandHi
          : (breath > 0
              ? tt(`oklch(${0.30 + 0.20 * breath} ${0.02 + 0.08 * breath} ${250 - 50 * breath})`)
              : tt(`oklch(0.30 0.02 250)`));

        // shimmer while running checks
        const shimActive = introT >= 1 && t > 1.5 && t < collapseStart;
        const shim = (() => {
          if (!shimActive) return null;
          const period = 2.6;
          const stagger = i * 0.45;
          const ph = (((t - 1.5) + stagger) % period) / period;
          const fade = clamp((collapseStart - t) / 0.3, 0, 1);
          return { ph, fade };
        })();

        const handle = s.name.toLowerCase()
          .replace(/\s*·\s*/g, '.').replace(/\s+/g, '.').replace(/[^a-z.]/g, '');
        const email = `partnerships@${handle}.org`;

        return (
          <div key={s.id} style={{
            position: 'absolute',
            left: cxNow - S3_LEAF_W / 2,
            top:  cyNow - S3_LEAF_H / 2,
            width: S3_LEAF_W, height: S3_LEAF_H,
            opacity: 1 - failK * 0.85,
            transform: `scale(${sNow})`,
            transformOrigin: 'center center',
          }}>
            <div style={{
              position: 'relative',
              width: '100%', height: '100%',
              background: tt('oklch(0.13 0.013 250 / 0.95)'),
              border: `1px solid ${borderColor}`,
              borderRadius: 10,
              padding: '10px 14px',
              boxSizing: 'border-box',
              boxShadow: passGlow > 0
                ? `0 0 16px ${C.brandGlow}`
                : (breath > 0 ? tt(`0 0 ${6 + 8 * breath}px oklch(0.74 0.14 195 / ${0.10 + 0.20 * breath})`) : 'none'),
              display: 'flex', flexDirection: 'column', justifyContent: 'center',
              gap: 4,
              filter: failed ? `grayscale(${failK})` : 'none',
              overflow: 'hidden',
            }}>
              {/* shimmer overlay during standby */}
              {shim && (
                <div style={{
                  position: 'absolute', top: 0, bottom: 0,
                  left: `${-50 + shim.ph * 200}%`,
                  width: '50%',
                  background: tt('linear-gradient(90deg, transparent 0%, oklch(0.74 0.14 195 / 0.10) 35%, oklch(0.74 0.14 195 / 0.20) 50%, oklch(0.74 0.14 195 / 0.10) 65%, transparent 100%)'),
                  transform: 'skewX(-12deg)',
                  opacity: Math.sin(shim.ph * Math.PI) * shim.fade,
                  pointerEvents: 'none',
                }}/>
              )}

              {/* row 1: name + price */}
              <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                <div style={{
                  flex: 1, minWidth: 0,
                  fontFamily: C.display, fontSize: 17, color: C.inkText,
                  letterSpacing: '-0.005em', lineHeight: 1.1,
                  whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
                  textDecoration: failK > 0.5 ? 'line-through' : 'none',
                }}>{s.name}</div>
                <div style={{
                  fontFamily: C.mono, fontSize: 11, color: C.inkText,
                  opacity: 0.85, flexShrink: 0,
                }}>{s.price}</div>
                {s.pub && (
                  <div title="indexed via publications" style={{
                    width: 18, height: 18, borderRadius: 4,
                    border: tt(`1px solid oklch(0.45 0.015 250)`),
                    background: tt('oklch(0.20 0.012 250 / 0.5)'),
                    display: 'flex', alignItems: 'center', justifyContent: 'center',
                    flexShrink: 0,
                  }}>{Icons.doc(tt('oklch(0.62 0.02 250)'))}</div>
                )}
              </div>

              {/* row 2: email */}
              <div style={{
                fontFamily: C.mono, fontSize: 10, color: C.inkText3,
                whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
              }}>{email}</div>

              {/* row 3: region + TAT tags. Status pip only appears AFTER reflow
                  completes — during the reflow the chip looks identical to scene 2's
                  keeper LeafChip (no extra controls), so the seam reads as the same
                  element evolving rather than a fresh component being drawn. */}
              <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                <MetaTagS3 label={s.region}/>
                <MetaTagS3 label={s.tat}/>
                <div style={{ flex: 1 }}/>
                {introT >= 1 && (
                  <div style={{ flexShrink: 0 }}>
                    {failed ? Icons.cross(C.inkText3, 12)
                      : passGlow > 0 ? Icons.check(C.brandHi, 14)
                      : <LiveDot size={6} time={t}/>}
                  </div>
                )}
              </div>
            </div>
          </div>
        );
      })}

      {/* central Sourcing Agent card (right side) */}
      <div style={{
        position: 'absolute',
        left: auditX - auditW / 2,
        top: auditY - auditH / 2,
        width: auditW, height: auditH,
        opacity: auditP,
        transform: `translateY(${(1 - auditP) * 14}px) scale(${0.97 + 0.03 * auditP})`,
        background: tt('oklch(0.13 0.013 250 / 0.96)'),
        border: `1px solid ${C.brandHi}`,
        borderRadius: 18,
        padding: '24px 32px 28px',
        boxSizing: 'border-box',
        boxShadow: `0 0 40px ${C.brandGlow}, 0 16px 48px rgba(0,0,0,0.45)`,
        backdropFilter: 'blur(10px)',
      }}>
        {/* header */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
          <div style={{
            width: 44, height: 44, borderRadius: 12,
            background: tt('oklch(0.74 0.14 195 / 0.18)'),
            border: `1px solid ${C.brandHi}`,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
          }}>
            <Sparkle size={22}/>
          </div>
          <div style={{ flex: 1 }}>
            <div style={{ fontFamily: C.display, fontSize: 32, color: C.inkText, letterSpacing: '-0.01em' }}>
              Audit agent
            </div>
            <div style={{ marginTop: 4 }}>
              <Mono size={11} color={C.brandHi}>VALIDATING FIT</Mono>
            </div>
          </div>
          <LiveDot size={8} time={t}/>
        </div>

        {/* divider */}
        <div style={{ height: 1, background: C.ink3, margin: '20px 0 16px' }}/>

        {/* checks list */}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
          {CHECKS.map((c, i) => {
            const local = t - c.start;
            const visible = local > -0.3;
            if (!visible) return null;
            const enterP = Easing.easeOutCubic(clamp((local + 0.3) / 0.4, 0, 1));
            const running = local >= 0 && local < c.dur;
            const done    = local >= c.dur;
            const runProg = running ? clamp(local / c.dur, 0, 1) : (done ? 1 : 0);

            return (
              <div key={i} style={{
                display: 'flex', alignItems: 'center', gap: 14,
                padding: '14px 16px',
                background: running ? tt('oklch(0.18 0.025 195 / 0.45)') : tt('oklch(0.10 0.01 250 / 0.7)'),
                border: tt(`1px solid ${running ? C.brandHi : (done ? 'oklch(0.30 0.06 195 / 0.8)' : C.ink3)}`),
                borderRadius: 12,
                opacity: enterP,
                transform: `translateY(${(1 - enterP) * 6}px)`,
                transition: 'background 200ms, border-color 200ms',
              }}>
                <div style={{
                  width: 28, height: 28, borderRadius: '50%',
                  background: done ? tt('oklch(0.74 0.14 195 / 0.20)') : tt('oklch(0.10 0.01 250)'),
                  border: `1px solid ${done ? C.brandHi : (running ? C.brandHi : C.ink3)}`,
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  flexShrink: 0,
                }}>
                  {done ? Icons.check(C.brandHi, 14) :
                    running ? <Spinner t={t}/> :
                    <span style={{ width: 6, height: 6, borderRadius: '50%', background: C.inkText3 }}/>}
                </div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{
                    fontFamily: C.body, fontSize: 19,
                    color: done || running ? C.inkText : C.inkText2,
                    letterSpacing: '-0.005em',
                  }}>{c.label}</div>
                  <div style={{
                    marginTop: 8,
                    height: 3, borderRadius: 2,
                    background: tt('oklch(0.18 0.018 250)'),
                    overflow: 'hidden',
                  }}>
                    <div style={{
                      width: `${runProg * 100}%`, height: '100%',
                      background: C.brandHi,
                      boxShadow: running ? `0 0 8px ${C.brandHi}` : 'none',
                      transition: running ? 'none' : 'width 0.3s linear',
                    }}/>
                  </div>
                </div>
                <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexShrink: 0 }}>
                  <CheckThread t={t} startT={c.start} dur={c.dur} kind={c.kind}/>
                  <div style={{ minWidth: 70, textAlign: 'right' }}>
                    <Mono size={10} color={done ? C.brandHi : (running ? C.brandHi : C.inkText3)}>
                      {done ? 'PASS' : running ? 'RUNNING' : 'QUEUED'}
                    </Mono>
                  </div>
                </div>
              </div>
            );
          })}
        </div>

        {/* bottom summary */}
        {t > 5.4 && (
          <div style={{
            marginTop: 20,
            fontFamily: C.body, fontSize: 16, color: C.inkText2,
            opacity: clamp((t - 5.4) / 0.5, 0, 1),
            display: 'flex', alignItems: 'center', gap: 12,
          }}>
            <Mono size={11} color={C.brandHi}>RESULT</Mono>
            <span>2 of 4 sample routes cleared all checks</span>
          </div>
        )}
      </div>

    </div>
  );
}

function MetaTagS3({ label }) {
  return (
    <div style={{
      fontFamily: C.mono, fontSize: 9, color: C.inkText3,
      padding: '1px 5px', borderRadius: 3,
      border: tt(`1px solid oklch(0.30 0.02 250)`),
      letterSpacing: '0.04em',
    }}>{label}</div>
  );
}

// Inline mini comm-thread that runs PARALLEL to a check phase.
function CheckThread({ t, startT, dur, kind = 'email' }) {
  const local = t - startT;
  const phaseT = clamp(local / dur, 0, 1);
  if (local < -0.2) return <div style={{ width: 240, height: 38 }}/>;

  if (kind === 'loop') {
    const done = phaseT >= 1;
    const spin = done ? 0 : (t * 220) % 360;
    const enterP = Easing.easeOutCubic(clamp((local + 0.2) / 0.3, 0, 1));
    const polls = done ? 18 : Math.floor(phaseT * 18);
    return (
      <div style={{
        width: 240, height: 38,
        display: 'flex', alignItems: 'center', justifyContent: 'flex-end',
        gap: 10, opacity: enterP,
      }}>
        <Mono size={10} color={done ? C.brandHi : C.inkText3} tracking="0.10em">
          {String(polls).padStart(2, '0')} REQ
        </Mono>
        <div style={{
          width: 32, height: 32, borderRadius: 8,
          background: tt('oklch(0.74 0.14 195 / 0.18)'),
          border: `1px solid ${C.brandHi}`,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          boxShadow: done ? 'none' : tt(`0 0 8px oklch(0.74 0.14 195 / 0.35)`),
        }}>
          <div style={{
            transform: `rotate(${spin}deg)`,
            display: 'flex',
            transition: done ? 'transform 0.2s ease-out' : 'none',
          }}>
            {done ? Icons.check(C.brandHi, 16) : Icons.loop(C.brandHi, 18)}
          </div>
        </div>
      </div>
    );
  }

  const SCHEDULES = {
    email: [
      { side: 'out', at: 0.05, icon: 'mail' },
      { side: 'in',  at: 0.40, icon: 'mail' },
      { side: 'out', at: 0.72, icon: 'mail' },
    ],
    chat: [
      { side: 'out', at: 0.05, icon: 'chat' },
      { side: 'in',  at: 0.35, icon: 'chat' },
      { side: 'out', at: 0.65, icon: 'phone' },
      { side: 'in',  at: 0.85, icon: 'chat' },
    ],
  };
  const BUBBLES = SCHEDULES[kind] || SCHEDULES.email;

  return (
    <div style={{
      width: 240, height: 38,
      display: 'flex', alignItems: 'center', justifyContent: 'flex-end',
      gap: 5,
      position: 'relative',
    }}>
      {BUBBLES.map((b, i) => {
        const bp = Easing.easeOutCubic(clamp((phaseT - b.at) / 0.16, 0, 1));
        if (bp <= 0) return null;
        const isOut = b.side === 'out';
        const isPhone = b.icon === 'phone';
        const accent = isOut || isPhone;
        const phasePulse = isPhone ? Math.sin(clamp((phaseT - b.at) / 0.22, 0, 1) * Math.PI) : 0;
        return (
          <div key={i} style={{
            opacity: bp,
            transform: `translateY(${(1 - bp) * 5}px) scale(${0.8 + 0.2 * bp + 0.05 * phasePulse})`,
            position: 'relative',
            width: 36, height: 26,
            borderRadius: 7,
            background: accent
              ? tt('oklch(0.74 0.14 195 / 0.22)')
              : tt('oklch(0.20 0.018 250 / 0.95)'),
            border: tt(`1px solid ${accent ? C.brandHi : 'oklch(0.42 0.04 250)'}`),
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            boxShadow: accent
              ? tt(`0 0 ${6 + 6 * phasePulse}px oklch(0.74 0.14 195 / ${0.3 + 0.3 * phasePulse})`)
              : 'none',
          }}>
            {Icons[b.icon](accent ? C.brandHi : tt('oklch(0.62 0.02 250)'), 13)}
            {isPhone && phasePulse > 0.05 && (
              <div style={{
                position: 'absolute', inset: -4,
                borderRadius: 9,
                border: `1px solid ${C.brandHi}`,
                opacity: 0.5 * phasePulse,
                transform: `scale(${1 + phasePulse * 0.4})`,
              }}/>
            )}
          </div>
        );
      })}
    </div>
  );
}

function Spinner({ t }) {
  const angle = (t * 360) % 360;
  return (
    <svg width="14" height="14" viewBox="0 0 14 14" style={{ transform: `rotate(${angle}deg)` }}>
      <circle cx="7" cy="7" r="5" stroke={C.ink3} strokeWidth="1.5" fill="none"/>
      <path d="M 7 2 A 5 5 0 0 1 12 7" stroke={C.brandHi} strokeWidth="1.5" fill="none" strokeLinecap="round"/>
    </svg>
  );
}

window.Scene3Audit = Scene3Audit;
window.S3_LEAF_W = S3_LEAF_W;
window.S3_LEAF_H = S3_LEAF_H;
window.S3_LEAF_GAP = S3_LEAF_GAP;
window.S3_COL_X = S3_COL_X;
window.S3_AUDIT_W = 820;
window.S3_AUDIT_H = 540;
window.S3_AUDIT_CX = STAGE_W / 2 + 320;
window.S3_AUDIT_CY = STAGE_H / 2;
