// scenes-10.jsx — "Retrieval (RAG): the anti-cache" — the top cost knob

function VectorDB({ x, y, scale = 1, pulse: pl = 0, label = 'vector DB' }) {
  const w = 150, h = 120, ry = 22, col = COLORS.purple;
  return (
    <div style={{ position:'absolute', left:x, top:y, transform:`translate(-50%,-50%) scale(${scale})` }}>
      <div style={{ position:'absolute', left:'50%', top:'50%', transform:'translate(-50%,-50%)',
        width:w*1.9, height:w*1.9, borderRadius:'50%', background:`radial-gradient(circle, ${col}33, transparent 68%)`, opacity:pl }} />
      <svg width={w} height={h+ry} viewBox={`0 0 ${w} ${h+ry}`} style={{ display:'block' }}>
        <path d={`M0 ${ry} a ${w/2} ${ry} 0 0 0 ${w} 0 v ${h} a ${w/2} ${ry} 0 0 1 ${-w} 0 Z`} fill={COLORS.bgPanel} stroke={col} strokeWidth="2.5" />
        <ellipse cx={w/2} cy={ry} rx={w/2} ry={ry} fill={COLORS.bgPanel} stroke={col} strokeWidth="2.5" />
        {/* embedding dots */}
        {[[40,60],[80,90],[110,70],[60,110],[95,130]].map((p,i)=>(
          <circle key={i} cx={p[0]} cy={p[1]} r="4" fill={col} opacity={0.5 + 0.5*Math.abs(Math.sin(pl*3+i))} />
        ))}
      </svg>
      <div style={{ textAlign:'center', marginTop:8, fontFamily:FONTS.sans, fontSize:20, fontWeight:600, color:col }}>{label}</div>
    </div>
  );
}

function RagBlock({ x, y, w, label, tok, state, o = 1 }) {
  // state: 'idle'|'fresh'|'cached'
  const col = state==='cached'?COLORS.green : state==='fresh'?COLORS.coral : COLORS.purple;
  const tag = state==='cached'?'cached ·×0.1' : state==='fresh'?'fresh ·×1' : null;
  return (
    <div style={{
      position:'absolute', left:x, top:y, width:w, height:60, opacity:o, transform:`translateX(${(1-o)*-24}px)`,
      background: col+'1f', border:`1.6px solid ${col}`, borderRadius:10,
      display:'flex', alignItems:'center', gap:10, paddingLeft:14, transition:'none',
      boxShadow: state==='fresh'?`0 0 16px ${col}33`:'none',
    }}>
      <span style={{ width:9, height:9, borderRadius:'50%', background:col, flexShrink:0 }} />
      <span style={{ fontFamily:FONTS.mono, fontSize:18, color:COLORS.ink }}>{label}</span>
      <span style={{ fontFamily:FONTS.mono, fontSize:15, color:col, marginLeft:8 }}>{tok}t</span>
      {tag && <span style={{ marginLeft:'auto', marginRight:12, fontFamily:FONTS.sans, fontSize:14, fontWeight:600, color:col, whiteSpace:'nowrap' }}>{tag}</span>}
    </div>
  );
}

