// scenes-core.jsx — shared theme + visual primitives for the AI Cost Explainer
// Exports to window: COLORS, FONTS, Bg, TimeLabeler, Caption, ModelNode, ToolBox,
//   Wire, TokenSquare, TokenRow, Tag, Brace, fmtUSD, lt helpers.

const COLORS = {
  bg:      '#0c0e14',
  bgPanel: '#141826',
  ink:     '#E9EAF0',
  inkDim:  '#8b93a7',
  inkFaint:'#566076',
  grid:    'rgba(120,140,190,0.07)',
  blue:    '#58C4DD',  // the model
  yellow:  '#FFD43B',  // tools / output tokens
  green:   '#5CD088',  // results / cached
  coral:   '#FC6255',  // cost / alerts
  purple:  '#9B82E0',  // memory / context
};

const FONTS = {
  sans:  '"Space Grotesk", system-ui, sans-serif',
  math:  '"STIX Two Text", Georgia, serif',
  mono:  '"JetBrains Mono", ui-monospace, monospace',
};

// ── tiny tween helpers (built on engine's animate/interpolate) ───────────────
// p(lt, a, b, ease) -> 0..1 ramp between local-times a and b
const ramp = (lt, a, b, ease = Easing.easeInOutCubic) =>
  animate({ from: 0, to: 1, start: a, end: b, ease })(lt);
// in-and-out: fade up over [a,a+d], hold, fade down over [b-d,b]
const pulse = (lt, a, b, d = 0.5) => {
  const up = clamp((lt - a) / d, 0, 1);
  const dn = clamp((b - lt) / d, 0, 1);
  return Math.min(up, dn) * (lt >= a && lt <= b ? 1 : 0);
};

const fmtUSD = (v) => {
  if (v >= 1) return '$' + v.toFixed(2);
  if (v >= 0.01) return '$' + v.toFixed(3);
  return '$' + v.toFixed(4);
};
const fmtNum = (v) => v.toLocaleString('en-US');

// ── Background: dot grid + radial vignette ───────────────────────────────────
function Bg({ accent = null }) {
  return (
    <div style={{ position: 'absolute', inset: 0, background: COLORS.bg, overflow: 'hidden' }}>
      <div style={{
        position: 'absolute', inset: 0,
        backgroundImage: `radial-gradient(${COLORS.grid} 1.4px, transparent 1.4px)`,
        backgroundSize: '46px 46px',
        backgroundPosition: 'center',
        maskImage: 'radial-gradient(ellipse 85% 80% at 50% 50%, #000 55%, transparent 100%)',
        WebkitMaskImage: 'radial-gradient(ellipse 85% 80% at 50% 50%, #000 55%, transparent 100%)',
      }} />
      {accent && (
        <div style={{
          position: 'absolute', inset: 0,
          background: `radial-gradient(ellipse 60% 55% at 50% 45%, ${accent}22, transparent 70%)`,
        }} />
      )}
      <div style={{
        position: 'absolute', inset: 0,
        boxShadow: 'inset 0 0 320px 80px rgba(0,0,0,0.55)',
        pointerEvents: 'none',
      }} />
    </div>
  );
}

// ── writes the current timestamp onto the video root each second ─────────────
function TimeLabeler() {
  const time = useTime();
  React.useEffect(() => {
    const root = document.getElementById('video-root');
    if (root) root.setAttribute('data-screen-label', `t=${time.toFixed(0)}s`);
  }, [Math.floor(time)]);
  return null;
}

// ── Caption: bottom-centred subtitle line, fades per beat ────────────────────
function Caption({ lt, a, b, children, color = COLORS.ink, sub = false }) {
  const o = pulse(lt, a, b, 0.45);
  if (o <= 0.001) return null;
  return (
    <div style={{
      position: 'absolute', left: '50%', bottom: sub ? 78 : 118,
      transform: `translate(-50%, ${(1 - o) * 10}px)`,
      opacity: o,
      fontFamily: FONTS.sans,
      fontSize: sub ? 28 : 40,
      fontWeight: sub ? 400 : 500,
      letterSpacing: '-0.01em',
      color: sub ? COLORS.inkDim : color,
      textAlign: 'center',
      maxWidth: 1500,
      lineHeight: 1.25,
      textWrap: 'balance',
    }}>
      {children}
    </div>
  );
}

// ── Section eyebrow label (top-left chapter marker) ──────────────────────────
function Eyebrow({ lt, a, b, n, label, color = COLORS.blue }) {
  const o = pulse(lt, a, b, 0.5);
  if (o <= 0.001) return null;
  return (
    <div style={{
      position: 'absolute', left: 84, top: 70, opacity: o,
      display: 'flex', alignItems: 'center', gap: 14,
      fontFamily: FONTS.mono, fontSize: 22, letterSpacing: '0.18em',
      textTransform: 'uppercase', color: COLORS.inkDim,
    }}>
      <span style={{ color, fontWeight: 700 }}>{n}</span>
      <span style={{ width: 30, height: 1, background: COLORS.inkFaint, flexShrink: 0 }} />
      <span style={{ whiteSpace: 'nowrap' }}>{label}</span>
    </div>
  );
}

