// scenes-8.jsx — "Many tools, messy runs" + "What is a token"

// ════════════════════════════════════════════════════════════════════════════
// SCENE — MANY TOOLS, MESSY RUNS  (length 20s)
// ════════════════════════════════════════════════════════════════════════════
function ToolChip({ x, y, w, icon, sig, col, state, o = 1 }) {
  // state: 'idle' | 'active' | 'error'
  const c = state === 'error' ? COLORS.coral : state === 'active' ? col : COLORS.inkFaint;
  const lit = state !== 'idle';
  return (
    <div style={{
      position:'absolute', left:x, top:y, width:w, opacity:o, transform:`translate(0,-50%)`,
      background: COLORS.bgPanel, border:`1.6px solid ${lit ? c : c+'66'}`, borderRadius:12,
      padding:'12px 16px', display:'flex', alignItems:'center', gap:12,
      boxShadow: lit ? `0 0 22px ${c}44` : 'none', transition:'none',
    }}>
      <span style={{ width:30, height:30, borderRadius:8, flexShrink:0, background:c+'22', border:`1px solid ${c}66`,
        display:'flex', alignItems:'center', justifyContent:'center', fontSize:17, color:c, fontFamily:FONTS.math }}>{icon}</span>
      <span style={{ fontFamily:FONTS.mono, fontSize:21, color: lit?COLORS.ink:COLORS.inkDim, fontWeight:600 }}>{sig}</span>
      {state==='error' && <span style={{ marginLeft:'auto', fontFamily:FONTS.sans, fontSize:14, fontWeight:700, color:COLORS.coral, whiteSpace:'nowrap' }}>error ↻</span>}
      {state==='active' && <span style={{ marginLeft:'auto', width:9, height:9, borderRadius:'50%', background:c, boxShadow:`0 0 10px ${c}` }} />}
    </div>
  );
}

