// CertAutoPilot Hero Video — 16:9, ~22s, scene-based
// Scenes:
//  0.0–4.0  : Title — "Certificate lifecycle, on autopilot"
//  4.0–10.0 : Discovery — terminal scan, finding certs
//  10.0–17.0 : Distribution — central hub fans out to nodes (live)
//  17.0–22.0 : Outro — logo + tagline

const DUR = 22;
const W = 1920, H = 1080;
const GOLD = '#d4af63';
const GOLD2 = '#f0d28a';
const GOLD3 = '#8a6e3c';
const BG = '#0a0908';
const TEXT = '#f5efe2';
const T2 = '#b8b0a0';

// Persistent timestamp label for comments
function TimestampLabel(){
  const t = useTime();
  React.useEffect(()=>{
    const root = document.querySelector('[data-stage-root]');
    if(root) root.setAttribute('data-screen-label', `t=${t.toFixed(1)}s`);
  },[Math.floor(t)]);
  return null;
}

// Background gradient + subtle grain
function Backdrop(){
  const t = useTime();
  const drift = (t % DUR) / DUR;
  return (
    <g>
      <rect x="0" y="0" width={W} height={H} fill={BG}/>
      <defs>
        <radialGradient id="bgGold" cx="50%" cy="50%" r="60%">
          <stop offset="0%" stopColor="#d4af63" stopOpacity="0.18"/>
          <stop offset="60%" stopColor="#d4af63" stopOpacity="0.04"/>
          <stop offset="100%" stopColor="#d4af63" stopOpacity="0"/>
        </radialGradient>
        <linearGradient id="goldText" x1="0" y1="0" x2="1" y2="1">
          <stop offset="0%" stopColor="#f0d28a"/>
          <stop offset="60%" stopColor="#d4af63"/>
          <stop offset="100%" stopColor="#8a6e3c"/>
        </linearGradient>
        <linearGradient id="goldStroke" x1="0" y1="0" x2="1" y2="0">
          <stop offset="0%" stopColor="#f0d28a" stopOpacity="0"/>
          <stop offset="50%" stopColor="#f0d28a" stopOpacity="1"/>
          <stop offset="100%" stopColor="#f0d28a" stopOpacity="0"/>
        </linearGradient>
        <filter id="softGlow"><feGaussianBlur stdDeviation="6"/></filter>
        <filter id="hardGlow"><feGaussianBlur stdDeviation="2"/></filter>
      </defs>
      <ellipse cx={W*0.5 + Math.sin(drift*Math.PI*2)*60} cy={H*0.5 + Math.cos(drift*Math.PI*2)*40} rx={W*0.6} ry={H*0.6} fill="url(#bgGold)"/>

      {/* grid */}
      <g opacity="0.18" stroke="#d4af63" strokeWidth="0.5">
        {Array.from({length: 24}).map((_,i)=>(
          <line key={`h${i}`} x1="0" y1={i*60} x2={W} y2={i*60}/>
        ))}
        {Array.from({length: 32}).map((_,i)=>(
          <line key={`v${i}`} x1={i*60} y1="0" x2={i*60} y2={H}/>
        ))}
      </g>
    </g>
  );
}

// Persistent corner brand
function CornerBrand(){
  const t = useTime();
  const op = interpolate([0, 0.5, DUR-0.5, DUR],[0,1,1,0])(t);
  return (
    <g opacity={op} transform={`translate(64 60)`}>
      <circle cx="14" cy="14" r="14" fill="url(#goldText)"/>
      <text x="40" y="20" fill={TEXT} fontFamily="IBM Plex Sans" fontSize="22" fontWeight="500" letterSpacing="-0.01em">
        <tspan fill={GOLD2}>Cert</tspan>AutoPilot
      </text>
    </g>
  );
}

