// site-live.jsx — Live telemetry / wire ticker / count-up / intake HUD
// These are the "the machine is on" layer of the site.

const { useState: useS, useEffect: useE, useRef: useR } = React;

// ============================================================
// useCountUp — animates 0→target on intersection
// ============================================================
function useCountUp(target, { duration = 1400, easing = 'easeOut' } = {}) {
  const ref = useR(null);
  const [val, setVal] = useS(0);
  useE(() => {
    if (!ref.current) return;
    let raf;
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (!e.isIntersecting) return;
        const start = performance.now();
        const tick = (now) => {
          const t = Math.min(1, (now - start) / duration);
          const eased = easing === 'easeOut' ? 1 - Math.pow(1 - t, 3) : t;
          setVal(target * eased);
          if (t < 1) raf = requestAnimationFrame(tick);
        };
        raf = requestAnimationFrame(tick);
        io.disconnect();
      });
    }, { threshold: 0.35 });
    io.observe(ref.current);
    return () => { io.disconnect(); if (raf) cancelAnimationFrame(raf); };
  }, [target, duration, easing]);
  return [val, ref];
}

// integer count-up with thousands separators
function CountInt({ to, format = (n) => n.toLocaleString('en-US'), className, style }) {
  const [v, ref] = useCountUp(to);
  return <span ref={ref} className={className} style={style}>{format(Math.round(v))}</span>;
}

// ============================================================
// TelemetryStrip — sticky bar above nav, always running.
// ============================================================
function TelemetryStrip() {
  const [now, setNow] = useS(new Date());
  const [signalsHr, setSignalsHr] = useS(47);
  const [cycle, setCycle] = useS(34);
  const [gpu, setGpu] = useS({ a: 82, b: 87 });
  const [scoring, setScoring] = useS(true);

  useE(() => {
    const t = setInterval(() => {
      setNow(new Date());
      setCycle((c) => (c + 1) % 120);
      if (Math.random() < 0.18) setSignalsHr((v) => v + 1);
      setGpu({
        a: Math.round((78 + Math.random() * 12) * 10) / 10,
        b: Math.round((84 + Math.random() * 10) * 10) / 10,
      });
      if (Math.random() < 0.06) setScoring((s) => !s);
    }, 1000);
    return () => clearInterval(t);
  }, []);

  const utc = now.toISOString().slice(11, 19); // HH:MM:SS

  return (
    <div className="telemetry">
      <div className="telemetry-inner">
        <span className="telemetry-live">
          <span className="telemetry-dot" />
          BUREAU · LIVE
        </span>
        <span className="telemetry-pipe" />
        <span>
          <span className="telemetry-label">Cycle</span>
          <span className="telemetry-val">{String(cycle).padStart(3, ' ')}s / 120</span>
        </span>
        <span className="telemetry-pipe" />
        <span>
          <span className="telemetry-label">Sources</span>
          <span className="telemetry-val">560 / 618</span>
        </span>
        <span className="telemetry-pipe" />
        <span>
          <span className="telemetry-label">Signals · 1h</span>
          <span className="telemetry-val">{signalsHr}</span>
        </span>
        <span className="telemetry-pipe" />
        <span>
          <span className="telemetry-label">Scoring</span>
          <span className="telemetry-val" style={{ color: scoring ? 'var(--teal-400)' : 'var(--gray-400)' }}>
            {scoring ? 'Qwen 3 27B · local' : 'idle'}
          </span>
        </span>
        <span className="telemetry-pipe" />
        <span>
          <span className="telemetry-label">Dual 3090</span>
          <span className="telemetry-val">{gpu.a}°C / {gpu.b}°C</span>
        </span>
        <span className="telemetry-pipe" />
        <span className="telemetry-mute">UTC {utc}</span>
      </div>
    </div>
  );
}

