// scenes-2.jsx — Act II: the agent loop + memory / context growth

// ════════════════════════════════════════════════════════════════════════════
// SCENE 4 — THE AGENT LOOP  (length 22s)
// ════════════════════════════════════════════════════════════════════════════
function SceneAgentLoop() {
  const { localTime: lt } = useSprite();
  const cx = 660, cy = 560, R = 250;
  const setup = ramp(lt, 0.2, 1.1, Easing.easeOutBack);

  // marker starts looping at t=2.0, lap = 3.6s
  const lapDur = 3.6, loopStart = 2.0;
  const loopT = Math.max(0, lt - loopStart);
  const laps = loopT / lapDur;
  const ang = -90 + (laps % 1) * 360;              // degrees, clockwise from top
  const markerOn = lt > loopStart && lt < 17.5 ? 1 : 0;

  const stations = [
    { lbl: '1 · Reason',    sub: 'what should I do next?', col: COLORS.blue,   at: -90 },
    { lbl: '2 · Call tool', sub: 'emit a request',         col: COLORS.yellow, at: 30 },
    { lbl: '3 · Observe',   sub: 'read the result',        col: COLORS.green,  at: 150 },
  ];

  // how many results have landed in memory (each lap passes Observe once)
  const completed = Math.max(0, Math.floor((loopT + lapDur * (1 - 150/360)) / lapDur));
  const memCount = clamp(completed, 0, 5);

  const mx = cx + Math.cos(ang * Math.PI/180) * R;
  const my = cy + Math.sin(ang * Math.PI/180) * R;

  return (
    <>
      <Bg accent={COLORS.purple} />
      <Eyebrow lt={lt} a={0.4} b={22} n="03" label="The agent loop" color={COLORS.purple} />

      {/* ring */}
      <div style={{
        position: 'absolute', left: cx, top: cy, width: R*2, height: R*2,
        transform: `translate(-50%,-50%) scale(${setup})`,
        border: `2.5px dashed ${COLORS.inkFaint}aa`, borderRadius: '50%', opacity: setup,
      }} />
      {/* central label */}
      <div style={{ position:'absolute', left: cx, top: cy, transform:'translate(-50%,-50%)', opacity: setup, textAlign:'center' }}>
        <div style={{ fontFamily: FONTS.sans, fontSize: 30, fontWeight: 700, color: COLORS.ink }}>AGENT</div>
        <div style={{ fontFamily: FONTS.math, fontStyle:'italic', fontSize: 23, color: COLORS.inkDim, marginTop: 4 }}>= model in a loop</div>
      </div>

      {/* stations */}
      {stations.map((s, i) => {
        const sx = cx + Math.cos(s.at * Math.PI/180) * R;
        const sy = cy + Math.sin(s.at * Math.PI/180) * R;
        const dist = Math.abs(((ang - s.at + 540) % 360) - 180); // 0=far,180=near
        const near = clamp((dist - 120) / 60, 0, 1) * markerOn;   // lights when marker close
        const sIn = ramp(lt, 0.6 + i*0.25, 1.4 + i*0.25, Easing.easeOutBack);
        return (
          <div key={i} style={{
            position:'absolute', left: sx, top: sy, transform:`translate(-50%,-50%) scale(${sIn})`, opacity: sIn,
            display:'flex', flexDirection:'column', alignItems:'center', gap:6, width: 230,
          }}>
            <div style={{
              width: 28, height: 28, borderRadius:'50%', background: s.col,
              boxShadow: `0 0 ${10 + near*26}px ${s.col}`, transform:`scale(${1 + near*0.4})`,
            }} />
            <div style={{ fontFamily: FONTS.sans, fontSize: 25, fontWeight: 700, color: near>0.4? s.col : COLORS.ink, whiteSpace:'nowrap' }}>{s.lbl}</div>
            <div style={{ fontFamily: FONTS.sans, fontSize: 18, color: COLORS.inkDim }}>{s.sub}</div>
          </div>
        );
      })}

      {/* travelling marker */}
      {markerOn > 0 && (
        <div style={{
          position:'absolute', left: mx, top: my, transform:'translate(-50%,-50%)',
          width: 22, height: 22, borderRadius:'50%', background:'#fff',
          boxShadow:`0 0 22px 6px ${COLORS.ink}`,
        }} />
      )}

      {/* memory / context tray on the right */}
      <div style={{
        position:'absolute', left: 1340, top: cy, transform:`translate(-50%,-50%)`, opacity: setup,
        width: 380, background: COLORS.bgPanel, border:`2px solid ${COLORS.purple}66`,
        borderRadius: 18, padding: '20px 22px', boxShadow:`0 0 30px ${COLORS.purple}22`,
      }}>
        <div style={{ display:'flex', alignItems:'center', gap:10, marginBottom:16 }}>
          <span style={{ width:14, height:14, borderRadius:4, background: COLORS.purple, boxShadow:`0 0 12px ${COLORS.purple}` }} />
          <span style={{ fontFamily: FONTS.sans, fontSize: 24, fontWeight:700, color: COLORS.ink }}>Memory</span>
          <span style={{ fontFamily: FONTS.math, fontStyle:'italic', fontSize: 19, color: COLORS.inkDim }}>(the context)</span>
        </div>
        {['system prompt', 'user request'].map((t,i)=>(
          <MemRow key={t} label={t} col={COLORS.inkDim} show={1} />
        ))}
        {Array.from({length: memCount}).map((_,i)=>{
          const appear = clamp((memCount - i), 0, 1);
          return <MemRow key={'r'+i} label={`tool call + result #${i+1}`} col={COLORS.green} show={appear} />;
        })}
      </div>

      <Caption lt={lt} a={2.2} b={6.2}>
        An agent is just a model run <b style={{color: COLORS.purple}}>over and over</b> in a loop.
      </Caption>
      <Caption lt={lt} a={6.6} b={11.0}>
        Each lap it reasons, calls a tool, and reads the result.
      </Caption>
      <Caption lt={lt} a={11.4} b={16.0} color={COLORS.purple}>
        Every result is appended to <b>memory</b> — so the next lap remembers it.
      </Caption>
      <Caption lt={lt} a={16.4} b={22} color={COLORS.coral}>
        And here's the catch: that memory is re-sent <b>every single lap</b>.
      </Caption>
    </>
  );
}