const TOOLBOX = [
  { sig:'search_web',  icon:'⚲', col: COLORS.blue },
  { sig:'get_weather', icon:'☼', col: COLORS.yellow },
  { sig:'run_sql',     icon:'▤', col: COLORS.green },
  { sig:'send_email',  icon:'✉', col: COLORS.purple },
  { sig:'read_file',   icon:'▢', col: COLORS.blue },
  { sig:'calculator',  icon:'∑', col: COLORS.coral },
];
function SceneManyTools() {
  const { localTime: lt } = useSprite();
  const setup = ramp(lt, 0.2, 1.0);
  const mx = 430, listX = 1090, listW = 360, y0 = 250, step = 96;
  const my = y0 + step * (TOOLBOX.length - 1) / 2;
  const chipY = (i) => y0 + i * step;

  // phase states
  const state = (i) => {
    const sig = TOOLBOX[i].sig;
    // choose one (2.5–5)
    if (lt >= 2.6 && lt < 5.2) return sig === 'search_web' ? 'active' : 'idle';
    // parallel two (5.6–8.6)
    if (lt >= 5.6 && lt < 8.8) return (sig === 'get_weather' || sig === 'run_sql') ? 'active' : 'idle';
    // error + retry (9.2–13): run_sql errors, then retried active
    if (lt >= 9.2 && lt < 11.4) return sig === 'run_sql' ? 'error' : 'idle';
    if (lt >= 11.4 && lt < 13.4) return sig === 'run_sql' ? 'active' : 'idle';
    return 'idle';
  };
  const wireFlow = (i) => {
    const st = state(i);
    if (st === 'idle') return null;
    return (lt % 0.85) / 0.85;
  };

  // retry counter
  const retryShow = pulse(lt, 9.6, 20, 0.4);

  return (
    <>
      <Bg accent={COLORS.yellow} />
      <Eyebrow lt={lt} a={0.4} b={20} n="04" label="Many tools, messy runs" color={COLORS.yellow} />

      <ModelNode x={mx} y={my} r={80} scale={setup} glow={setup}
        thinking={(lt>1.6&&lt<2.6)||(lt>8.8&&lt<9.2) ? lt : 0} />
      <div style={{ position:'absolute', left:mx, top:my+108, transform:'translateX(-50%)', opacity:setup, fontFamily:FONTS.sans, fontSize:22, color:COLORS.inkDim, whiteSpace:'nowrap' }}>the agent</div>

      {/* toolbox label */}
      <div style={{ position:'absolute', left:listX, top:y0-72, opacity:setup, fontFamily:FONTS.mono, fontSize:18, letterSpacing:'0.14em', textTransform:'uppercase', color:COLORS.inkDim }}>
        the toolbox
      </div>

      {TOOLBOX.map((t,i) => {
        const st = state(i);
        const c = st==='error'?COLORS.coral:st==='active'?t.col:COLORS.inkFaint;
        const fl = wireFlow(i);
        return (
          <React.Fragment key={i}>
            <Wire from={[mx+78, my]} to={[listX-10, chipY(i)]} color={c + (st==='idle'?'33':'')} width={2}
              draw={ramp(lt, 0.6+i*0.12, 1.4+i*0.12)} flow={fl} />
            <ToolChip x={listX} y={chipY(i)} w={listW} icon={t.icon} sig={t.sig} col={t.col} state={st}
              o={ramp(lt, 0.7+i*0.12, 1.5+i*0.12, Easing.easeOutBack)} />
          </React.Fragment>
        );
      })}

      {/* retry / turn counter */}
      {retryShow > 0.01 && (
        <div style={{ position:'absolute', left: listX+listW+60, top: my, transform:'translateY(-50%)', opacity:retryShow, width:230 }}>
          <div style={{ fontFamily:FONTS.sans, fontSize:18, color:COLORS.inkDim, letterSpacing:'0.06em', textTransform:'uppercase', marginBottom:6 }}>paid turns</div>
          <div style={{ fontFamily:FONTS.math, fontSize:90, fontWeight:700, color:COLORS.coral, lineHeight:1 }}>
            {lt<5.6?1:lt<9.2?2:lt<11.4?3:4}
          </div>
          <div style={{ fontFamily:FONTS.sans, fontSize:18, color:COLORS.inkDim, marginTop:8 }}>every call &amp; retry is another billed round-trip</div>
        </div>
      )}

      <Caption lt={lt} a={1.2} b={2.6}>
        Real agents don't have one tool — they have a whole <b style={{color:COLORS.yellow}}>toolbox</b>.
      </Caption>
      <Caption lt={lt} a={2.8} b={5.2}>
        Each turn the model <b>chooses</b> which tool to call, by name.
      </Caption>
      <Caption lt={lt} a={5.6} b={8.8} color={COLORS.blue}>
        It can fire <b>several in parallel</b> when steps don't depend on each other.
      </Caption>
      <Caption lt={lt} a={9.2} b={13.4} color={COLORS.coral}>
        Tools fail — and the agent <b>retries</b>. Each retry is another paid turn.
      </Caption>
      <Caption lt={lt} a={13.8} b={20}>
        More tools, more loops, more retries — all of it lands in the same growing context.
      </Caption>
    </>
  );
}