// ============================================================
// WIRE — items + marquee
// ============================================================
const WIRE_BASE = [
  { kind: 'SEC',         text: '8-K filed · MICROSOFT · capex guidance revised', score: 0.71, delta: '2m' },
  { kind: 'FERC',        text: 'PJM peak demand alert · IL/OH industrial load',  score: 0.62, delta: '4m' },
  { kind: 'Reddit',      text: 'r/datacenter · 230 cmts/hr · Quincy WA siting',  score: 0.54, delta: '6m' },
  { kind: '990',         text: 'ProPublica · officer overlap · Taylor TX LLC',   score: 0.78, delta: '8m' },
  { kind: 'USASpending', text: '$112M contract · DOE / cooling research',         score: 0.65, delta: '11m' },
  { kind: 'Congress',    text: 'H.R. 2024 · voice vote · public land transfer',  score: 0.83, delta: '14m' },
  { kind: 'FEC',         text: 'Committee filing · $8.2M · disclosure update',   score: 0.41, delta: '18m' },
  { kind: 'HuggingFace', text: 'Qwen3-32B-Instruct · weights release',            score: 0.32, delta: '22m' },
  { kind: 'Lobby',       text: 'New filing · semiconductor / water permit',      score: 0.69, delta: '25m' },
  { kind: 'EIA',         text: 'Industrial water withdrawal · Texas Q1 update',   score: 0.58, delta: '29m' },
  { kind: 'SEC',         text: '13F · BERKSHIRE · new utilities position',        score: 0.66, delta: '34m' },
  { kind: 'Sentinel',    text: 'fy_end_flush · $14.2M · last 14 days',            score: 0.74, delta: '38m' },
  { kind: 'Reddit',      text: 'r/water · permit denial · Yuba CA',                score: 0.49, delta: '42m' },
  { kind: 'Court',       text: 'PACER · public records suit unsealed · OR',       score: 0.81, delta: '47m' },
  { kind: 'Earnings',    text: 'GOOGL Q1 · datacenter capex up 47% YoY',           score: 0.72, delta: '51m' },
  { kind: 'Lobby',       text: 'Hyperscaler · state-level water transparency',    score: 0.68, delta: '56m' },
];

const KIND_COLOR = {
  SEC: 'var(--amber-400)',
  FERC: 'var(--teal-400)',
  Reddit: 'var(--gray-300)',
  '990': 'var(--amber-400)',
  USASpending: 'var(--amber-400)',
  Congress: 'var(--red-400)',
  FEC: 'var(--red-400)',
  HuggingFace: 'var(--teal-400)',
  Lobby: 'var(--amber-400)',
  EIA: 'var(--teal-400)',
  Sentinel: 'var(--red-400)',
  Court: 'var(--paper)',
  Earnings: 'var(--amber-400)',
};

function WireItem({ item }) {
  const c = KIND_COLOR[item.kind] || 'var(--gray-300)';
  return (
    <span className="wire-item">
      <span className="wire-kind" style={{ color: c, borderColor: c }}>{item.kind}</span>
      <span className="wire-text">{item.text}</span>
      <span className="wire-meta">
        <span className="wire-score">↳ {item.score.toFixed(2)}</span>
        <span className="wire-delta">+{item.delta}</span>
      </span>
    </span>
  );
}

function WireMarquee({ tone = 'dark' }) {
  // Duplicate the list so the CSS marquee loops seamlessly.
  const items = [...WIRE_BASE, ...WIRE_BASE];
  return (
    <div className={`wire-marquee wire-marquee--${tone}`}>
      <div className="wire-rail">
        <span className="wire-rail-label">
          <span className="wire-rail-dot" /> Wire · Last 60 min
        </span>
      </div>
      <div className="wire-viewport">
        <div className="wire-track">
          {items.map((it, i) => <WireItem key={i} item={it} />)}
        </div>
      </div>
    </div>
  );
}