// ════════════════════════════════════════════════════════════════════════════
// SCENE — RETRIEVAL (RAG): THE ANTI-CACHE  (length 23s)
// ════════════════════════════════════════════════════════════════════════════
const CHUNKS = 5, TOK_PER = 512;
function SceneRAG() {
  const { localTime: lt } = useSprite();
  const setup = ramp(lt, 0.2, 1.0);

  const colX = 760, colW = 380, sysY = 230, chunkY0 = 312, ch = 60, cgap = 12;
  const mx = 1560, my = 470;
  const dbXY = [320, 470];

  // retrieve: chunks fan in 1.6→4.4
  const chunkAt = (i) => 1.8 + i*0.42;
  const nChunks = Array.from({length:CHUNKS}).filter((_,i)=> lt > chunkAt(i)).length;
  const flowRetrieve = lt > 1.6 && lt < 4.6;

  // phases
  const showCount = pulse(lt, 4.8, 23, 0.4);
  const bustPhase = lt > 8.6;        // cache-bust reveal
  const showCost = pulse(lt, 13.4, 23, 0.4);

  const sysState = bustPhase ? 'cached' : 'idle';
  const chunkState = bustPhase ? 'fresh' : 'idle';

  const totalChunkTok = nChunks * TOK_PER;
  const perQueryCost = (CHUNKS*TOK_PER)/1e6 * PRICE.input; // fresh price

  const chunkMidY = (i) => chunkY0 + i*(ch+cgap) + ch/2;

  return (
    <>
      <Bg accent={COLORS.purple} />
      <Eyebrow lt={lt} a={0.4} b={23} n="14" label="Retrieval (RAG) · the anti-cache" color={COLORS.purple} />

      {/* query */}
      <div style={{ position:'absolute', left:dbXY[0], top:230, transform:'translate(-50%,-50%)', opacity:setup, width:260, textAlign:'center',
        background:COLORS.bgPanel, border:`1.5px solid ${COLORS.inkFaint}`, borderRadius:14, padding:'12px 16px',
        fontFamily:FONTS.sans, fontSize:19, color:COLORS.ink }}>
        <span style={{color:COLORS.inkDim}}>query ·</span> "refund policy?"
      </div>

      {/* vector DB */}
      <VectorDB x={dbXY[0]} y={dbXY[1]} scale={ramp(lt,0.4,1.2,Easing.easeOutBack)} pulse={flowRetrieve?lt:0} />

      {/* retrieval wires DB -> each chunk */}
      {Array.from({length:nChunks}).map((_,i)=>(
        <Wire key={i} from={[dbXY[0]+80, dbXY[1]]} to={[colX-10, chunkMidY(i)]} color={COLORS.purple + (flowRetrieve?'':'66')} width={2}
          draw={ramp(lt, chunkAt(i)-0.2, chunkAt(i)+0.4)} flow={flowRetrieve?((lt%0.8)/0.8):null} />
      ))}

      {/* context column: system prompt (stable) + retrieved chunks */}
      <div style={{ position:'absolute', left:colX, top:sysY-44, opacity:setup, fontFamily:FONTS.mono, fontSize:17, letterSpacing:'0.12em', textTransform:'uppercase', color:COLORS.inkDim }}>
        the prompt sent to the model
      </div>
      <RagBlock x={colX} y={sysY} w={colW} label="system prompt" tok={400} state={sysState} o={setup} />
      {Array.from({length:nChunks}).map((_,i)=>(
        <RagBlock key={i} x={colX} y={chunkY0 + i*(ch+cgap)} w={colW} label={`retrieved chunk ${i+1}`} tok={TOK_PER} state={chunkState}
          o={ramp(lt, chunkAt(i), chunkAt(i)+0.4, Easing.easeOutBack)} />
      ))}

      {/* model */}
      <ModelNode x={mx} y={my} r={72} scale={setup} glow={setup} color={COLORS.purple} thinking={flowRetrieve?lt:0} />
      <Wire from={[colX+colW, sysY+30]} to={[mx-78, my]} color={COLORS.inkFaint+'88'} width={2} draw={setup} />
      {nChunks>=CHUNKS && <Wire from={[colX+colW, chunkMidY(2)]} to={[mx-78, my]} color={COLORS.purple+'88'} width={2} draw={ramp(lt,4.4,5.0)} />}

      {/* per-query token counter */}
      {showCount > 0.01 && (
        <div style={{ position:'absolute', left:mx, top:my+150, transform:'translate(-50%,0)', opacity:showCount, textAlign:'center', width:420 }}>
          <div style={{ fontFamily:FONTS.sans, fontSize:20, color:COLORS.inkDim, letterSpacing:'0.04em' }}>injected every query</div>
          <div style={{ fontFamily:FONTS.math, fontSize:90, fontWeight:700, color:COLORS.purple, lineHeight:1, fontVariantNumeric:'tabular-nums' }}>
            +{fmtNum(totalChunkTok)}
          </div>
          <div style={{ fontFamily:FONTS.math, fontStyle:'italic', fontSize:21, color:COLORS.inkDim }}>{CHUNKS} chunks × {TOK_PER} tokens</div>
        </div>
      )}

      {/* cache-bust callout */}
      {bustPhase && (
        <div style={{ position:'absolute', left:colX+colW/2, top:chunkY0 + CHUNKS*(ch+cgap) + 16, transform:'translate(-50%,0)',
          opacity:pulse(lt,8.8,23,0.4), textAlign:'center', width:520 }}>
          <div style={{ fontFamily:FONTS.sans, fontSize:22, fontWeight:600, color:COLORS.coral }}>
            chunks change every query → <b>cache miss</b>
          </div>
          <div style={{ fontFamily:FONTS.sans, fontSize:18, color:COLORS.inkDim, marginTop:4 }}>
            only the stable system prompt keeps the discount
          </div>
        </div>
      )}

      {/* cost line */}
      {showCost > 0.01 && (
        <div style={{ position:'absolute', left:mx, top:my-210, transform:'translate(-50%,0)', opacity:showCost, textAlign:'center', width:460 }}>
          <div style={{ fontFamily:FONTS.math, fontSize:30, color:COLORS.ink }}>
            {fmtNum(CHUNKS*TOK_PER)} <span style={{color:COLORS.inkFaint}}>×</span> $3/M <span style={{color:COLORS.inkFaint}}>=</span> <b style={{color:COLORS.coral}}>{fmtUSD(perQueryCost)}</b>
          </div>
          <div style={{ fontFamily:FONTS.sans, fontSize:19, color:COLORS.inkDim, marginTop:6 }}>per query — on <b style={{color:COLORS.coral}}>every</b> call, at full fresh price</div>
        </div>
      )}

      <Caption lt={lt} a={1.2} b={4.6}>
        <b style={{color:COLORS.purple}}>RAG</b> retrieves the top-K chunks from a vector DB and injects them into the prompt.
      </Caption>
      <Caption lt={lt} a={4.8} b={8.4}>
        Five chunks of ~512 tokens = <b>2,560 fresh input tokens</b> added to every query.
      </Caption>
      <Caption lt={lt} a={8.8} b={13.2} color={COLORS.coral}>
        And they're <b>query-specific</b> — so unlike the system prompt, they rarely hit the cache.
      </Caption>
      <Caption lt={lt} a={13.6} b={17.8}>
        That's full-price input tokens, paid on <b>every single call</b>.
      </Caption>
      <Caption lt={lt} a={18.2} b={23} color={COLORS.purple}>
        The levers: retrieve <b>fewer, smaller</b> chunks, rerank hard, and cache hot documents.
      </Caption>
    </>
  );
}

Object.assign(window, { SceneRAG, VectorDB, RagBlock });
