// FlickeringWaveGrid — full-hero canvas background that paints a flickering pixel grid
// shaped like an audio-recording equalizer. Each column's envelope follows a pre-baked
// conversation waveform (agent segments in terracotta, customer in navy, silence dim cream).
// Cells flicker with per-cell probability. Respects prefers-reduced-motion.

const FlickeringWaveGrid = ({
  squareSize = 3,
  gridGap = 3,
  flickerChance = 0.12,
  // Palette — pulls from C if available, falls back to hex
  agentColor,
  customerColor,
  bgColor,
  // Baseline opacity of a "lit" cell — envelope scales from 0 to this
  maxOpacity = 0.85,
  // Conversation pattern: array of segments { who: 'agent'|'customer'|'silence', dur: seconds }
  conversation,
  // Loop duration (seconds). Envelope time wraps.
  loopSeconds = 14,
  style,
}) => {
  const canvasRef = React.useRef(null);
  const rafRef = React.useRef(0);
  const stateRef = React.useRef({ cells: null, cols: 0, rows: 0, w: 0, h: 0 });

  // Defaults pulled from palette if available
  const aC = agentColor || (typeof C !== 'undefined' ? C.terracotta : '#E85D3A');
  const cC = customerColor || (typeof C !== 'undefined' ? C.navy : '#1E3A5F');
  const bC = bgColor || (typeof C !== 'undefined' ? C.inkMute : '#9A9385');

  const convo = conversation || [
    // ~14s conversation loop: Agent intro → Customer → Agent upsell → Customer yes
    { who: 'agent',    dur: 2.2 },
    { who: 'silence',  dur: 0.25 },
    { who: 'customer', dur: 1.6 },
    { who: 'silence',  dur: 0.35 },
    { who: 'agent',    dur: 3.1 },
    { who: 'silence',  dur: 0.3 },
    { who: 'customer', dur: 0.9 },
    { who: 'silence',  dur: 0.4 },
    { who: 'agent',    dur: 2.0 },
    { who: 'silence',  dur: 0.3 },
    { who: 'customer', dur: 1.1 },
    { who: 'silence',  dur: 0.5 },
  ];

  // Pre-bake per-column envelope: amplitude 0–1 and tint (agent/customer/silence)
  const bakeEnvelope = (cols) => {
    const totalDur = convo.reduce((s, seg) => s + seg.dur, 0);
    const scale = loopSeconds / totalDur; // normalize to loopSeconds
    const env = new Float32Array(cols);
    const tint = new Uint8Array(cols); // 0 silence, 1 agent, 2 customer
    // Build per-column by mapping column idx → time in loop
    for (let i = 0; i < cols; i++) {
      const t = (i / cols) * loopSeconds;
      // Find segment
      let acc = 0;
      let seg = convo[0];
      let localT = 0, segLen = seg.dur * scale;
      for (let j = 0; j < convo.length; j++) {
        const s = convo[j];
        const len = s.dur * scale;
        if (t <= acc + len) { seg = s; localT = t - acc; segLen = len; break; }
        acc += len;
      }
      // Envelope: silence = tiny baseline; voiced = rich waveform with micro-variation
      if (seg.who === 'silence') {
        env[i] = 0.12 + 0.08 * Math.random();
        tint[i] = 0;
      } else {
        // Syllable rhythm — sum of a few sinusoids to mimic speech envelope
        const p = localT / Math.max(0.0001, segLen); // 0..1 within segment
        // Attack-sustain-decay window
        const adsr = Math.sin(Math.PI * p) ** 0.35;
        // Syllable beating at ~5Hz
        const syl = 0.55 + 0.45 * Math.abs(Math.sin(localT * 8.5 + i * 0.03));
        // Fine high-freq grit
        const grit = 0.75 + 0.25 * Math.sin(localT * 38 + i * 0.9);
        let amp = adsr * syl * grit;
        // Random spikes (consonants)
        if (Math.random() < 0.04) amp = Math.min(1, amp + 0.3 * Math.random());
        env[i] = Math.max(0.18, Math.min(1, amp));
        tint[i] = seg.who === 'agent' ? 1 : 2;
      }
    }
    return { env, tint };
  };

  React.useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext('2d', { alpha: true });
    const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

    const parseColor = (c) => {
      // Accept "#rrggbb"
      const m = c.replace('#','');
      const full = m.length === 3 ? m.split('').map(x => x+x).join('') : m;
      return {
        r: parseInt(full.slice(0, 2), 16),
        g: parseInt(full.slice(2, 4), 16),
        b: parseInt(full.slice(4, 6), 16),
      };
    };
    const agentRGB = parseColor(aC);
    const custRGB = parseColor(cC);
    const bgRGB = parseColor(bC);

    const resize = () => {
      const parent = canvas.parentElement;
      const dpr = Math.min(window.devicePixelRatio || 1, 2);
      const w = parent.clientWidth;
      const h = parent.clientHeight;
      canvas.style.width = w + 'px';
      canvas.style.height = h + 'px';
      canvas.width = Math.floor(w * dpr);
      canvas.height = Math.floor(h * dpr);
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);

      const step = squareSize + gridGap;
      const cols = Math.floor(w / step);
      const rows = Math.floor(h / step);
      const { env, tint } = bakeEnvelope(cols);
      // Per-cell phase + base opacity noise so flicker feels organic
      const phase = new Float32Array(cols * rows);
      const baseNoise = new Float32Array(cols * rows);
      for (let i = 0; i < phase.length; i++) {
        phase[i] = Math.random() * Math.PI * 2;
        baseNoise[i] = 0.65 + 0.35 * Math.random();
      }
      stateRef.current = { cols, rows, w, h, step, env, tint, phase, baseNoise };
    };

    resize();
    const ro = new ResizeObserver(resize);
    ro.observe(canvas.parentElement);

    const start = performance.now();
    const draw = (now) => {
      const t = (now - start) / 1000;
      const { cols, rows, step, env, tint, phase, baseNoise } = stateRef.current;
      if (!cols) { rafRef.current = requestAnimationFrame(draw); return; }

      ctx.clearRect(0, 0, canvas.width, canvas.height);

      // Stationary tape — no horizontal shift. Envelope stays fixed; cells flicker in place
      // and amplitude breathes over time so bars rise/fall like live voice.

      for (let x = 0; x < cols; x++) {
        const srcX = x % cols;
        const baseAmp = env[srcX];
        const tintId = tint[srcX];

        // Time-based breathing per column: each column has its own phase so
        // different bars pulse independently — like voice activity flickering to life
        const colPhase = srcX * 0.17;
        const breathe = prefersReduced
          ? 1
          : (0.55 + 0.45 * Math.abs(Math.sin(t * 5.2 + colPhase))) * (0.85 + 0.15 * Math.sin(t * 1.3 + colPhase * 0.3));
        const amp = Math.min(1, baseAmp * breathe);

        // Vertical amplitude: center-ish bias, height proportional to amp
        // Compute rows lit: the column paints a vertical bar centered mid-height
        const midRow = rows / 2;
        const halfBar = amp * (rows / 2) * 1.02; // 1.02 to allow edges to reach

        let col;
        if (tintId === 1) col = agentRGB;
        else if (tintId === 2) col = custRGB;
        else col = bgRGB;

        for (let y = 0; y < rows; y++) {
          const dist = Math.abs(y - midRow);
          const barEdge = halfBar;
          // Inside bar → lit by amp; outside bar → faint background dots
          let cellAmp;
          if (dist <= barEdge) {
            // Smooth fall-off at edges
            const edgeSoft = 1 - Math.max(0, (dist - (barEdge - 1.2)) / 1.2);
            cellAmp = amp * Math.max(0, edgeSoft);
          } else {
            // faint ambient outside bar
            cellAmp = 0.04 + 0.06 * Math.random() * (1 - Math.min(1, dist / rows));
          }

          const idx = y * cols + x;
          // Flicker
          const flicker = prefersReduced
            ? 1
            : (0.6 + 0.4 * Math.sin(t * 6 + phase[idx] + x * 0.02)) * (Math.random() < flickerChance ? (0.4 + 0.6 * Math.random()) : 1);
          const noise = baseNoise[idx];
          const a = Math.max(0, Math.min(maxOpacity, cellAmp * flicker * noise * maxOpacity));
          if (a < 0.02) continue;

          ctx.fillStyle = `rgba(${col.r},${col.g},${col.b},${a.toFixed(3)})`;
          ctx.fillRect(x * step, y * step, squareSize, squareSize);
        }
      }

      rafRef.current = requestAnimationFrame(draw);
    };
    rafRef.current = requestAnimationFrame(draw);

    return () => {
      cancelAnimationFrame(rafRef.current);
      ro.disconnect();
    };
  }, [squareSize, gridGap, flickerChance, aC, cC, bC, maxOpacity, loopSeconds]);

  return (
    <canvas
      ref={canvasRef}
      style={{
        position: 'absolute',
        inset: 0,
        width: '100%',
        height: '100%',
        pointerEvents: 'none',
        ...style,
      }}
    />
  );
};

window.FlickeringWaveGrid = FlickeringWaveGrid;