// Scene 1 — Title
function SceneTitle(){
  const { progress, localTime } = useSprite();
  const fadeIn = interpolate([0, 0.3],[0,1], Easing.easeOutCubic)(progress);
  const fadeOut = interpolate([0.85, 1],[1,0], Easing.easeInCubic)(progress);
  const op = Math.min(fadeIn, fadeOut);

  // line reveals
  const l1 = interpolate([0.05, 0.25],[0,1], Easing.easeOutCubic)(progress);
  const l2 = interpolate([0.18, 0.40],[0,1], Easing.easeOutCubic)(progress);
  const l3 = interpolate([0.32, 0.55],[0,1], Easing.easeOutCubic)(progress);

  const yShift = (k)=> 40*(1-k);
  const tagOp = interpolate([0.45, 0.65],[0,1], Easing.easeOutCubic)(progress);

  // background pulse certificate icon faint behind
  const certPulse = 1 + Math.sin(localTime*1.5)*0.04;

  return (
    <g opacity={op}>
      {/* faint cert icon back */}
      <g transform={`translate(${W/2} ${H/2 - 60}) scale(${certPulse*1.2})`} opacity="0.06">
        <rect x="-90" y="-110" width="180" height="220" rx="6" fill="none" stroke={GOLD} strokeWidth="2"/>
        <line x1="-60" y1="-70" x2="60" y2="-70" stroke={GOLD} strokeWidth="2"/>
        <line x1="-60" y1="-40" x2="60" y2="-40" stroke={GOLD} strokeWidth="2"/>
        <line x1="-60" y1="-10" x2="40" y2="-10" stroke={GOLD} strokeWidth="2"/>
        <circle cx="0" cy="50" r="30" fill="none" stroke={GOLD} strokeWidth="2"/>
        <path d="M-15,50 L-5,60 L15,40" fill="none" stroke={GOLD} strokeWidth="3"/>
      </g>

      {/* eyebrow */}
      <g opacity={tagOp}>
        <line x1={W/2 - 280} y1={H/2 - 220} x2={W/2 - 250} y2={H/2 - 220} stroke={GOLD2} strokeWidth="1.5"/>
        <text x={W/2 - 240} y={H/2 - 215} fill={GOLD2} fontFamily="IBM Plex Mono" fontSize="16" letterSpacing="0.18em">SELF-HOSTED · ENTERPRISE · ON-PREMISE</text>
      </g>

      {/* Title */}
      <g transform={`translate(${W/2} ${H/2})`} textAnchor="middle">
        <g opacity={l1} transform={`translate(0 ${-100 + yShift(l1)})`}>
          <text fontFamily="IBM Plex Sans" fontSize="110" fontWeight="300" fill={TEXT} letterSpacing="-0.03em">Certificate lifecycle,</text>
        </g>
        <g opacity={l2} transform={`translate(0 ${30 + yShift(l2)})`}>
          <text fontFamily="IBM Plex Sans" fontSize="110" fontWeight="300" fill="url(#goldText)" letterSpacing="-0.03em" filter="url(#hardGlow)">on autopilot.</text>
          <text fontFamily="IBM Plex Sans" fontSize="110" fontWeight="300" fill="url(#goldText)" letterSpacing="-0.03em">on autopilot.</text>
        </g>
        <g opacity={l3} transform={`translate(0 ${130 + yShift(l3)})`}>
          <text fontFamily="IBM Plex Sans" fontSize="26" fontWeight="300" fill={T2} letterSpacing="0">Discover. Issue. Distribute. Renew. — without lifting a finger.</text>
        </g>
      </g>
    </g>
  );
}