// ── ModelNode: glowing circle = the LLM ──────────────────────────────────────
function ModelNode({ x, y, r = 92, scale = 1, glow = 1, label = 'LLM', color = COLORS.blue, thinking = 0 }) {
  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: r * 2.7, height: r * 2.7, borderRadius: '50%',
        background: `radial-gradient(circle, ${color}33, transparent 68%)`,
        opacity: glow,
      }} />
      <div style={{
        width: r * 2, height: r * 2, borderRadius: '50%',
        background: `radial-gradient(circle at 38% 32%, ${color}, ${color}bb 45%, ${color}55 100%)`,
        border: `2px solid ${color}`,
        boxShadow: `0 0 ${40 * glow}px ${color}66, inset 0 0 40px rgba(255,255,255,0.18)`,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        position: 'relative',
      }}>
        <span style={{ fontFamily: FONTS.sans, fontWeight: 700, fontSize: r * 0.46, color: '#08121a', letterSpacing: '0.02em' }}>{label}</span>
        {thinking > 0 && (
          <div style={{ position: 'absolute', bottom: -2, left: '50%', transform: 'translateX(-50%)', display: 'flex', gap: 7 }}>
            {[0,1,2].map(i => {
              const a = (Math.sin(thinking * 6 - i * 0.9) + 1) / 2;
              return <span key={i} style={{ width: 10, height: 10, borderRadius: '50%', background: '#08121a', opacity: 0.35 + a * 0.6 }} />;
            })}
          </div>
        )}
      </div>
    </div>
  );
}

// ── ToolBox: a function the model can call ───────────────────────────────────
function ToolBox({ x, y, w = 300, sig = 'get_weather(city)', desc = 'live weather data', color = COLORS.yellow, active = 0, scale = 1, icon = '⚙' }) {
  return (
    <div style={{
      position: 'absolute', left: x, top: y, width: w,
      transform: `translate(-50%,-50%) scale(${scale})`,
      background: COLORS.bgPanel,
      border: `2px solid ${active > 0.5 ? color : color + '66'}`,
      borderRadius: 16,
      boxShadow: active > 0.5 ? `0 0 34px ${color}44` : '0 10px 30px rgba(0,0,0,0.4)',
      padding: '18px 22px',
      transition: 'none',
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 10 }}>
        <span style={{
          width: 34, height: 34, borderRadius: 9, flexShrink: 0,
          background: color + '22', border: `1px solid ${color}66`,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          fontSize: 18, color,
        }}>{icon}</span>
        <span style={{ fontFamily: FONTS.mono, fontSize: 24, color: COLORS.ink, fontWeight: 600 }}>{sig}</span>
      </div>
      <div style={{ fontFamily: FONTS.sans, fontSize: 18, color: COLORS.inkDim, paddingLeft: 46 }}>{desc}</div>
    </div>
  );
}

// ── Wire: connector with optional travelling pulse ───────────────────────────
// from/to in canvas coords. flow 0..1 places the pulse; dir +1 (a→b) or -1.
function Wire({ from, to, color = COLORS.inkFaint, width = 2.5, draw = 1, flow = null, dashed = false, glow = false }) {
  const dx = to[0] - from[0], dy = to[1] - from[1];
  const len = Math.hypot(dx, dy);
  const ang = Math.atan2(dy, dx) * 180 / Math.PI;
  const shown = len * clamp(draw, 0, 1);
  return (
    <div style={{
      position: 'absolute', left: from[0], top: from[1],
      width: len, height: width,
      transform: `translateY(-50%) rotate(${ang}deg)`, transformOrigin: '0 50%',
    }}>
      <div style={{
        position: 'absolute', left: 0, top: 0, height: width, width: shown,
        background: color, borderRadius: width,
        borderTop: dashed ? `${width}px dashed ${color}` : 'none',
        backgroundImage: dashed ? `repeating-linear-gradient(90deg, ${color} 0 10px, transparent 10px 20px)` : 'none',
        boxShadow: glow ? `0 0 12px ${color}` : 'none',
      }} />
      {flow != null && flow >= 0 && flow <= 1 && (
        <div style={{
          position: 'absolute', left: len * flow - 8, top: '50%',
          transform: 'translateY(-50%)',
          width: 16, height: 16, borderRadius: '50%',
          background: color, boxShadow: `0 0 18px 4px ${color}`,
        }} />
      )}
    </div>
  );
}

// ── Token square (a unit of cost) ────────────────────────────────────────────
function TokenSquare({ size = 22, color = COLORS.blue, o = 1, r = 5 }) {
  return <span style={{ width: size, height: size, borderRadius: r, background: color, opacity: o, display: 'inline-block', boxShadow: `inset 0 0 0 1px rgba(255,255,255,0.12)` }} />;
}

// row/grid of token squares; `n` shown of `total`
function TokenRow({ n, total, color = COLORS.blue, size = 22, gap = 5, cols = 20 }) {
  const cells = [];
  for (let i = 0; i < total; i++) {
    const on = i < n;
    cells.push(<TokenSquare key={i} size={size} color={color} o={on ? 1 : 0.12} />);
  }
  return (
    <div style={{ display: 'grid', gridTemplateColumns: `repeat(${cols}, ${size}px)`, gap }}>
      {cells}
    </div>
  );
}

// ── Tag / pill ───────────────────────────────────────────────────────────────
function Tag({ children, color = COLORS.blue, solid = false, size = 20 }) {
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 8,
      fontFamily: FONTS.mono, fontSize: size, fontWeight: 600,
      padding: '6px 14px', borderRadius: 999,
      color: solid ? '#0c0e14' : color,
      background: solid ? color : color + '1f',
      border: `1px solid ${color}${solid ? '' : '55'}`,
      whiteSpace: 'nowrap',
    }}>{children}</span>
  );
}

Object.assign(window, {
  COLORS, FONTS, ramp, pulse, fmtUSD, fmtNum,
  Bg, TimeLabeler, Caption, Eyebrow, ModelNode, ToolBox, Wire, TokenSquare, TokenRow, Tag,
});