// ════════════════════════════════════════════════════════════════════════════
// SCENE — WHAT IS A TOKEN  (length 19s)
// ════════════════════════════════════════════════════════════════════════════
const TOKS = [
  { t:'Token', id:'9126' },
  { t:'ization', id:'2065' },
  { t:' splits', id:'62069' },
  { t:' text', id:'1495' },
  { t:' into', id:'1139' },
  { t:' chunks', id:'37833' },
  { t:'.', id:'13' },
];
function SceneToken() {
  const { localTime: lt } = useSprite();
  const palette = [COLORS.blue, COLORS.green, COLORS.yellow, COLORS.purple, COLORS.coral, COLORS.blue, COLORS.green];

  const sentenceO = pulse(lt, 0.8, 3.2, 0.4);      // plain sentence
  const split = ramp(lt, 2.6, 3.8, Easing.easeInOutCubic);   // chips separate
  const chipsO = pulse(lt, 2.8, 19, 0.4);
  const idsIn = ramp(lt, 5.2, 6.2);                // token IDs flip in
  const ruleIn = pulse(lt, 8.6, 19, 0.45);

  // layout chips centered
  const cy = 430;
  const chipW = (tk) => Math.max(70, tk.t.trim().length * 19 + 44);
  const gap = 12;
  const widths = TOKS.map(chipW);
  const totalW = widths.reduce((a,b)=>a+b,0) + gap*(TOKS.length-1);
  let xcursor = 960 - totalW/2;
  const xs = widths.map((w)=>{ const x=xcursor; xcursor += w+gap; return x; });
  // when unsplit, chips sit flush (no gaps) — interpolate gap by split
  const flushW = widths.reduce((a,b)=>a+b,0);
  let fcursor = 960 - flushW/2;
  const fxs = widths.map((w)=>{ const x=fcursor; fcursor += w; return x; });

  return (
    <>
      <Bg accent={COLORS.blue} />
      <Eyebrow lt={lt} a={0.4} b={19} n="05" label="What is a token?" color={COLORS.blue} />

      {/* chips */}
      {chipsO > 0.01 && TOKS.map((tk, i) => {
        const x = fxs[i] + (xs[i]-fxs[i]) * split;
        const c = palette[i % palette.length];
        const o = Math.max(sentenceO, chipsO);
        return (
          <div key={i}>
            <div style={{
              position:'absolute', left:x, top:cy, width:widths[i], height:64, opacity:o,
              transform:'translateY(-50%)',
              background: split>0.3 ? c+'1f' : 'transparent',
              border:`1.6px solid ${split>0.3 ? c : 'transparent'}`, borderRadius:10,
              display:'flex', alignItems:'center', justifyContent:'center',
              fontFamily:FONTS.mono, fontSize:26, color:COLORS.ink, whiteSpace:'pre', transition:'none',
            }}>
              {tk.t}
            </div>
            {/* token id */}
            <div style={{
              position:'absolute', left:x+widths[i]/2, top:cy+58, transform:'translate(-50%,0)',
              opacity:idsIn, fontFamily:FONTS.mono, fontSize:18, color:c,
            }}>{tk.id}</div>
          </div>
        );
      })}
      {/* id row label */}
      {idsIn>0.1 && (
        <div style={{ position:'absolute', left:960-totalW/2-150, top:cy+58, transform:'translateY(-2px)', opacity:idsIn,
          width:130, textAlign:'right', fontFamily:FONTS.sans, fontSize:17, color:COLORS.inkDim }}>token IDs →</div>
      )}

      {/* rule of thumb */}
      {ruleIn > 0.01 && (
        <div style={{ position:'absolute', left:'50%', top:640, transform:'translate(-50%,0)', opacity:ruleIn,
          display:'flex', gap:48, alignItems:'center' }}>
          {[
            ['≈ 4 chars','1 token'],
            ['100 tokens','≈ 75 words'],
            ['everything','is counted'],
          ].map(([a,b],i)=>(
            <div key={i} style={{ textAlign:'center' }}>
              <div style={{ fontFamily:FONTS.math, fontSize:40, fontWeight:700, color: i===2?COLORS.coral:COLORS.ink }}>{a}</div>
              <div style={{ fontFamily:FONTS.sans, fontSize:21, color:COLORS.inkDim, marginTop:4 }}>{b}</div>
            </div>
          ))}
        </div>
      )}

      <Caption lt={lt} a={1.0} b={2.6}>
        Before cost makes sense, one idea: the <b style={{color:COLORS.blue}}>token</b>.
      </Caption>
      <Caption lt={lt} a={2.8} b={5.0}>
        Models don't see words — text is chopped into <b>tokens</b> (often sub-word pieces).
      </Caption>
      <Caption lt={lt} a={5.4} b={8.4}>
        Each token is really just an <b>integer ID</b> the model processes.
      </Caption>
      <Caption lt={lt} a={8.8} b={13.0}>
        Rough rule: <b>~4 characters ≈ 1 token</b>, so 100 tokens ≈ 75 words.
      </Caption>
      <Caption lt={lt} a={13.4} b={19} color={COLORS.coral}>
        Prompt, tools, history, the reply — <b>all</b> of it is measured in tokens.
      </Caption>
    </>
  );
}

Object.assign(window, { SceneManyTools, SceneToken, ToolChip });