// Scene 2 — Terminal discovery
function SceneTerminal(){
  const { progress, localTime } = useSprite();
  const fadeIn = interpolate([0, 0.15],[0,1], Easing.easeOutCubic)(progress);
  const fadeOut = interpolate([0.88, 1],[1,0], Easing.easeInCubic)(progress);
  const op = Math.min(fadeIn, fadeOut);

  // Typewriter
  const lines = [
    {t: 0.10, txt:'$ certautopilot discover --cidr 10.0.0.0/16', cls:'cmd'},
    {t: 0.22, txt:'[scan] TLS handshake → certificate chain → cipher analysis', cls:'dim'},
    {t: 0.30, txt:'', cls:''},
    {t: 0.32, txt:'HOST                  SUBJECT               EXPIRES   STATUS', cls:'head'},
    {t: 0.38, txt:'10.0.1.15:443         api.internal.co       12 days   WARN', cls:'warn-row'},
    {t: 0.44, txt:'10.0.1.22:443         auth.internal.co      89 days   OK', cls:'ok-row'},
    {t: 0.50, txt:'10.0.2.8:8443         payments.internal.co   3 days   CRITICAL', cls:'crit-row'},
    {t: 0.56, txt:'10.0.3.44:443         cdn.internal.co       241 days  OK', cls:'ok-row'},
    {t: 0.62, txt:'10.0.5.12:443         mail.internal.co      Self-sign WARN', cls:'warn-row'},
    {t: 0.70, txt:'', cls:''},
    {t: 0.72, txt:'✓ Discovered 847 certs across 1,204 endpoints', cls:'success'},
    {t: 0.78, txt:'⚠ 12 expiring within 30 days · 14 findings generated', cls:'warn'},
    {t: 0.84, txt:'', cls:''},
    {t: 0.86, txt:'$ certautopilot enroll --auto-renew --distribute', cls:'cmd'},
  ];

  const colorFor = (cls, txt) => {
    if (cls==='cmd') return TEXT;
    if (cls==='head') return GOLD;
    if (cls==='dim') return T2;
    if (cls==='success') return '#4ade80';
    if (cls==='warn') return '#f59e0b';
    if (cls==='ok-row') return TEXT;
    if (cls==='warn-row') return TEXT;
    if (cls==='crit-row') return TEXT;
    return TEXT;
  };

  // Render typed substring per line
  const renderLine = (line, idx) => {
    if (progress < line.t) return null;
    const charSpeed = 0.0025; // 400 chars/s
    const elapsed = progress - line.t;
    const numChars = Math.min(line.txt.length, Math.floor(elapsed / charSpeed));
    const visible = line.txt.slice(0, numChars);
    let color = colorFor(line.cls, visible);

    // colorize status word at end
    let mainText = visible;
    let trailingColor = null;
    let trailingText = '';
    if (line.cls === 'ok-row' && visible.endsWith('OK')) {
      mainText = visible.slice(0,-2);
      trailingText = 'OK';
      trailingColor = '#4ade80';
    } else if (line.cls === 'warn-row' && visible.endsWith('WARN')) {
      mainText = visible.slice(0,-4);
      trailingText = 'WARN';
      trailingColor = '#f59e0b';
    } else if (line.cls === 'crit-row' && visible.endsWith('CRITICAL')) {
      mainText = visible.slice(0,-8);
      trailingText = 'CRITICAL';
      trailingColor = '#ef4444';
    }

    const y = 90 + idx * 40;
    return (
      <g key={idx}>
        <text x="40" y={y} fontFamily="IBM Plex Mono" fontSize="22" fill={color}>
          {mainText}
          {trailingText && <tspan fill={trailingColor} fontWeight="600">{trailingText}</tspan>}
        </text>
      </g>
    );
  };

  // blinking cursor at last visible line
  const cursorY = 90 + lines.findLastIndex(l => progress >= l.t) * 40;
  const cursorBlink = Math.floor(localTime*2) % 2 === 0;

  // Window dimensions
  const winX = 200, winY = 120, winW = W-400, winH = H-260;

  return (
    <g opacity={op}>
      {/* Title up top */}
      <g opacity={interpolate([0.05, 0.2],[0,1])(progress)}>
        <text x={W/2} y="80" textAnchor="middle" fill={GOLD2} fontFamily="IBM Plex Mono" fontSize="18" letterSpacing="0.18em">— DISCOVERY —</text>
      </g>

      {/* Terminal window */}
      <g transform={`translate(${winX} ${winY})`}>
        <rect x="0" y="0" width={winW} height={winH} rx="14" fill="#08070a" stroke="rgba(212,175,99,0.18)" strokeWidth="1.2"/>
        {/* glow */}
        <rect x="0" y="0" width={winW} height={winH} rx="14" fill="none" stroke="url(#goldStroke)" strokeWidth="1" opacity="0.4"/>

        {/* Title bar */}
        <rect x="0" y="0" width={winW} height="50" rx="14" fill="#0d0c10"/>
        <rect x="0" y="36" width={winW} height="14" fill="#0d0c10"/>
        <line x1="0" y1="50" x2={winW} y2="50" stroke="rgba(212,175,99,0.18)" strokeWidth="1"/>
        <circle cx="24" cy="25" r="6" fill="#5c4a2a"/>
        <circle cx="46" cy="25" r="6" fill="#4a4030"/>
        <circle cx="68" cy="25" r="6" fill="#3a342c"/>
        <text x={winW/2} y="32" textAnchor="middle" fill={T2} fontFamily="IBM Plex Mono" fontSize="14" letterSpacing="0.1em">certificate-discovery — live</text>

        {/* Body */}
        <g transform={`translate(0 50)`}>
          {lines.map((l, i) => renderLine(l, i))}
          {cursorBlink && (
            <rect x={40 + (lines[lines.findLastIndex(l => progress >= l.t)]?.txt?.length || 0) * 13.2 - 5} y={cursorY - 22} width="12" height="26" fill={GOLD2}/>
          )}
        </g>
      </g>
    </g>
  );
}