// ============================================================
// IntakeHUD — small live panel that overlays the hero.
// Shows the last ~6 signals; rotates one in every few seconds.
// ============================================================
function IntakeHUD() {
  const [items, setItems] = useS(WIRE_BASE.slice(0, 6));
  const [tick, setTick] = useS(0);
  useE(() => {
    const t = setInterval(() => setTick((x) => x + 1), 3200);
    return () => clearInterval(t);
  }, []);
  useE(() => {
    setItems((prev) => {
      const next = WIRE_BASE[(tick + 6) % WIRE_BASE.length];
      // re-stamp delta as freshest
      return [{ ...next, delta: 'now' }, ...prev.slice(0, 5)];
    });
  }, [tick]);

  return (
    <div className="intake">
      <div className="intake-head">
        <span className="intake-eyebrow">
          <span className="intake-dot" />
          Intake · Meridian
        </span>
        <span className="intake-cycle">120s loop</span>
      </div>
      <div className="intake-body">
        {items.map((it, i) => (
          <div key={`${tick}-${i}`} className="intake-row" style={{ opacity: 1 - i * 0.13, animation: i === 0 ? 'intakeIn .55s ease-out' : undefined }}>
            <span className="intake-kind" style={{ color: KIND_COLOR[it.kind] || 'var(--gray-300)' }}>{it.kind}</span>
            <span className="intake-text">{it.text}</span>
            <span className="intake-score" style={{ color: it.score >= 0.7 ? 'var(--amber-400)' : 'var(--gray-400)' }}>
              {it.score.toFixed(2)}
            </span>
            <span className="intake-delta">{it.delta}</span>
          </div>
        ))}
      </div>
      <div className="intake-foot">
        <span className="intake-mute">Scored by Qwen 3 27B · local</span>
        <span className="intake-mute">Sentinel · 15 detectors</span>
      </div>
    </div>
  );
}

// ============================================================
// Odometer — a rolling-digit count-up for a single big number.
// Each character is animated independently so it FEELS mechanical.
// ============================================================
function Odometer({ value, className, style }) {
  const [v, ref] = useCountUp(parseInt(String(value).replace(/[^\d]/g, ''), 10));
  const formatted = Math.round(v).toLocaleString('en-US');
  const target = String(value);
  const len = target.length;
  // pad the formatted value with spaces to align with the target
  const padded = formatted.padStart(len, ' ');
  return (
    <span ref={ref} className={className} style={{ display: 'inline-flex', ...style }}>
      {padded.split('').map((ch, i) => (
        <span key={i} style={{
          display: 'inline-block',
          width: ch === ',' ? '0.35em' : '0.62em',
          textAlign: 'center',
          fontVariantNumeric: 'tabular-nums',
          color: ch === ',' ? 'var(--amber-400)' : undefined,
        }}>{ch === ' ' ? '\u00a0' : ch}</span>
      ))}
    </span>
  );
}

// ============================================================
// QA Stamp — editorial quality badge from the script rules.
// ============================================================
function QAStamp({ rotation = -4, accent = 'var(--teal-400)' }) {
  return (
    <div className="qa-stamp" style={{ transform: `rotate(${rotation}deg)`, borderColor: accent, color: accent }}>
      <div className="qa-stamp-row">
        <span>QA</span>
        <span className="qa-stamp-sep">/</span>
        <span>DM-2026</span>
      </div>
      <div className="qa-stamp-grid">
        <div><span className="qa-tick">✓</span> 0 em dashes</div>
        <div><span className="qa-tick">✓</span> 0 AI-isms</div>
        <div><span className="qa-tick">✓</span> Sources verified</div>
        <div><span className="qa-tick">✓</span> Three-voice cast</div>
      </div>
    </div>
  );
}

// ============================================================
// EXPORTS
// ============================================================
Object.assign(window, {
  TelemetryStrip, WireMarquee, IntakeHUD, useCountUp, CountInt, Odometer, QAStamp,
});
