/* ============================================================
   ChartField — frosted-glass trading chart, infinite drift.
   Canvas background for the hero. White semi-transparent glass
   candles in three parallax depth layers + flowing wave lines,
   on a transparent canvas (the hero supplies the dark backdrop).
   Pauses when scrolled out of view.

   Props: speed, frost, amp, density ('sparse'|'mid'|'dense')
   ============================================================ */
function ChartField({ speed = 0.7, frost = 0.9, amp = 0.92, density = "mid", theme = "dark", style }) {
  const ref = React.useRef(null);
  const cfg = React.useRef({ speed, frost, amp, density, theme });
  cfg.current = { speed, frost, amp, density, theme };

  React.useEffect(() => {
    const cv = ref.current;
    const host = cv.parentElement;
    const ctx = cv.getContext("2d");
    const reduce = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;

    let W = 0, H = 0, DPR = 1, scl = 1;
    const resize = () => {
      DPR = Math.min(window.devicePixelRatio || 1, 2);
      W = host.clientWidth || window.innerWidth;
      H = host.clientHeight || window.innerHeight;
      cv.style.width = W + "px"; cv.style.height = H + "px";
      cv.width = Math.round(W * DPR); cv.height = Math.round(H * DPR);
      ctx.setTransform(DPR, 0, 0, DPR, 0, 0);
      scl = Math.max(0.7, Math.min(1.5, W / 1280));
    };
    const ro = new ResizeObserver(resize); ro.observe(host); resize();

    // frosted micro-grain
    const grain = document.createElement("canvas"); grain.width = grain.height = 150;
    (function () {
      const g = grain.getContext("2d"); const id = g.createImageData(150, 150);
      for (let i = 0; i < id.data.length; i += 4) {
        const v = 120 + Math.random() * 135;
        id.data[i] = id.data[i + 1] = id.data[i + 2] = v; id.data[i + 3] = 255;
      }
      g.putImageData(id, 0, 0);
    })();
    const grainPat = ctx.createPattern(grain, "repeat");

    const hash = (n) => { const s = Math.sin(n * 127.1 + 31.7) * 43758.5453; return s - Math.floor(s); };
    const wave = (wx, t, a) => a * (
      72 * Math.sin(wx * 0.0015 + t * 0.18) +
      40 * Math.sin(wx * 0.0041 - t * 0.12 + 1.3) +
      18 * Math.sin(wx * 0.0090 + t * 0.27 + 2.1)
    );
    const roundRect = (x, y, w, h, r) => {
      r = Math.max(0, Math.min(r, w / 2, h / 2));
      ctx.beginPath();
      ctx.moveTo(x + r, y);
      ctx.arcTo(x + w, y, x + w, y + h, r); ctx.arcTo(x + w, y + h, x, y + h, r);
      ctx.arcTo(x, y + h, x, y, r); ctx.arcTo(x, y, x + w, y, r);
      ctx.closePath();
    };

    const layers = [
      { par: 0.40, S: 44, w: 10, op: 0.30, glow: 5, base: 0.40, hi: 0.16, hMin: 22, hMax: 120, lbl: false },
      { par: 0.70, S: 74, w: 19, op: 0.60, glow: 11, base: 0.52, hi: 0.30, hMin: 46, hMax: 210, lbl: true },
      { par: 1.00, S: 116, w: 31, op: 1.00, glow: 18, base: 0.60, hi: 0.55, hMin: 68, hMax: 320, lbl: true },
    ];

    function drawCandle(L, x, baseY, n, f, time) {
      const light = cfg.current.theme === "light";
      const RGB = light ? "14,14,12" : "255,255,255";
      const r1 = hash(n), r2 = hash(n * 2.3 + 0.7), r3 = hash(n * 0.7 + 5.1);
      // animated level — the candle body height drifts over time like a live
      // price print (slow swing + small fast wobble), not a frozen shape.
      let frac = 0.5 + 0.5 * (0.62 * Math.sin(n * 0.5 + L.par * 3 + time * 0.5) + 0.38 * (r1 * 2 - 1))
        + 0.09 * Math.sin(time * 1.7 + n * 1.3);
      frac = Math.max(0.05, Math.min(1, frac));
      let h = (L.hMin + (L.hMax - L.hMin) * frac) * scl;
      if (h < 10 * scl) h = 10 * scl;
      const up = r2 > 0.32, w = L.w * scl;
      const wickTop = (6 + r3 * 22) * scl, wickBot = (6 + hash(n + 3) * 22) * scl;
      let yTop, yBot;
      if (up) { yBot = baseY + 4 * scl; yTop = baseY - h; } else { yTop = baseY - 4 * scl; yBot = baseY + h; }
      const op = L.op * f;

      ctx.strokeStyle = "rgba(" + RGB + "," + (0.26 * op) + ")";
      ctx.lineWidth = Math.max(1, 1.1 * scl);
      ctx.beginPath(); ctx.moveTo(x, yTop - wickTop); ctx.lineTo(x, yBot + wickBot); ctx.stroke();

      ctx.save();
      ctx.shadowColor = "rgba(" + RGB + "," + (0.30 * op) + ")"; ctx.shadowBlur = L.glow * scl;
      const g = ctx.createLinearGradient(0, yTop, 0, yBot);
      g.addColorStop(0, "rgba(" + RGB + "," + ((light ? 0.42 : 0.30) * op) + ")");
      g.addColorStop(0.5, "rgba(" + RGB + "," + ((light ? 0.20 : 0.14) * op) + ")");
      g.addColorStop(1, "rgba(" + RGB + "," + ((light ? 0.10 : 0.07) * op) + ")");
      ctx.fillStyle = g; roundRect(x - w / 2, yTop, w, yBot - yTop, 2 * scl); ctx.fill();
      ctx.restore();

      ctx.fillStyle = "rgba(" + RGB + "," + (L.hi * f) + ")";
      ctx.fillRect(x - w / 2, yTop, w, Math.max(1, 1.2 * scl));
      ctx.fillStyle = "rgba(" + RGB + "," + (0.5 * L.hi * f) + ")";
      ctx.fillRect(x - w / 2, yTop, Math.max(1, scl), yBot - yTop);

      if (L.lbl && hash(n + 9) > 0.78) {
        // direction + value follow THIS candle's own price level over time:
        // an up-candle's level is its body top, a down-candle's is its body
        // bottom (its leading edge). Rising level = green ▲, falling = red ▼.
        const a = cfg.current.amp, dt2 = 0.12;
        const lvlAt = (tt) => {
          const bY = H * L.base + wave(x + off * 0.85, tt, a) * 0.5;
          let fr = 0.5 + 0.5 * (0.62 * Math.sin(n * 0.5 + L.par * 3 + tt * 0.5) + 0.38 * (r1 * 2 - 1))
            + 0.09 * Math.sin(tt * 1.7 + n * 1.3);
          fr = Math.max(0.05, Math.min(1, fr));
          let hh = (L.hMin + (L.hMax - L.hMin) * fr) * scl; if (hh < 10 * scl) hh = 10 * scl;
          return up ? (bY - hh) : (bY + hh);
        };
        const priceY = up ? yTop : yBot;
        const vel = lvlAt(time - dt2) - priceY;   // >0 ⇒ level moved up (y decreased)
        const rising = vel >= 0;
        const pct = Math.min(9.99, Math.abs(vel) / scl * 0.3 + 0.04);
        // market semantics — the one place colour is allowed (real gain/loss).
        // brand green/red on light, brightened on dark.
        const col = rising
          ? (light ? "21,128,61" : "82,214,138")
          : (light ? "194,54,43" : "242,108,96");
        const aL = (rising ? 0.92 : 0.88) * f;
        const fp = 11 * scl, tx = x + w * 0.7, ty = yTop - 10 * scl;
        // classic exchange arrow ▲ / ▼
        const aw = fp * 0.5, ah = fp * 0.6;
        ctx.fillStyle = "rgba(" + col + "," + aL + ")";
        ctx.beginPath();
        if (rising) { ctx.moveTo(tx, ty - ah); ctx.lineTo(tx + aw, ty); ctx.lineTo(tx - aw, ty); }
        else { ctx.moveTo(tx, ty); ctx.lineTo(tx + aw, ty - ah); ctx.lineTo(tx - aw, ty - ah); }
        ctx.closePath(); ctx.fill();
        // ticking percent after the arrow
        ctx.font = '500 ' + fp + 'px "JetBrains Mono", monospace';
        ctx.fillText((rising ? "+" : "\u2212") + pct.toFixed(2) + "%", tx + aw + 4 * scl, ty);
      }
    }

    function drawWaveLine(t, off, f, a, p) {
      const light = cfg.current.theme === "light";
      const RGB = light ? "14,14,12" : "255,255,255";
      ctx.beginPath();
      for (let x = -24; x <= W + 24; x += 6) {
        const wx = x + off * 0.85;
        const y = H * p.yMul + wave(wx, t + p.phase, p.amp * a);
        if (x <= -24) ctx.moveTo(x, y); else ctx.lineTo(x, y);
      }
      ctx.save();
      ctx.shadowColor = "rgba(" + RGB + "," + (0.5 * p.glow * f) + ")"; ctx.shadowBlur = 22 * scl;
      ctx.lineWidth = 7 * scl; ctx.strokeStyle = "rgba(" + RGB + "," + (0.05 * f) + ")"; ctx.stroke();
      ctx.restore();
      ctx.lineWidth = p.core * scl; ctx.strokeStyle = "rgba(" + RGB + "," + ((light ? 0.6 : 0.72) * f) + ")";
      ctx.lineCap = "round"; ctx.stroke();
    }

    let last = performance.now(), off = 0, time = 0, raf = 0, visible = true;
    const io = new IntersectionObserver((es) => { visible = es[0].isIntersecting; }, { threshold: 0 });
    io.observe(host);

    function frame(now) {
      raf = requestAnimationFrame(frame);
      const dt = Math.min(0.05, (now - last) / 1000); last = now;
      if (!visible) return;
      const c = cfg.current;
      if (!reduce) { off += 42 * scl * c.speed * dt; time += dt * c.speed; }

      ctx.clearRect(0, 0, W, H);
      const dens = c.density === "dense" ? 0.72 : c.density === "sparse" ? 1.4 : 1.0;

      for (const L of layers) {
        const S = L.S * scl * dens, scroll = off * L.par;
        const start = Math.floor(scroll / S) - 2, end = start + Math.ceil(W / S) + 4;
        for (let n = start; n <= end; n++) {
          const x = n * S - scroll;
          const baseY = H * L.base + wave(x + off * 0.85, time, c.amp) * 0.5;
          drawCandle(L, x, baseY, n, c.frost, time);
        }
      }
      drawWaveLine(time, off, c.frost, c.amp, { amp: 1.00, phase: 0.0, yMul: 0.50, core: 1.9, glow: 1.0 });
      drawWaveLine(time, off, c.frost, c.amp, { amp: 0.72, phase: 2.4, yMul: 0.55, core: 1.4, glow: 0.7 });
      drawWaveLine(time, off, c.frost, c.amp, { amp: 0.46, phase: 4.7, yMul: 0.47, core: 1.0, glow: 0.45 });

      ctx.globalAlpha = 0.035 * c.frost; ctx.fillStyle = grainPat; ctx.fillRect(0, 0, W, H); ctx.globalAlpha = 1;
    }
    raf = requestAnimationFrame(frame);

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

  return <canvas ref={ref} style={{ position: "absolute", inset: 0, display: "block", ...style }} />;
}

Object.assign(window, { ChartField });