// Scene 3 — Distribution (hub fans out to nodes with packets)
function SceneDistribution(){
  const { progress, localTime } = useSprite();
  const fadeIn = interpolate([0, 0.12],[0,1], Easing.easeOutCubic)(progress);
  const fadeOut = interpolate([0.90, 1],[1,0], Easing.easeInCubic)(progress);
  const op = Math.min(fadeIn, fadeOut);

  const cx = W/2, cy = H/2 + 30;
  // 12 nodes evenly spaced — 30° apart, offset so none collide at the top
  const nodes = [
    {label:'k8s.prod',     a:-75, type:'K8S'},
    {label:'api.internal', a:-45, type:'SSH'},
    {label:'edge-f5',      a:-15, type:'F5'},
    {label:'vault-01',     a: 15, type:'VLT'},
    {label:'iis-web',      a: 45, type:'IIS'},
    {label:'ns-cdn',       a: 75, type:'NS'},
    {label:'mail-mx',      a:105, type:'SSH'},
    {label:'auth-svc',     a:135, type:'K8S'},
    {label:'payments',     a:165, type:'F5'},
    {label:'cdn-eu',       a:195, type:'WHK'},
    {label:'gw-apac',      a:225, type:'NS'},
    {label:'analytics',    a:255, type:'VLT'},
  ];
  const R = 360;

  // packet timings: each node gets a packet wave staggered
  const waveStart = 0.08;
  const perNode = 0.05;
  const travelDur = 0.20;

  return (
    <g opacity={op}>
      <text x={W/2} y="80" textAnchor="middle" fill={GOLD2} fontFamily="IBM Plex Mono" fontSize="18" letterSpacing="0.18em">— DISTRIBUTION —</text>

      {/* orbit rings */}
      {[180, 280, 380].map((r,i)=>(
        <circle key={i} cx={cx} cy={cy} r={r} fill="none" stroke={GOLD} strokeOpacity={0.18 - i*0.04} strokeDasharray="3 8"/>
      ))}

      {/* Rotating tick ring */}
      <g transform={`rotate(${localTime * 12} ${cx} ${cy})`}>
        {Array.from({length: 60}).map((_,i)=>{
          const ang = (i/60) * Math.PI*2;
          const r1 = 410, r2 = i%5===0 ? 425 : 418;
          return <line key={i} x1={cx + Math.cos(ang)*r1} y1={cy + Math.sin(ang)*r1} x2={cx + Math.cos(ang)*r2} y2={cy + Math.sin(ang)*r2} stroke={GOLD} strokeOpacity={i%5===0 ? 0.5 : 0.2} strokeWidth={i%5===0 ? 1.5 : 1}/>;
        })}
      </g>

      {/* Paths + nodes */}
      {nodes.map((n, idx)=>{
        const rad = n.a * Math.PI/180;
        const x = cx + Math.cos(rad) * R;
        const y = cy + Math.sin(rad) * R;
        const mx = (cx+x)/2, my = (cy+y)/2;
        const dx = x-cx, dy = y-cy;
        const len = Math.hypot(dx,dy);
        const px = -dy/len, py = dx/len;
        const bend = (idx%2===0?1:-1) * 60;
        const qx = mx + px*bend, qy = my + py*bend;

        const nodeStart = waveStart + idx * perNode;
        const arrived = progress >= nodeStart + travelDur;
        const inflight = progress >= nodeStart && progress < nodeStart + travelDur;
        const localT = inflight ? (progress - nodeStart) / travelDur : 0;

        // bezier point
        const u = 1 - localT;
        const pktX = u*u*cx + 2*u*localT*qx + localT*localT*x;
        const pktY = u*u*cy + 2*u*localT*qy + localT*localT*y;

        // path opacity
        const pathOp = inflight ? 0.6 : (arrived ? 0.18 : 0.08);
        const nodeColor = arrived ? '#4ade80' : GOLD;
        const nodeStroke = arrived ? 'rgba(74,222,128,0.6)' : 'rgba(212,175,99,0.4)';
        const labelOffset = (y < cy ? -50 : 56);
        const arrivePulse = arrived ? Math.max(0, 1 - (progress - nodeStart - travelDur) * 5) : 0;

        return (
          <g key={idx}>
            <path d={`M ${cx} ${cy} Q ${qx} ${qy} ${x} ${y}`} fill="none" stroke={GOLD2} strokeOpacity={pathOp} strokeWidth={inflight ? 2 : 1}/>
            {inflight && (
              <g>
                <circle cx={pktX} cy={pktY} r="6" fill={GOLD2} filter="url(#softGlow)"/>
                <circle cx={pktX} cy={pktY} r="3" fill="#fff"/>
              </g>
            )}
            {/* node */}
            <g transform={`translate(${x} ${y})`}>
              <circle r={36 + arrivePulse*16} fill="none" stroke={nodeStroke} strokeWidth="1" opacity={arrived ? (0.5 + arrivePulse*0.5) : 0.4}/>
              <circle r="14" fill="#1a1714" stroke={nodeColor} strokeWidth="2"/>
              <circle r="5" fill={nodeColor}/>
              {arrived && <text y="3" textAnchor="middle" fill="#06050a" fontFamily="IBM Plex Mono" fontSize="10" fontWeight="700">✓</text>}
              <text y={labelOffset} textAnchor="middle" fill={TEXT} fontFamily="IBM Plex Mono" fontSize="18" letterSpacing="0.04em">{n.label}</text>
              <text y={labelOffset + 22} textAnchor="middle" fill={T2} fontFamily="IBM Plex Mono" fontSize="13" letterSpacing="0.1em">{n.type}</text>
            </g>
          </g>
        );
      })}

      {/* Hub */}
      <g>
        <circle cx={cx} cy={cy} r={120 + Math.sin(localTime*2)*6} fill="url(#bgGold)" opacity="0.6"/>
        <circle cx={cx} cy={cy} r="48" fill="url(#goldText)" filter="url(#softGlow)"/>
        <circle cx={cx} cy={cy} r="22" fill="#1a1209" stroke={GOLD2} strokeWidth="2"/>
        <text x={cx} y={cy+5} textAnchor="middle" fill={GOLD2} fontFamily="IBM Plex Mono" fontSize="14" fontWeight="600" letterSpacing="0.1em">CA</text>
        <text x={cx} y={cy + 100} textAnchor="middle" fill={T2} fontFamily="IBM Plex Mono" fontSize="14" letterSpacing="0.2em">CERTAUTOPILOT</text>
      </g>

      {/* Counter */}
      <g transform={`translate(120 ${H-180})`}>
        <text fill={GOLD2} fontFamily="IBM Plex Mono" fontSize="16" letterSpacing="0.12em">DISTRIBUTED</text>
        <text y="60" fill={TEXT} fontFamily="IBM Plex Sans" fontSize="72" fontWeight="300" letterSpacing="-0.02em">
          {Math.min(nodes.length, Math.max(0, Math.floor((progress - waveStart) / perNode + 1)))}
          <tspan fill={T2} fontSize="36"> / {nodes.length}</tspan>
        </text>
        <text y="100" fill={T2} fontFamily="IBM Plex Mono" fontSize="14" letterSpacing="0.1em">endpoints · fan-out execution</text>
      </g>
      <g transform={`translate(${W-460} ${H-180})`}>
        <text fill={GOLD2} fontFamily="IBM Plex Mono" fontSize="16" letterSpacing="0.12em">RENEWAL TIME</text>
        <text y="60" fill={TEXT} fontFamily="IBM Plex Sans" fontSize="72" fontWeight="300" letterSpacing="-0.02em">
          {(0.6 + (1-progress)*0.3).toFixed(2)}<tspan fill={T2} fontSize="36">s</tspan>
        </text>
        <text y="100" fill={T2} fontFamily="IBM Plex Mono" fontSize="14" letterSpacing="0.1em">avg p95 · zero downtime</text>
      </g>
    </g>
  );
}

