// Hero signal canvas — neuro-symbolic data flow
// Renders multi-channel signal traces + node graph reasoning

window.HeroCanvas = function HeroCanvas({ accentHue }) {
  const canvasRef = React.useRef(null);
  const rafRef = React.useRef(null);
  const stateRef = React.useRef({ t: 0, hist: [], nodes: null, pulses: [] });

  React.useEffect(() => {
    window.__heroEffectRan = (window.__heroEffectRan || 0) + 1;
    const canvas = canvasRef.current;
    if (!canvas) { window.__heroNoCanvas = true; return; }
    const ctx = canvas.getContext('2d');
    let dpr = Math.min(window.devicePixelRatio || 1, 2);

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

    const accent = `oklch(0.55 0.18 ${accentHue})`;
    const accentDim = `oklch(0.55 0.18 ${accentHue} / 0.18)`;
    const accentFill = `oklch(0.55 0.18 ${accentHue} / 0.08)`;
    const lime = `oklch(0.55 0.18 145)`;
    const ink3 = `oklch(0.55 0.014 250)`;
    const grid = `oklch(0.78 0.008 250 / 0.5)`;

    // 8 graph nodes for symbolic side
    const initNodes = (w, h) => {
      const cx = w * 0.78;
      const cy = h * 0.5;
      const r = Math.min(w, h) * 0.22;
      const nodes = [];
      for (let i = 0; i < 7; i++) {
        const a = (i / 7) * Math.PI * 2 - Math.PI / 2;
        nodes.push({
          x: cx + Math.cos(a) * r * (0.7 + (i % 3) * 0.15),
          y: cy + Math.sin(a) * r * (0.7 + (i % 3) * 0.15),
          act: 0,
          phase: Math.random() * Math.PI * 2,
        });
      }
      // central hub
      nodes.push({ x: cx, y: cy, act: 0, phase: 0, hub: true });
      return nodes;
    };

    const draw = () => {
      const w = canvas.clientWidth;
      const h = canvas.clientHeight;
      if (!w || !h) return;
      const s = stateRef.current;
      if (!s.nodes) s.nodes = initNodes(w, h);
      s.t += 0.016;

      ctx.clearRect(0, 0, w, h);

      // grid
      ctx.strokeStyle = grid;
      ctx.lineWidth = 1;
      const step = 24;
      ctx.beginPath();
      for (let x = 0; x < w; x += step) { ctx.moveTo(x, 0); ctx.lineTo(x, h); }
      for (let y = 0; y < h; y += step) { ctx.moveTo(0, y); ctx.lineTo(w, y); }
      ctx.stroke();

      // === LEFT: signal channels ===
      const leftPad = 24;
      const rightSplit = w * 0.58;
      const channels = 4;
      const chH = (h - 40) / channels;

      // generate samples
      if (!s.hist.length) {
        for (let i = 0; i < 200; i++) s.hist.push([0, 0, 0, 0]);
      }
      const t = s.t;
      const sample = [
        Math.sin(t * 2.1) * 0.5 + Math.sin(t * 5.3) * 0.25 + (Math.random() - 0.5) * 0.1,
        Math.sin(t * 1.3 + 1) * 0.4 + Math.sin(t * 7.1 + 2) * 0.3 + (Math.random() - 0.5) * 0.15,
        Math.sin(t * 0.9 + 2) * 0.6 + Math.sin(t * 3.7 + 1) * 0.15 + (Math.random() - 0.5) * 0.08,
        (Math.sin(t * 0.7) > 0.6 ? 0.7 : -0.3) + (Math.random() - 0.5) * 0.05, // pulse channel
      ];
      s.hist.push(sample);
      if (s.hist.length > 200) s.hist.shift();

      for (let c = 0; c < channels; c++) {
        const baseY = 28 + chH * c + chH / 2;
        // axis
        ctx.strokeStyle = `oklch(0.27 0.018 250 / 0.5)`;
        ctx.lineWidth = 1;
        ctx.beginPath();
        ctx.moveTo(leftPad, baseY);
        ctx.lineTo(rightSplit - 16, baseY);
        ctx.stroke();

        // channel label
        ctx.fillStyle = ink3;
        ctx.font = '10px JetBrains Mono, monospace';
        ctx.textAlign = 'left';
        const labels = ['CH.0  vibration', 'CH.1  thermal', 'CH.2  acoustic', 'CH.3  trigger'];
        ctx.fillText(labels[c], leftPad, 28 + chH * c + 12);

        // trace
        ctx.strokeStyle = c === 3 ? lime : accent;
        ctx.lineWidth = c === 3 ? 1.4 : 1.2;
        ctx.beginPath();
        const pts = s.hist.length;
        for (let i = 0; i < pts; i++) {
          const x = leftPad + ((rightSplit - 16 - leftPad) * i) / (pts - 1);
          const y = baseY - s.hist[i][c] * chH * 0.36;
          if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
        }
        ctx.stroke();

        // current point dot
        const lastY = baseY - sample[c] * chH * 0.36;
        ctx.fillStyle = c === 3 ? lime : accent;
        ctx.beginPath();
        ctx.arc(rightSplit - 16, lastY, 2.5, 0, Math.PI * 2);
        ctx.fill();
      }

      // divider
      ctx.strokeStyle = `oklch(0.27 0.018 250 / 0.7)`;
      ctx.setLineDash([2, 4]);
      ctx.beginPath();
      ctx.moveTo(rightSplit, 16);
      ctx.lineTo(rightSplit, h - 16);
      ctx.stroke();
      ctx.setLineDash([]);

      // bridge label
      ctx.fillStyle = ink3;
      ctx.font = '9px JetBrains Mono, monospace';
      ctx.textAlign = 'center';
      ctx.fillText('NEURO  →  SYMBOLIC', (rightSplit + w) / 2, 16);

      // === RIGHT: graph reasoning ===
      const nodes = s.nodes;

      // pulses on trigger
      if (sample[3] > 0.5 && Math.random() < 0.06) {
        const target = Math.floor(Math.random() * (nodes.length - 1));
        s.pulses.push({ from: 'in', to: target, p: 0 });
      }
      if (Math.random() < 0.04) {
        const a = Math.floor(Math.random() * (nodes.length - 1));
        let b = Math.floor(Math.random() * nodes.length);
        if (a === b) b = (b + 1) % nodes.length;
        s.pulses.push({ from: a, to: b, p: 0 });
      }

      // node activations gentle pulse
      for (let n of nodes) n.act *= 0.94;

      // edges between adjacent + to hub
      ctx.strokeStyle = accentDim;
      ctx.lineWidth = 1;
      const hub = nodes[nodes.length - 1];
      for (let i = 0; i < nodes.length - 1; i++) {
        const n = nodes[i];
        const m = nodes[(i + 1) % (nodes.length - 1)];
        ctx.beginPath();
        ctx.moveTo(n.x, n.y);
        ctx.lineTo(m.x, m.y);
        ctx.stroke();
        ctx.beginPath();
        ctx.moveTo(n.x, n.y);
        ctx.lineTo(hub.x, hub.y);
        ctx.stroke();
      }

      // pulses
      const inX = rightSplit;
      const inY = h * 0.5;
      s.pulses = s.pulses.filter(p => {
        p.p += 0.04;
        const target = nodes[p.to];
        const sx = p.from === 'in' ? inX : nodes[p.from].x;
        const sy = p.from === 'in' ? inY : nodes[p.from].y;
        const x = sx + (target.x - sx) * p.p;
        const y = sy + (target.y - sy) * p.p;
        ctx.fillStyle = lime;
        ctx.beginPath();
        ctx.arc(x, y, 2.5, 0, Math.PI * 2);
        ctx.fill();
        // glow
        ctx.fillStyle = `oklch(0.55 0.18 145 / 0.25)`;
        ctx.beginPath();
        ctx.arc(x, y, 6, 0, Math.PI * 2);
        ctx.fill();
        if (p.p >= 1) {
          target.act = 1;
          return false;
        }
        return true;
      });

      // nodes
      for (let i = 0; i < nodes.length; i++) {
        const n = nodes[i];
        const idle = 0.5 + 0.5 * Math.sin(t * 1.2 + n.phase);
        const act = Math.max(n.act, idle * 0.25);
        const r = n.hub ? 8 : 5;
        // halo
        ctx.fillStyle = `oklch(0.55 0.18 ${accentHue} / ${0.05 + act * 0.25})`;
        ctx.beginPath();
        ctx.arc(n.x, n.y, r + 8 + act * 4, 0, Math.PI * 2);
        ctx.fill();
        // core
        ctx.fillStyle = n.hub ? lime : accent;
        ctx.beginPath();
        ctx.arc(n.x, n.y, r, 0, Math.PI * 2);
        ctx.fill();
        // ring
        ctx.strokeStyle = n.hub ? lime : accent;
        ctx.lineWidth = 1;
        ctx.beginPath();
        ctx.arc(n.x, n.y, r + 3, 0, Math.PI * 2);
        ctx.stroke();
      }

      // crosshair on input
      ctx.strokeStyle = accent;
      ctx.lineWidth = 1;
      ctx.beginPath();
      ctx.moveTo(inX - 6, inY); ctx.lineTo(inX - 2, inY);
      ctx.moveTo(inX + 2, inY); ctx.lineTo(inX + 6, inY);
      ctx.moveTo(inX, inY - 6); ctx.lineTo(inX, inY - 2);
      ctx.moveTo(inX, inY + 2); ctx.lineTo(inX, inY + 6);
      ctx.stroke();
    };
    const safeDraw = () => {
      try { draw(); } catch (e) { console.error('hero draw err', e); }
    };
    // Use setInterval — RAF can be throttled when iframe is offscreen
    rafRef.current = setInterval(safeDraw, 33);

    return () => {
      clearInterval(rafRef.current);
      ro.disconnect();
    };
  }, [accentHue]);

  return <canvas ref={canvasRef} />;
};

