// shared.jsx — TapeScript launch video: shared primitives, charts, scene wrapper.
// Depends on animations.jsx (Easing, interpolate, animate, clamp, useTime, useSprite).

const COL = {
  black:    '#050505',
  charcoal: '#0B0B0B',
  panel:    '#0d0d0c',
  vanilla:  '#F4EEDF',
  ivory:    '#EDE4D3',
  line:     'rgba(244,238,223,0.10)',
  lineSoft: 'rgba(244,238,223,0.05)',
  dim:      'rgba(244,238,223,0.42)',
  dimmer:   'rgba(244,238,223,0.22)',
};
const DISP = "'Geist', 'Schibsted Grotesk', system-ui, sans-serif";
const MONO = "'Geist Mono', 'JetBrains Mono', ui-monospace, monospace";

// ── deterministic pseudo-random ───────────────────────────────────────────
function mulberry(seed) {
  let a = seed >>> 0;
  return () => {
    a |= 0; a = (a + 0x6D2B79F5) | 0;
    let t = Math.imul(a ^ (a >>> 15), 1 | a);
    t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  };
}

// Random walk with upward drift → returns array of y in [0..1] (0 bottom)
function genEquity(n, seed, drift = 0.012, vol = 0.05) {
  const r = mulberry(seed);
  const pts = [];
  let v = 0.22;
  for (let i = 0; i < n; i++) {
    // a drawdown dip around 45-60%
    const phase = i / n;
    let d = drift;
    if (phase > 0.42 && phase < 0.6) d = -0.02;
    v += d + (r() - 0.5) * vol;
    v = clamp(v, 0.05, 0.95);
    pts.push(v);
  }
  // normalize so it trends up overall
  return pts;
}

function pointsToPath(pts, w, h, pad = 0) {
  const n = pts.length;
  return pts.map((p, i) => {
    const x = pad + (i / (n - 1)) * (w - pad * 2);
    const y = h - pad - p * (h - pad * 2);
    return `${i === 0 ? 'M' : 'L'}${x.toFixed(1)},${y.toFixed(1)}`;
  }).join(' ');
}

// ── Backdrop — persistent faint grid + vignette, slow drift ────────────────
function Backdrop() {
  const t = useTime();
  const drift = Math.sin(t * 0.12) * 14;
  return (
    <div style={{ position: 'absolute', inset: 0, background: COL.black, overflow: 'hidden' }}>
      <div style={{
        position: 'absolute', inset: -60,
        backgroundImage: `linear-gradient(${COL.lineSoft} 1px, transparent 1px), linear-gradient(90deg, ${COL.lineSoft} 1px, transparent 1px)`,
        backgroundSize: '64px 64px',
        transform: `translate(${drift}px, ${drift * 0.5}px)`,
      }} />
      <div style={{
        position: 'absolute', inset: 0,
        background: 'radial-gradient(120% 90% at 50% 42%, transparent 38%, rgba(0,0,0,0.55) 100%)',
      }} />
    </div>
  );
}

// ── Scene wrapper: fade in/out + subtle camera drift ───────────────────────
function Scene({ children, fadeIn = 0.6, fadeOut = 0.7, zoomFrom = 1.0, zoomTo = 1.035, drift = true }) {
  const { localTime, duration } = useSprite();
  const exitStart = Math.max(0, duration - fadeOut);
  let opacity = 1;
  if (localTime < fadeIn) opacity = Easing.easeOutCubic(clamp(localTime / fadeIn, 0, 1));
  else if (localTime > exitStart) opacity = 1 - Easing.easeInCubic(clamp((localTime - exitStart) / fadeOut, 0, 1));
  const p = duration > 0 ? clamp(localTime / duration, 0, 1) : 0;
  const scale = drift ? (zoomFrom + (zoomTo - zoomFrom) * p) : 1;
  return (
    <div style={{
      position: 'absolute', inset: 0,
      opacity,
      transform: `scale(${scale})`,
      transformOrigin: 'center',
      willChange: 'transform, opacity',
    }}>
      {children}
    </div>
  );
}