// Scene 4 — Outro
function SceneOutro(){
  const { progress, localTime } = useSprite();
  const fadeIn = interpolate([0, 0.2],[0,1], Easing.easeOutCubic)(progress);
  const op = fadeIn;
  const lineGrow = interpolate([0.3, 0.7],[0,1], Easing.easeOutCubic)(progress);
  const subOp = interpolate([0.45, 0.7],[0,1])(progress);
  const ctaOp = interpolate([0.6, 0.85],[0,1])(progress);
  const logoScale = 1 + Math.sin(localTime*1.4)*0.03;

  return (
    <g opacity={op}>
      <g transform={`translate(${W/2} ${H/2 - 120})`} textAnchor="middle">
        <g transform={`scale(${logoScale})`}>
          <circle cx="0" cy="0" r="56" fill="url(#goldText)" filter="url(#softGlow)" opacity="0.9"/>
          <circle cx="0" cy="0" r="24" fill="#06050a" stroke={GOLD2} strokeWidth="2"/>
          <text y="6" textAnchor="middle" fill={GOLD2} fontFamily="IBM Plex Mono" fontSize="14" fontWeight="700">CAP</text>
        </g>
      </g>

      <g transform={`translate(${W/2} ${H/2 + 30})`} textAnchor="middle">
        <text fontFamily="IBM Plex Sans" fontSize="78" fontWeight="300" fill={TEXT} letterSpacing="-0.03em">Automate your certificates.</text>
        <g opacity={subOp}>
          <text y="100" fontFamily="IBM Plex Sans" fontSize="78" fontWeight="300" fill="url(#goldText)" letterSpacing="-0.03em">End the renewal panic.</text>
        </g>
      </g>

      {/* Underline */}
      <g transform={`translate(${W/2} ${H/2 + 170})`}>
        <line x1={-300*lineGrow} y1="0" x2={300*lineGrow} y2="0" stroke="url(#goldStroke)" strokeWidth="2"/>
      </g>

      <g opacity={ctaOp} transform={`translate(${W/2} ${H/2 + 260})`} textAnchor="middle">
        <text fontFamily="IBM Plex Mono" fontSize="20" fill={T2} letterSpacing="0.18em">CERTAUTOPILOT.COM</text>
        <text y="40" fontFamily="IBM Plex Mono" fontSize="14" fill={GOLD3} letterSpacing="0.12em">SELF-HOSTED · ENTERPRISE-READY · ON-PREMISE</text>
      </g>
    </g>
  );
}

function Video(){
  return (
    <svg width={W} height={H} viewBox={`0 0 ${W} ${H}`} xmlns="http://www.w3.org/2000/svg" style={{display:'block', width: W, height: H}}>
      <Backdrop/>
      <CornerBrand/>
      <Sprite start={0} end={4.2}><SceneTitle/></Sprite>
      <Sprite start={4.0} end={10.4}><SceneTerminal/></Sprite>
      <Sprite start={10.0} end={17.4}><SceneDistribution/></Sprite>
      <Sprite start={17.0} end={DUR}><SceneOutro/></Sprite>
    </svg>
  );
}

function VideoRoot(){
  return (
    <React.Fragment>
      <TimestampLabel/>
      <Video/>
    </React.Fragment>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(
  <div data-stage-root>
    <Stage width={W} height={H} duration={DUR} background={BG}>
      <VideoRoot/>
    </Stage>
  </div>
);