// Small approach diagram canvas — animates per active stage
window.ApproachCanvas = function ApproachCanvas({ stage, accentHue }) {
  const canvasRef = React.useRef(null);
  const rafRef = React.useRef(null);
  const stageRef = React.useRef(stage);

  React.useEffect(() => { stageRef.current = stage; }, [stage]);

  React.useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    let dpr = Math.min(window.devicePixelRatio || 1, 2);
    const resize = () => {
      const rect = canvas.getBoundingClientRect();
      canvas.width = rect.width * dpr;
      canvas.height = rect.height * dpr;
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    };
    resize();
    const ro = new ResizeObserver(resize);
    ro.observe(canvas);

    const accent = `oklch(0.55 0.18 ${accentHue})`;
    const accentDim = `oklch(0.55 0.18 ${accentHue} / 0.2)`;
    const lime = `oklch(0.55 0.18 145)`;
    const ink3 = `oklch(0.55 0.014 250)`;
    const grid = `oklch(0.78 0.008 250 / 0.4)`;

    let t = 0;

    const drawSense = (w, h) => {
      // sensor + waveform forming
      ctx.strokeStyle = accent;
      ctx.lineWidth = 1.4;
      const cy = h / 2;
      ctx.beginPath();
      for (let x = 0; x < w; x++) {
        const phase = x / w * Math.PI * 4 - t * 2;
        const env = Math.min(1, Math.max(0, (t - x / w * 1.2) * 1.5));
        const y = cy + Math.sin(phase) * 60 * env + Math.sin(phase * 3.1) * 20 * env;
        if (x === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
      }
      ctx.stroke();
      // sensor box at left
      ctx.strokeStyle = lime;
      ctx.fillStyle = `oklch(0.55 0.18 145 / 0.1)`;
      ctx.lineWidth = 1.4;
      ctx.beginPath();
      ctx.rect(20, cy - 24, 56, 48);
      ctx.fill();
      ctx.stroke();
      ctx.fillStyle = lime;
      ctx.font = '11px JetBrains Mono, monospace';
      ctx.textAlign = 'center';
      ctx.fillText('SENSOR', 48, cy + 4);
    };

    const drawEncode = (w, h) => {
      // graph nodes forming
      const cx = w / 2, cy = h / 2;
      const r = Math.min(w, h) * 0.3;
      const N = 9;
      const nodes = [];
      for (let i = 0; i < N; i++) {
        const a = (i / N) * Math.PI * 2;
        nodes.push({ x: cx + Math.cos(a) * r, y: cy + Math.sin(a) * r });
      }
      // edges
      ctx.strokeStyle = accentDim;
      ctx.lineWidth = 1;
      for (let i = 0; i < N; i++) {
        for (let j = i + 1; j < N; j++) {
          if ((i + j + Math.floor(t * 0.5)) % 3 === 0) {
            ctx.beginPath();
            ctx.moveTo(nodes[i].x, nodes[i].y);
            ctx.lineTo(nodes[j].x, nodes[j].y);
            ctx.stroke();
          }
        }
      }
      // nodes
      for (let i = 0; i < N; i++) {
        const pulse = 0.5 + 0.5 * Math.sin(t * 2 + i);
        ctx.fillStyle = `oklch(0.55 0.18 ${accentHue} / ${0.1 + pulse * 0.2})`;
        ctx.beginPath();
        ctx.arc(nodes[i].x, nodes[i].y, 14, 0, Math.PI * 2);
        ctx.fill();
        ctx.fillStyle = accent;
        ctx.beginPath();
        ctx.arc(nodes[i].x, nodes[i].y, 5, 0, Math.PI * 2);
        ctx.fill();
      }
    };

    const drawReason = (w, h) => {
      // dual columns: neural blocks left, symbolic tree right, arrows between
      const padX = 40;
      const leftX = padX;
      const rightX = w - padX - 110;
      // neural stack
      for (let i = 0; i < 4; i++) {
        const y = 40 + i * 56;
        ctx.strokeStyle = accent;
        ctx.fillStyle = `oklch(0.55 0.18 ${accentHue} / 0.08)`;
        ctx.lineWidth = 1.2;
        ctx.beginPath(); ctx.rect(leftX, y, 110, 36); ctx.fill(); ctx.stroke();
        // bar fills
        const fill = 0.5 + 0.5 * Math.sin(t * 1.5 + i);
        ctx.fillStyle = accent;
        ctx.fillRect(leftX + 6, y + 12, 98 * fill, 4);
        ctx.fillRect(leftX + 6, y + 22, 98 * (1 - fill * 0.7), 4);
      }
      ctx.fillStyle = ink3;
      ctx.font = '9px JetBrains Mono, monospace';
      ctx.textAlign = 'left';
      ctx.fillText('NEURAL STACK', leftX, 30);

      // symbolic tree
      const tree = [
        { x: rightX + 55, y: 50 },
        { x: rightX + 25, y: 110 },
        { x: rightX + 85, y: 110 },
        { x: rightX + 10, y: 170 },
        { x: rightX + 45, y: 170 },
        { x: rightX + 70, y: 170 },
        { x: rightX + 100, y: 170 },
      ];
      ctx.strokeStyle = lime;
      ctx.lineWidth = 1;
      const edges = [[0,1],[0,2],[1,3],[1,4],[2,5],[2,6]];
      for (const [a, b] of edges) {
        ctx.beginPath();
        ctx.moveTo(tree[a].x, tree[a].y);
        ctx.lineTo(tree[b].x, tree[b].y);
        ctx.stroke();
      }
      for (let i = 0; i < tree.length; i++) {
        ctx.fillStyle = lime;
        ctx.beginPath(); ctx.arc(tree[i].x, tree[i].y, 4, 0, Math.PI * 2); ctx.fill();
      }
      ctx.fillStyle = ink3;
      ctx.fillText('SYMBOLIC', rightX, 30);

      // bridge arrows
      ctx.strokeStyle = `oklch(0.55 0.18 ${accentHue} / 0.6)`;
      ctx.lineWidth = 1;
      ctx.setLineDash([3, 3]);
      const flow = (t * 30) % 14;
      for (let i = 0; i < 4; i++) {
        const y = 60 + i * 56;
        ctx.beginPath();
        ctx.moveTo(leftX + 110, y);
        ctx.lineTo(rightX, y);
        ctx.stroke();
      }
      ctx.setLineDash([]);
    };

    const drawDeploy = (w, h) => {
      // chip outline + shrinking model + meters
      const cx = w / 2, cy = h / 2;
      // chip
      ctx.strokeStyle = accent;
      ctx.lineWidth = 1.4;
      ctx.beginPath();
      ctx.rect(cx - 80, cy - 80, 160, 160);
      ctx.stroke();
      // chip pins
      for (let i = 0; i < 6; i++) {
        const off = -50 + i * 20;
        ctx.beginPath();
        ctx.moveTo(cx + off, cy - 80); ctx.lineTo(cx + off, cy - 88);
        ctx.moveTo(cx + off, cy + 80); ctx.lineTo(cx + off, cy + 88);
        ctx.moveTo(cx - 80, cy + off); ctx.lineTo(cx - 88, cy + off);
        ctx.moveTo(cx + 80, cy + off); ctx.lineTo(cx + 88, cy + off);
        ctx.stroke();
      }
      // shrinking model viz inside
      const breathe = 0.7 + 0.3 * (0.5 + 0.5 * Math.sin(t * 1.4));
      ctx.fillStyle = `oklch(0.55 0.18 ${accentHue} / 0.15)`;
      ctx.beginPath();
      ctx.arc(cx, cy, 50 * breathe, 0, Math.PI * 2);
      ctx.fill();
      ctx.fillStyle = accent;
      ctx.beginPath();
      ctx.arc(cx, cy, 18 * breathe, 0, Math.PI * 2);
      ctx.fill();
      // chip label
      ctx.fillStyle = ink3;
      ctx.font = '10px JetBrains Mono, monospace';
      ctx.textAlign = 'center';
      ctx.fillText('ESP32-S3', cx, cy - 90);

      // meters
      ctx.fillStyle = ink3;
      ctx.font = '9px JetBrains Mono, monospace';
      ctx.textAlign = 'left';
      const meterX = 24;
      const labels = ['MEM 184/256 KB', 'LAT 31 ms', 'PWR 64 mW'];
      const fills = [0.72, 0.4, 0.5];
      for (let i = 0; i < 3; i++) {
        const y = h - 60 + i * 16;
        ctx.fillStyle = ink3;
        ctx.fillText(labels[i], meterX, y);
        ctx.strokeStyle = grid; ctx.strokeRect(meterX + 110, y - 7, 80, 6);
        ctx.fillStyle = i === 0 ? lime : accent;
        ctx.fillRect(meterX + 110, y - 7, 80 * fills[i], 6);
      }
    };

    const draw = () => {
      const w = canvas.clientWidth;
      const h = canvas.clientHeight;
      if (!w || !h) { rafRef.current = requestAnimationFrame(draw); return; }
      t += 0.016;
      ctx.clearRect(0, 0, w, h);

      // grid bg
      ctx.strokeStyle = grid;
      ctx.lineWidth = 1;
      const step = 24;
      ctx.beginPath();
      for (let x = 0; x < w; x += step) { ctx.moveTo(x, 0); ctx.lineTo(x, h); }
      for (let y = 0; y < h; y += step) { ctx.moveTo(0, y); ctx.lineTo(w, y); }
      ctx.stroke();

      const s = stageRef.current;
      if (s === 0) drawSense(w, h);
      else if (s === 1) drawEncode(w, h);
      else if (s === 2) drawReason(w, h);
      else drawDeploy(w, h);
    };
    const safeDraw = () => { try { draw(); } catch (e) { console.error('approach err', e); } };
    rafRef.current = setInterval(safeDraw, 33);

    return () => {
      clearInterval(rafRef.current);
      ro.disconnect();
    };
  }, [accentHue]);

  return <canvas ref={canvasRef} />;
};

