/* ============================================================
   CHAINUS — data-viz primitives (native SVG/CSS, no deps)
   Sparkline · CandleChart · Gauge · Ticker · LiquidityBars ·
   RatesList · LiveNumber · Donut
   Exported to window for cross-file use.
   ============================================================ */

/* seeded pseudo-random so charts are stable across renders */
function seeded(seed) {
  let s = seed % 2147483647;
  if (s <= 0) s += 2147483646;
  return () => (s = (s * 16807) % 2147483647) / 2147483647;
}

/* ---- in-view hook: fires once when element enters viewport ---- */
function useInView(threshold = 0.4) {
  const ref = useRef(null);
  const [seen, setSeen] = useState(false);
  useEffect(() => {
    const el = ref.current; if (!el) return;
    if (reduceMotion()) { setSeen(true); return; }
    const io = new IntersectionObserver((es) => {
      es.forEach((e) => { if (e.isIntersecting) { setSeen(true); io.disconnect(); } });
    }, { threshold });
    io.observe(el);
    return () => io.disconnect();
  }, [threshold]);
  return [ref, seen];
}

/* ---- animated number that eases to a target & wiggles "live" ---- */
function LiveNumber({ value, decimals = 0, prefix = '', suffix = '', live = false, className = '' }) {
  const [ref, seen] = useInView(0.6);
  const [val, setVal] = useState(0);
  useEffect(() => {
    if (!seen) return;
    if (reduceMotion()) { setVal(value); return; }
    const t0 = performance.now(); const dur = 1200; const from = 0;
    let raf;
    const tick = (t) => {
      const p = Math.min(1, (t - t0) / dur);
      const e = 1 - Math.pow(1 - p, 3);
      setVal(from + (value - from) * e);
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [seen, value]);
  useEffect(() => {
    if (!live || !seen || reduceMotion()) return;
    const id = setInterval(() => {
      setVal((v) => {
        const jitter = value * (0.0006 * (Math.random() - 0.5));
        return value + jitter * 1;
      });
    }, 1600);
    return () => clearInterval(id);
  }, [live, seen, value]);
  const text = prefix + val.toLocaleString('en-US', { minimumFractionDigits: decimals, maximumFractionDigits: decimals }) + suffix;
  return <span ref={ref} className={`tnum ${className}`}>{text}</span>;
}

/* ---- Sparkline: draws on with stroke-dashoffset ---- */
function Sparkline({ data, color = 'var(--up)', w = 100, h = 30, fill = true, strokeW = 2, id }) {
  const [ref, seen] = useInView(0.5);
  const max = Math.max(...data), min = Math.min(...data);
  const rng = max - min || 1;
  const pts = data.map((d, i) => [ (i / (data.length - 1)) * w, h - ((d - min) / rng) * (h - 4) - 2 ]);
  const line = pts.map((p, i) => `${i === 0 ? 'M' : 'L'}${p[0].toFixed(1)} ${p[1].toFixed(1)}`).join(' ');
  const area = `${line} L${w} ${h} L0 ${h} Z`;
  const gid = id || ('sg' + Math.round(min + max + data.length));
  return (
    <svg ref={ref} className="spark" viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" style={{ width: '100%', height: '100%' }}>
      <defs>
        <linearGradient id={gid} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0" stopColor={color} stopOpacity="0.28" />
          <stop offset="1" stopColor={color} stopOpacity="0" />
        </linearGradient>
      </defs>
      {fill && <path d={area} fill={`url(#${gid})`} className={seen ? 'spark-fill in' : 'spark-fill'} />}
      <path d={line} fill="none" stroke={color} strokeWidth={strokeW} strokeLinecap="round" strokeLinejoin="round"
        className={seen ? 'spark-line in' : 'spark-line'} pathLength="1" />
    </svg>
  );
}

/* ---- Candlestick chart with grid + axis ---- */
function CandleChart({ seed = 7, count = 34, height = 280, labels = true }) {
  const [ref, seen] = useInView(0.35);
  const data = React.useMemo(() => {
    const rnd = seeded(seed);
    let price = 100; const out = [];
    for (let i = 0; i < count; i++) {
      const drift = (rnd() - 0.46) * 6;
      const open = price;
      const close = Math.max(40, open + drift);
      const high = Math.max(open, close) + rnd() * 3.4;
      const low = Math.min(open, close) - rnd() * 3.4;
      out.push({ open, close, high, low, up: close >= open });
      price = close;
    }
    return out;
  }, [seed, count]);

  const w = 560, h = height, padR = 46, padB = 22, padT = 12;
  const plotW = w - padR, plotH = h - padB - padT;
  const allHi = Math.max(...data.map((d) => d.high));
  const allLo = Math.min(...data.map((d) => d.low));
  const rng = allHi - allLo || 1;
  const y = (v) => padT + (1 - (v - allLo) / rng) * plotH;
  const slot = plotW / count; const bw = slot * 0.56;
  const grid = [0, 0.25, 0.5, 0.75, 1];
  const lastClose = data[data.length - 1].close;
  const sparkData = data.map((d) => d.close);

  return (
    <svg ref={ref} viewBox={`0 0 ${w} ${h}`} className={`candles ${seen ? 'in' : ''}`} style={{ width: '100%', height: 'auto' }}>
      {/* horizontal grid + price axis */}
      {grid.map((g, i) => {
        const yy = padT + g * plotH; const price = (allHi - g * rng);
        return (
          <g key={i}>
            <line x1="0" y1={yy} x2={plotW} y2={yy} stroke="var(--divider)" strokeWidth="1" />
            {labels && <text x={plotW + 8} y={yy + 4} className="cx-axis" fill="var(--text-muted)">{price.toFixed(0)}</text>}
          </g>
        );
      })}
      {/* last price marker line */}
      <line x1="0" y1={y(lastClose)} x2={plotW} y2={y(lastClose)} stroke="var(--brand-line)" strokeWidth="1" strokeDasharray="3 4" />
      {/* candles */}
      {data.map((d, i) => {
        const cx = i * slot + slot / 2;
        const col = d.up ? 'var(--up)' : 'var(--down)';
        const yO = y(d.open), yC = y(d.close);
        const top = Math.min(yO, yC), bh = Math.max(2, Math.abs(yC - yO));
        return (
          <g key={i} className="candle" style={{ '--ci': i }}>
            <line x1={cx} y1={y(d.high)} x2={cx} y2={y(d.low)} stroke={col} strokeWidth="1.2" opacity="0.7" />
            <rect x={cx - bw / 2} y={top} width={bw} height={bh} fill={col} rx="1" />
          </g>
        );
      })}
    </svg>
  );
}

/* ---- Sentiment gauge (semicircle) ---- */
function Gauge({ value = 68, label = 'Optimistic' }) {
  const [ref, seen] = useInView(0.5);
  const r = 70, cx = 90, cy = 88, circ = Math.PI * r;
  const pct = Math.max(0, Math.min(100, value)) / 100;
  const dash = `${(circ * pct).toFixed(1)} ${circ.toFixed(1)}`;
  return (
    <div ref={ref} className="gauge">
      <svg viewBox="0 0 180 100">
        <defs>
          <linearGradient id="gaugeg" x1="0" y1="0" x2="1" y2="0">
            <stop offset="0" stopColor="var(--down)" />
            <stop offset="0.5" stopColor="var(--warn)" />
            <stop offset="1" stopColor="var(--up)" />
          </linearGradient>
        </defs>
        <path d={`M ${cx - r} ${cy} A ${r} ${r} 0 0 1 ${cx + r} ${cy}`} fill="none" stroke="var(--well)" strokeWidth="11" strokeLinecap="round" />
        <path d={`M ${cx - r} ${cy} A ${r} ${r} 0 0 1 ${cx + r} ${cy}`} fill="none" stroke="url(#gaugeg)" strokeWidth="11" strokeLinecap="round"
          className={`gauge-arc ${seen ? 'in' : ''}`} pathLength={circ} style={{ '--dash': dash }} />
      </svg>
      <div className="gauge-c">
        <span className="gauge-v tnum">{value}</span>
        <span className="gauge-l">{label}</span>
      </div>
    </div>
  );
}

/* ---- Liquidity / volume bars ---- */
function LiquidityBars({ seed = 3, count = 16 }) {
  const [ref, seen] = useInView(0.4);
  const bars = React.useMemo(() => {
    const rnd = seeded(seed);
    return Array.from({ length: count }, (_, i) => ({ h: 24 + rnd() * 76, up: rnd() > 0.4 }));
  }, [seed, count]);
  return (
    <div ref={ref} className={`liq ${seen ? 'in' : ''}`}>
      {bars.map((b, i) => (
        <span key={i} className="liq-bar" style={{ '--bh': b.h + '%', '--bi': i, background: b.up ? 'var(--up)' : 'var(--down)' }}></span>
      ))}
    </div>
  );
}

/* ---- Donut (allocation) ---- */
function Donut({ segments }) {
  const [ref, seen] = useInView(0.5);
  const r = 34, c = 2 * Math.PI * r; let acc = 0;
  return (
    <svg ref={ref} viewBox="0 0 90 90" className={`donut ${seen ? 'in' : ''}`} style={{ width: 90, height: 90 }}>
      <circle cx="45" cy="45" r={r} fill="none" stroke="var(--well)" strokeWidth="11" />
      {segments.map((s, i) => {
        const len = (s.v / 100) * c;
        const el = <circle key={i} cx="45" cy="45" r={r} fill="none" stroke={s.color} strokeWidth="11"
          strokeDasharray={`${len} ${c}`} strokeDashoffset={-acc} transform="rotate(-90 45 45)"
          style={{ transition: 'stroke-dasharray 0.9s var(--ease)', strokeDasharray: seen ? `${len} ${c}` : `0 ${c}` }} />;
        acc += len; return el;
      })}
    </svg>
  );
}

/* ---- Ticker row data ---- */
const TICKER = [
  { s: 'BTC', n: 'Bitcoin', p: 71204, c: 2.41, d: [60,62,61,64,63,66,68,67,70,71] },
  { s: 'ETH', n: 'Ethereum', p: 3842, c: 1.12, d: [40,41,40,42,43,42,44,45,46,47] },
  { s: 'SOL', n: 'Solana', p: 184.6, c: -0.84, d: [50,52,51,49,50,48,47,49,48,47] },
  { s: 'RWA', n: 'Real-World Assets', p: 12.84, c: 4.27, d: [20,22,24,23,26,28,30,33,35,38] },
  { s: 'LINK', n: 'Chainlink', p: 18.42, c: 0.96, d: [30,31,30,32,33,34,33,35,36,37] },
  { s: 'ARB', n: 'Arbitrum', p: 1.04, c: -1.31, d: [44,43,42,43,41,40,41,39,40,38] },
  { s: 'NFT', n: 'NFT Index', p: 842.3, c: 3.05, d: [55,57,56,59,61,60,63,65,67,69] },
];

function TickerStrip() {
  const row = TICKER.concat(TICKER);
  return (
    <div className="ticker glass">
      <span className="ticker-live tag"><span className="chip-dot" style={{ animation: 'pulse-dot 1.6s infinite' }}></span>Live</span>
      <div className="ticker-track marquee-mask">
        <div className="marquee" style={{ '--dur': '46s' }}>
          {row.map((t, i) => (
            <span key={i} className="ti">
              <b className="ti-s">{t.s}</b>
              <span className="ti-p tnum">${t.p.toLocaleString('en-US', { minimumFractionDigits: t.p < 100 ? 2 : 0 })}</span>
              <span className={`ti-c tnum ${t.c >= 0 ? 'pos' : 'neg'}`}>{t.c >= 0 ? '+' : ''}{t.c.toFixed(2)}%</span>
            </span>
          ))}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, {
  useInView, LiveNumber, Sparkline, CandleChart, Gauge, LiquidityBars, Donut, TickerStrip, TICKER, seeded,
});