// ── Panel — charcoal card with hairline border ─────────────────────────────
function Panel({ children, style }) {
  return (
    <div style={{
      background: COL.panel,
      border: `1px solid ${COL.line}`,
      borderRadius: 14,
      boxShadow: '0 30px 80px rgba(0,0,0,0.55)',
      position: 'relative',
      overflow: 'hidden',
      ...style,
    }}>{children}</div>
  );
}

// ── Mono label, uppercase ──────────────────────────────────────────────────
function Kicker({ children, style }) {
  return (
    <div style={{
      fontFamily: MONO, fontSize: 17, letterSpacing: '0.32em',
      textTransform: 'uppercase', color: COL.dim, fontWeight: 500, whiteSpace: 'nowrap', ...style,
    }}>{children}</div>
  );
}

// ── Typewriter — reveals `text` by character based on progress 0..1 ────────
function Typer({ text, progress, style, caret = true }) {
  const n = Math.floor(clamp(progress, 0, 1) * text.length + 0.0001);
  const shown = text.slice(0, n);
  const done = n >= text.length;
  const t = useTime();
  const blink = Math.floor(t * 1.6) % 2 === 0;
  return (
    <span style={style}>
      {shown}
      {caret && (!done || blink) && (
        <span style={{
          display: 'inline-block', width: '0.56em', height: '1.05em',
          marginLeft: 2, transform: 'translateY(0.16em)',
          background: COL.vanilla, opacity: done ? (blink ? 0.9 : 0) : 0.9,
        }} />
      )}
    </span>
  );
}

// ── Equity curve that draws based on `p` (0..1), with leading dot ──────────
function EquityCurve({ p, w = 760, h = 300, seed = 7, stroke = COL.vanilla, sw = 2.5 }) {
  const pts = React.useMemo(() => genEquity(120, seed), [seed]);
  const path = React.useMemo(() => pointsToPath(pts, w, h, 6), [pts, w, h]);
  const ref = React.useRef(null);
  const [len, setLen] = React.useState(1400);
  React.useEffect(() => { if (ref.current) setLen(ref.current.getTotalLength()); }, [path]);
  const draw = clamp(p, 0, 1);
  // leading dot position
  const idx = Math.min(pts.length - 1, Math.floor(draw * (pts.length - 1)));
  const dotX = 6 + (idx / (pts.length - 1)) * (w - 12);
  const dotY = h - 6 - pts[idx] * (h - 12);
  return (
    <svg viewBox={`0 0 ${w} ${h}`} style={{ width: '100%', height: '100%', display: 'block' }}>
      <defs>
        <linearGradient id={`eqfill${seed}`} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={COL.vanilla} stopOpacity="0.12" />
          <stop offset="100%" stopColor={COL.vanilla} stopOpacity="0" />
        </linearGradient>
      </defs>
      {/* fill under curve, masked by draw */}
      <path d={`${path} L${w - 6},${h - 6} L6,${h - 6} Z`} fill={`url(#eqfill${seed})`}
            opacity={draw > 0.04 ? clamp((draw - 0.04) / 0.2, 0, 1) : 0} />
      <path ref={ref} d={path} fill="none" stroke={stroke} strokeWidth={sw}
            strokeLinecap="round" strokeLinejoin="round"
            style={{ strokeDasharray: len, strokeDashoffset: len * (1 - draw) }} />
      {draw > 0.01 && draw < 0.999 && (
        <circle cx={dotX} cy={dotY} r={5} fill={COL.vanilla}>
          <animate attributeName="r" values="4;7;4" dur="1.4s" repeatCount="indefinite" />
        </circle>
      )}
    </svg>
  );
}

// ── Counter — eased count up to `value` based on p ─────────────────────────
function useCount(value, p, ease = Easing.easeOutCubic) {
  return value * ease(clamp(p, 0, 1));
}

Object.assign(window, {
  COL, DISP, MONO, mulberry, genEquity, pointsToPath,
  Backdrop, Scene, Panel, Kicker, Typer, EquityCurve, useCount,
});