function MemRow({ label, col, show }) {
  if (show <= 0.01) return null;
  return (
    <div style={{
      display:'flex', alignItems:'center', gap:10, padding:'8px 12px', marginBottom:8,
      background: col + '14', border:`1px solid ${col}44`, borderRadius:9,
      opacity: show, transform:`translateX(${(1-show)*20}px)`,
      fontFamily: FONTS.mono, fontSize: 18, color: COLORS.ink,
    }}>
      <span style={{ width:8, height:8, borderRadius:'50%', background: col }} />
      {label}
    </div>
  );
}

// ════════════════════════════════════════════════════════════════════════════
// SCENE 5 — CONTEXT GROWS  (length 21s)
// ════════════════════════════════════════════════════════════════════════════
function SceneContextGrows() {
  const { localTime: lt } = useSprite();
  const setup = ramp(lt, 0.2, 1.0);

  // conversation blocks, each appears in sequence and has a token weight
  const blocks = [
    { t: 'system prompt',        tok: 400,  col: COLORS.inkDim, at: 1.4 },
    { t: 'user request',         tok: 120,  col: COLORS.blue,   at: 3.0 },
    { t: 'assistant: tool call', tok: 90,   col: COLORS.yellow, at: 4.6 },
    { t: 'tool result',          tok: 650,  col: COLORS.green,  at: 6.2 },
    { t: 'assistant: tool call', tok: 110,  col: COLORS.yellow, at: 7.8 },
    { t: 'tool result',          tok: 900,  col: COLORS.green,  at: 9.4 },
    { t: 'assistant: answer',    tok: 240,  col: COLORS.blue,   at: 11.0 },
  ];

  const colX = 560, colTop = 250, blockH = 64, gap = 12;
  let cumulative = 0;
  const placed = blocks.map((b) => {
    const o = { ...b, yTop: colTop + (b._i = blocks.indexOf(b)) * (blockH + gap), cumBefore: cumulative };
    cumulative += b.tok;
    return o;
  });
  const totalTok = cumulative;

  // how many blocks are visible
  const visCount = placed.filter(b => lt > b.at).length;
  const runningTok = placed.filter(b => lt > b.at).reduce((s,b)=>s+b.tok,0);

  // the "re-sent each turn" bracket sweeping height
  const bracketH = visCount > 0 ? (visCount * (blockH+gap) - gap) : 0;

  // token counter on the right (climbs as blocks land)
  const counterIn = ramp(lt, 12.0, 12.6);

  return (
    <>
      <Bg accent={COLORS.coral} />
      <Eyebrow lt={lt} a={0.4} b={21} n="06" label="The context window" color={COLORS.coral} />

      {/* heading */}
      <div style={{ position:'absolute', left: colX, top: 150, transform:'translateX(-50%)', opacity: setup, textAlign:'center' }}>
        <div style={{ fontFamily: FONTS.sans, fontSize: 32, fontWeight: 700, color: COLORS.ink }}>One conversation</div>
        <div style={{ fontFamily: FONTS.math, fontStyle:'italic', fontSize: 21, color: COLORS.inkDim, marginTop:4 }}>grows turn by turn</div>
      </div>

      {/* conversation column */}
      {placed.map((b, i) => {
        const o = pulse(lt, b.at, 21, 0.35);
        if (o <= 0.01) return null;
        const w = 120 + (b.tok / 900) * 300; // width ~ token weight
        return (
          <div key={i} style={{
            position:'absolute', left: colX - 200, top: b.yTop, width: 400, height: blockH,
            opacity: o, transform:`translateX(${(1-o)*-30}px)`,
          }}>
            <div style={{
              width: w, height: blockH, borderRadius: 10,
              background: b.col + '22', border:`1.5px solid ${b.col}`,
              display:'flex', alignItems:'center', paddingLeft: 16, gap: 10,
              fontFamily: FONTS.mono, fontSize: 19, color: COLORS.ink,
            }}>
              <span style={{ width:9, height:9, borderRadius:'50%', background: b.col }} />
              {b.t}
              <span style={{ marginLeft:'auto', marginRight:14, color: b.col, fontSize:16 }}>{b.tok}t</span>
            </div>
          </div>
        );
      })}

      {/* re-sent bracket */}
      {lt > 13.5 && bracketH > 0 && (() => {
        const o = pulse(lt, 13.6, 21, 0.4);
        const bx = colX - 230;
        return (
          <div style={{ position:'absolute', left: bx - 30, top: colTop, height: bracketH, opacity:o }}>
            <div style={{ width: 14, height:'100%', borderLeft:`3px solid ${COLORS.coral}`, borderTop:`3px solid ${COLORS.coral}`, borderBottom:`3px solid ${COLORS.coral}`, borderRadius:'6px 0 0 6px' }} />
            <div style={{ position:'absolute', left: -150, top:'50%', transform:'translateY(-50%)', width: 140, textAlign:'right', fontFamily: FONTS.sans, fontSize: 21, fontWeight:600, color: COLORS.coral, lineHeight:1.2 }}>
              re-sent<br/>every turn
            </div>
          </div>
        );
      })()}

      {/* running token total */}
      <div style={{
        position:'absolute', left: 1380, top: 540, transform:'translate(-50%,-50%)', opacity: setup,
        textAlign:'center', width: 420,
      }}>
        <div style={{ fontFamily: FONTS.sans, fontSize: 24, color: COLORS.inkDim, letterSpacing:'0.04em' }}>tokens in context</div>
        <div style={{ fontFamily: FONTS.math, fontSize: 150, fontWeight: 700, color: COLORS.coral, lineHeight: 1, fontVariantNumeric:'tabular-nums' }}>
          {fmtNum(runningTok)}
        </div>
        {counterIn > 0.01 && (
          <div style={{ opacity: counterIn, marginTop: 18, fontFamily: FONTS.sans, fontSize: 22, color: COLORS.ink, lineHeight:1.4 }}>
            Every new turn pays to re-read <b style={{color:COLORS.coral}}>all of it</b>.<br/>
            <span style={{ color: COLORS.inkDim, fontSize: 20 }}>Long agent runs get expensive fast.</span>
          </div>
        )}
      </div>

      <Caption lt={lt} a={1.6} b={6.0}>
        Models are <b>stateless</b> — so the whole history travels with every call.
      </Caption>
      <Caption lt={lt} a={6.4} b={11.4} color={COLORS.green}>
        Tool results are often the <b>biggest</b> blocks of all.
      </Caption>
      <Caption lt={lt} a={11.8} b={16.2}>
        These tokens are exactly what you're billed for…
      </Caption>
      <Caption lt={lt} a={16.6} b={21} color={COLORS.coral}>
        …so let's see how that bill is actually computed.
      </Caption>
    </>
  );
}

Object.assign(window, { SceneAgentLoop, SceneContextGrows, MemRow });