// Case thumb mini canvas
window.CaseThumb = function CaseThumb({ kind, accentHue }) {
  const canvasRef = React.useRef(null);

  React.useEffect(() => {
    const c = canvasRef.current;
    if (!c) return;
    const ctx = c.getContext('2d');
    const dpr = Math.min(window.devicePixelRatio || 1, 2);
    const resize = () => {
      const r = c.getBoundingClientRect();
      c.width = r.width * dpr;
      c.height = r.height * dpr;
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    };
    resize();
    const ro = new ResizeObserver(resize);
    ro.observe(c);

    const accent = `oklch(0.55 0.18 ${accentHue})`;
    const lime = `oklch(0.55 0.18 145)`;
    const grid = `oklch(0.78 0.008 250 / 0.5)`;

    let raf;
    let t = 0;
    const draw = () => {
      const w = c.clientWidth, h = c.clientHeight;
      if (!w || !h) { raf = requestAnimationFrame(draw); return; }
      t += 0.016;
      ctx.clearRect(0, 0, w, h);
      ctx.strokeStyle = grid;
      ctx.lineWidth = 1;
      ctx.beginPath();
      for (let x = 0; x < w; x += 16) { ctx.moveTo(x, 0); ctx.lineTo(x, h); }
      for (let y = 0; y < h; y += 16) { ctx.moveTo(0, y); ctx.lineTo(w, y); }
      ctx.stroke();

      if (kind === 'twin') {
        // multi-trace
        for (let ch = 0; ch < 3; ch++) {
          ctx.strokeStyle = ch === 1 ? lime : accent;
          ctx.lineWidth = 1.2;
          ctx.beginPath();
          for (let x = 0; x < w; x++) {
            const y = h / 2 + Math.sin(x * 0.04 + t * 1.5 + ch) * 12 - 24 + ch * 24;
            if (x === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
          }
          ctx.stroke();
        }
      } else if (kind === 'legal') {
        // text-line bars
        ctx.fillStyle = accent;
        for (let i = 0; i < 6; i++) {
          const y = 16 + i * 16;
          ctx.fillRect(12, y, 80 + Math.sin(t + i) * 30, 2);
        }
        ctx.fillStyle = lime;
        ctx.fillRect(12, 16 + 3 * 16 + 6, 40, 4);
      } else if (kind === 'edge') {
        // chip + sound wave
        ctx.strokeStyle = accent;
        ctx.lineWidth = 1.2;
        ctx.beginPath();
        for (let x = 0; x < w; x++) {
          const env = Math.exp(-Math.pow((x / w - 0.5) * 4, 2));
          const y = h / 2 + Math.sin(x * 0.6 + t * 4) * 24 * env;
          if (x === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
        }
        ctx.stroke();
        ctx.strokeStyle = lime;
        ctx.strokeRect(w / 2 - 14, h / 2 - 14, 28, 28);
      } else if (kind === 'bio') {
        // grid of cells with one highlighted
        for (let i = 0; i < 6; i++) {
          for (let j = 0; j < 4; j++) {
            const x = 12 + i * 18;
            const y = 12 + j * 18;
            const isHot = (i === 3 && j === 1);
            ctx.strokeStyle = isHot ? lime : accent;
            ctx.fillStyle = isHot ? `oklch(0.55 0.18 145 / ${0.3 + Math.sin(t * 2) * 0.1})` : 'transparent';
            ctx.beginPath();
            ctx.rect(x, y, 12, 12);
            ctx.fill();
            ctx.stroke();
          }
        }
      }
    };
    const safeDraw = () => { try { draw(); } catch (e) { console.error('thumb err', e); } };
    raf = setInterval(safeDraw, 50);
    return () => { clearInterval(raf); ro.disconnect(); };
  }, [kind, accentHue]);

  return <canvas ref={canvasRef} />;
};
