// Ticker tape — canvas-rendered streaming row above the terminal HUD.
// 200+ synthetic tickers, 60fps virtualized scroll, only the visible portion drawn.
// Looks alive without being noisy.

const { useEffect: useTTEffect, useRef: useTTRef, useState: useTTState } = React;

const TT_SYMBOLS = [
  "AAPL","MSFT","NVDA","GOOGL","AMZN","META","TSLA","AVGO","BRK.B","V",
  "JPM","WMT","XOM","UNH","MA","PG","JNJ","HD","LLY","COST",
  "ABBV","CVX","BAC","KO","MRK","PEP","ADBE","NFLX","CRM","TMO",
  "MCD","CSCO","AMD","ACN","ABT","LIN","INTC","WFC","DHR","DIS",
  "TXN","NEE","ORCL","VZ","CMCSA","PM","PFE","INTU","IBM","CAT",
  "GE","BMY","HON","UPS","RTX","T","UNP","COP","NKE","SPGI",
  "AMGN","LOW","SCHW","BLK","BA","ELV","GS","ISRG","DE","GILD",
  "AXP","SBUX","BKNG","TJX","MDT","C","SYK","ADP","CB","PLD",
  "MMC","VRTX","CI","ZTS","REGN","ETN","FI","BSX","SO","PGR",
  "MO","CL","DUK","EOG","NOW","MU","SHW","SLB","AON","CME",
  "ITW","CSX","ICE","BDX","FCX","WM","HUM","TGT","APD","NSC",
  "ATVI","KLAC","PSA","EW","MMM","EMR","SNPS","FDX","AIG","LRCX",
  "MAR","PXD","ROP","ADI","GD","PNC","MCK","COF","SRE","ECL",
  "OXY","CCI","TFC","FIS","PYPL","ROST","KMB","CTAS","NXPI","AEP",
  "USB","KMI","WMB","ANET","TRV","ORLY","KHC","MET","AFL","BIIB",
  "DLR","JCI","MNST","MSCI","HCA","PAYX","MPC","ALL","HSY","STZ",
  "PRU","AZO","CHTR","O","CARR","CMG","MCO","TT","KDP","IDXX",
  "AMP","PSX","DOW","NUE","BK","D","XEL","TEL","PCAR","VLO",
  "AJG","ON","FTNT","LULU","COR","WELL","CTSH","AME","ROK","WBA",
  "PH","FAST","RSG","CPRT","EXC","DD","OTIS","CMI","HLT","HES",
];

