// ────── shared utilities + small components ──────

function useTick(ms = 1000) {
  const [, set] = React.useState(0);
  React.useEffect(() => {
    const id = setInterval(() => set(x => x + 1), ms);
    return () => clearInterval(id);
  }, [ms]);
}

// LIVE clock — 2026/04/27 13:53:32 ish
function LiveClock() {
  const [t, setT] = React.useState(() => new Date(2026, 3, 27, 13, 53, 32));
  React.useEffect(() => {
    const id = setInterval(() => setT(prev => new Date(prev.getTime() + 1000)), 1000);
    return () => clearInterval(id);
  }, []);
  const pad = n => String(n).padStart(2, '0');
  const txt = `${t.getFullYear()}.${pad(t.getMonth()+1)}.${pad(t.getDate())} ${pad(t.getHours())}:${pad(t.getMinutes())}:${pad(t.getSeconds())}`;
  return (
    <div className="live-badge">
      <span className="dot" />
      <span className="live">LIVE</span>
      <span className="ts">{txt}</span>
    </div>
  );
}

// Animated counter / sparkline
function MicroDial({ value, max = 100, label }) {
  const [v, setV] = React.useState(value);
  React.useEffect(() => {
    const id = setInterval(() => {
      setV(value + (Math.sin(Date.now() / 800) * 1.2));
    }, 100);
    return () => clearInterval(id);
  }, [value]);
  return (
    <span className="num" style={{ fontVariantNumeric: 'tabular-nums' }}>{v.toFixed(0)}</span>
  );
}

// Sparkline SVG
function Sparkline({ data = [], color = 'var(--accent)', height = 28, animate = true }) {
  const w = 120, h = height, pad = 2;
  const max = Math.max(...data), min = Math.min(...data);
  const r = max - min || 1;
  const pts = data.map((v, i) => [pad + (i / (data.length - 1)) * (w - pad * 2),
    h - pad - ((v - min) / r) * (h - pad * 2)]);
  const path = pts.map((p, i) => (i === 0 ? 'M' : 'L') + p[0].toFixed(1) + ',' + p[1].toFixed(1)).join(' ');
  const area = path + ` L${w - pad},${h} L${pad},${h} Z`;
  return (
    <svg width="100%" height={h} viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" style={{ display: 'block' }}>
      <defs>
        <linearGradient id={`spk-${color.replace(/\W/g, '')}`} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={color} stopOpacity="0.3" />
          <stop offset="100%" stopColor={color} stopOpacity="0" />
        </linearGradient>
      </defs>
      <path d={area} fill={`url(#spk-${color.replace(/\W/g, '')})`} />
      <path d={path} fill="none" stroke={color} strokeWidth="1.2" />
      {animate && (
        <circle cx={pts[pts.length - 1][0]} cy={pts[pts.length - 1][1]} r="2" fill={color}>
          <animate attributeName="r" values="2;3.5;2" dur="2s" repeatCount="indefinite" />
        </circle>
      )}
    </svg>
  );
}

// stat tile
function StatTile({ label, value, unit, delta, deltaDir, sparkData, sparkColor }) {
  return (
    <div className="stat-tile corner-ticks scan-target">
      <div className="row">
        <span className="k">{label}</span>
        <span className="sp" />
      </div>
      <div className="v">
        <span>{value}</span>{unit && <span className="unit">{unit}</span>}
      </div>
      {delta && (
        <div className={`delta ${deltaDir === 'down' ? 'dn' : ''}`}>
          {deltaDir === 'down' ? '▼' : '▲'} {delta}
        </div>
      )}
      <div className="spark"><Sparkline data={sparkData} color={sparkColor || 'var(--accent)'} /></div>
    </div>
  );
}

// section header inside page
function SectionHead({ super: sup, title, right }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 14 }}>
      <div>
        <div className="lbl lbl-accent" style={{ marginBottom: 4 }}>{sup}</div>
        <div style={{ fontSize: 18, fontWeight: 600 }}>{title}</div>
      </div>
      {right}
    </div>
  );
}

// notification toast manager
function useToasts() {
  const [list, setList] = React.useState([]);
  const push = React.useCallback((t) => {
    const id = Math.random();
    setList(l => [...l, { ...t, id }]);
    setTimeout(() => setList(l => l.filter(x => x.id !== id)), 3200);
  }, []);
  const node = (
    <div style={{ position: 'fixed', top: 60, right: 24, display: 'flex', flexDirection: 'column', gap: 8, zIndex: 300 }}>
      {list.map(t => (
        <div key={t.id} className={`toast ${t.kind || 'ok'}`} style={{ position: 'relative', top: 0, right: 0 }}>
          <div className={`ico ${t.kind || 'ok'}`}>{t.kind === 'warn' ? '▲' : '✓'}</div>
          <div>
            <div className="msg">{t.msg}</div>
            {t.sub && <div className="sub">{t.sub}</div>}
          </div>
        </div>
      ))}
    </div>
  );
  return [push, node];
}

// terminal log
function TerminalLog() {
  const [lines, setLines] = React.useState([
    { ts: '13:53:01', lvl: 'INFO', msg: 'Mission Control 引导完成', ok: true },
    { ts: '13:53:08', lvl: 'OK', msg: '加载 12 个生产节点 · v1.4.2', ok: true },
    { ts: '13:53:14', lvl: 'INFO', msg: 'KV 缓存命中率 87.3%' },
    { ts: '13:53:22', lvl: 'INFO', msg: '本地草稿已同步 · drafts:3' },
  ]);
  React.useEffect(() => {
    const messages = [
      { lvl: 'INFO', msg: 'Token 桶已重置 · 1024 / 4096' },
      { lvl: 'OK', msg: '节点 NO·02 创作方案 推进中', ok: true },
      { lvl: 'INFO', msg: '资产指针 C001 / S001 已校验' },
      { lvl: 'WARN', msg: '视觉风格弱定向 · 建议补充', err: true },
      { lvl: 'INFO', msg: 'Nano Banana Pro 模型预热中' },
      { lvl: 'OK', msg: '阶段闸门 PHASE-01 通过', ok: true },
      { lvl: 'INFO', msg: '提示词模板 v1.5.0 已加载' },
    ];
    let i = 0;
    const id = setInterval(() => {
      const n = new Date();
      const ts = `${String(n.getHours()).padStart(2,'0')}:${String(n.getMinutes()).padStart(2,'0')}:${String(n.getSeconds()).padStart(2,'0')}`;
      setLines(l => [...l.slice(-3), { ts, ...messages[i % messages.length] }]);
      i++;
    }, 4500);
    return () => clearInterval(id);
  }, []);
  return (
      <div className="term-log">
        <div className="lbl-hdr">● TERMINAL · LIVE</div>
        {lines.map((l, i) => (
        <div key={i} className={`ln ${l.err ? 'err' : ''} ${l.ok ? 'ok' : ''}`}>
          <span className="ts">{l.ts}</span>
          <span className="lvl">[{l.lvl}]</span>
          <span className="msg">{l.msg}</span>
        </div>
      ))}
    </div>
  );
}

window.useTick = useTick;
window.LiveClock = LiveClock;
window.MicroDial = MicroDial;
window.Sparkline = Sparkline;
window.StatTile = StatTile;
window.SectionHead = SectionHead;
window.useToasts = useToasts;
window.TerminalLog = TerminalLog;