function TickerTape() {
  const canvasRef = useTTRef(null);
  const stateRef = useTTRef(null);
  const [paused, setPaused] = useTTState(false);
  const pausedRef = useTTRef(false);

  useTTEffect(() => { pausedRef.current = paused; }, [paused]);

  useTTEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    const dpr = Math.min(window.devicePixelRatio || 1, 2);

    // synthesize 200 tickers with stable seed
    const tickers = TT_SYMBOLS.map((sym, i) => {
      const seed = (sym.charCodeAt(0) * 31 + sym.charCodeAt(1) || 1) * (i + 7);
      const rng = () => { let x = (seed * 9301 + 49297) % 233280; return x / 233280; };
      const base = 20 + rng() * 480;
      return {
        sym,
        price: base,
        prev: base,
        // each ticker has its own micro-drift seed
        drift: (rng() - 0.5) * 0.0018,
        vol: 0.003 + rng() * 0.008,
        flashUntil: 0,
        dir: 0,
      };
    });

    const fmt = (n) => n >= 100 ? n.toFixed(2) : n >= 10 ? n.toFixed(2) : n.toFixed(3);
    const itemWidth = 220; // approximate px per ticker

    let scroll = 0;
    let lastTickUpdate = 0;
    let raf = 0;

    const resize = () => {
      const r = canvas.getBoundingClientRect();
      canvas.width = Math.floor(r.width * dpr);
      canvas.height = Math.floor(r.height * dpr);
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    };
    resize();
    const ro = new ResizeObserver(resize);
    ro.observe(canvas);

    function render(t) {
      const r = canvas.getBoundingClientRect();
      const w = r.width, h = r.height;
      ctx.clearRect(0, 0, w, h);

      // Update prices on a slower cadence (~10 Hz)
      if (t - lastTickUpdate > 100) {
        lastTickUpdate = t;
        tickers.forEach((tk) => {
          const noise = (Math.random() - 0.5) * tk.vol;
          const dp = tk.drift + noise;
          tk.prev = tk.price;
          tk.price = Math.max(0.01, tk.price * (1 + dp));
          if (tk.price > tk.prev * 1.0008) { tk.dir = 1; tk.flashUntil = t + 600; }
          else if (tk.price < tk.prev * 0.9992) { tk.dir = -1; tk.flashUntil = t + 600; }
        });
      }

      // Scroll at constant rate (paused on hover)
      if (!pausedRef.current) scroll += 0.6;

      const totalWidth = tickers.length * itemWidth;
      const firstVisible = Math.floor(scroll / itemWidth);
      const offset = -((scroll % itemWidth));
      const visibleCount = Math.ceil(w / itemWidth) + 2;

      ctx.font = "500 11px 'JetBrains Mono', ui-monospace, monospace";
      ctx.textBaseline = "middle";

      for (let k = 0; k < visibleCount; k++) {
        const idx = ((firstVisible + k) % tickers.length + tickers.length) % tickers.length;
        const tk = tickers[idx];
        const x = offset + k * itemWidth + 14;
        const cy = h / 2;

        // symbol — muted
        ctx.fillStyle = "#7f7466";
        ctx.fillText(tk.sym, x, cy);

        // price — green/red on direction flash, else fg-2
        const flashing = t < tk.flashUntil;
        const dir = tk.dir;
        const symW = ctx.measureText(tk.sym).width;
        let priceColor = "#bdb2a0";
        if (flashing) {
          priceColor = dir > 0 ? "#7fc28a" : dir < 0 ? "#d4847a" : priceColor;
        }
        ctx.fillStyle = priceColor;
        ctx.fillText(fmt(tk.price), x + symW + 10, cy);

        // ▲ / ▼ marker
        const priceW = ctx.measureText(fmt(tk.price)).width;
        if (dir !== 0) {
          ctx.fillStyle = dir > 0 ? "#7fc28a" : "#d4847a";
          ctx.fillText(dir > 0 ? "▲" : "▼", x + symW + 10 + priceW + 6, cy);
        }

        // % change
        const pct = ((tk.price - tk.prev) / tk.prev) * 100;
        if (Math.abs(pct) > 0.01) {
          ctx.fillStyle = "#5a5347";
          ctx.font = "400 9px 'JetBrains Mono', ui-monospace, monospace";
          ctx.fillText(`${pct > 0 ? "+" : ""}${pct.toFixed(2)}%`, x + symW + 10 + priceW + 22, cy);
          ctx.font = "500 11px 'JetBrains Mono', ui-monospace, monospace";
        }

        // separator
        ctx.fillStyle = "#2a241d";
        ctx.fillRect(x + itemWidth - 22, cy - 6, 1, 12);
      }

      // edge fades
      const grad1 = ctx.createLinearGradient(0, 0, 60, 0);
      grad1.addColorStop(0, "rgba(20, 18, 14, 1)");
      grad1.addColorStop(1, "rgba(20, 18, 14, 0)");
      ctx.fillStyle = grad1;
      ctx.fillRect(0, 0, 60, h);

      const grad2 = ctx.createLinearGradient(w - 60, 0, w, 0);
      grad2.addColorStop(0, "rgba(20, 18, 14, 0)");
      grad2.addColorStop(1, "rgba(20, 18, 14, 1)");
      ctx.fillStyle = grad2;
      ctx.fillRect(w - 60, 0, 60, h);

      raf = requestAnimationFrame(render);
    }
    raf = requestAnimationFrame(render);

    return () => {
      cancelAnimationFrame(raf);
      ro.disconnect();
    };
  }, []);

  return (
    <div style={ttStyles.wrap}
         onMouseEnter={() => setPaused(true)}
         onMouseLeave={() => setPaused(false)}>
      <div style={ttStyles.label} className="mono">
        <span style={{ color: "var(--amber)" }}>●</span> TAPE · 200 SYM · 10 HZ
      </div>
      <canvas ref={canvasRef} style={ttStyles.canvas} />
      <div style={ttStyles.right} className="mono">
        {paused ? "PAUSED" : "LIVE"}
      </div>
    </div>
  );
}

const ttStyles = {
  wrap: {
    display: "grid",
    gridTemplateColumns: "auto 1fr auto",
    alignItems: "center",
    height: 24,
    background: "oklch(8% 0.008 55 / 0.92)",
    borderTop: "1px solid var(--line)",
    borderBottom: "1px solid var(--line)",
    fontSize: 10,
    overflow: "hidden",
  },
  label: {
    padding: "0 14px",
    color: "var(--muted)",
    letterSpacing: ".22em",
    fontSize: 9,
    whiteSpace: "nowrap",
    display: "flex",
    alignItems: "center",
    gap: 8,
    height: "100%",
    borderRight: "1px dashed var(--line)",
  },
  canvas: {
    width: "100%",
    height: "100%",
    display: "block",
  },
  right: {
    padding: "0 14px",
    color: "var(--muted)",
    letterSpacing: ".22em",
    fontSize: 9,
    whiteSpace: "nowrap",
    height: "100%",
    display: "flex",
    alignItems: "center",
    borderLeft: "1px dashed var(--line)",
  },
};

window.TickerTape = TickerTape;
