// BackgroundGrid.jsx — Dotty grid with hairlines + subtle "information" wavefronts.

function BackgroundGrid({ density = 28 }) {
  const ref = React.useRef(null);

  React.useEffect(() => {
    const canvas = ref.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");

    let raf, start = performance.now();
    let w = 0, h = 0, cols = 0, rows = 0, points = [];

    // Tunables — kept gentle on purpose
    const NUM_FRONTS = 3;
    const FRONT_WIDTH = 60;
    const FRONT_SPEED = 0.11;
    const TRAIL_DECAY = 2.0;
    const BASE_DOT_ALPHA = 0.10;
    const LIT_DOT_ALPHA = 0.55;
    const LINE_BASE_ALPHA = 0.035;
    const LINE_LIT_ALPHA = 0.12;

    let fronts = [];
    const spawnFront = (now) => {
      const angle = Math.random() * Math.PI * 2;
      const nx = Math.cos(angle), ny = Math.sin(angle);
      const cx = w / 2, cy = h / 2;
      const diag = Math.sqrt(w * w + h * h) / 2 + 80;
      const x0 = cx - nx * diag;
      const y0 = cy - ny * diag;
      return {
        x0, y0, nx, ny,
        born: now,
        life: (diag * 2) / (FRONT_SPEED * Math.max(w, h)),
      };
    };

    const resize = () => {
      const dpr = Math.min(window.devicePixelRatio || 1, 2);
      const rect = canvas.getBoundingClientRect();
      w = rect.width; h = rect.height;
      canvas.width = w * dpr;
      canvas.height = h * dpr;
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);

      cols = Math.ceil(w / density) + 1;
      rows = Math.ceil(h / density) + 1;
      points = [];
      for (let y = 0; y < rows; y++) {
        for (let x = 0; x < cols; x++) {
          points.push({ x: x * density, y: y * density, lastLit: -10, litStrength: 0 });
        }
      }
      fronts = [];
      for (let i = 0; i < NUM_FRONTS; i++) {
        const f = spawnFront(performance.now() / 1000);
        f.born -= Math.random() * f.life;
        fronts.push(f);
      }
    };

    const draw = (now) => {
      const t = (now - start) / 1000;
      ctx.clearRect(0, 0, w, h);

      for (let i = 0; i < fronts.length; i++) {
        if (t - fronts[i].born > fronts[i].life) fronts[i] = spawnFront(t);
      }

      const frontPositions = fronts.map(f => {
        const traveled = (t - f.born) * FRONT_SPEED * Math.max(w, h);
        return { f, traveled };
      });

      // Update lit state per dot
      for (let i = 0; i < points.length; i++) {
        const p = points[i];
        let minAbs = Infinity, signedAt = 0;
        for (let k = 0; k < frontPositions.length; k++) {
          const fp = frontPositions[k];
          const dx = p.x - fp.f.x0, dy = p.y - fp.f.y0;
          const proj = dx * fp.f.nx + dy * fp.f.ny;
          const d = proj - fp.traveled;
          const ad = Math.abs(d);
          if (ad < minAbs) { minAbs = ad; signedAt = d; }
        }
        if (minAbs < FRONT_WIDTH && signedAt <= 0) {
          const peak = 1 - (minAbs / FRONT_WIDTH);
          if (peak > p.litStrength) {
            p.litStrength = peak;
            p.lastLit = t;
          }
        }
      }

      // ---- grid lines (hairlines between dots) ----
      ctx.lineWidth = 1;
      // Horizontals
      for (let y = 0; y < rows; y++) {
        for (let x = 0; x < cols - 1; x++) {
          const a = points[y * cols + x];
          const b = points[y * cols + x + 1];
          const fadeA = Math.max(0, 1 - (t - a.lastLit) / TRAIL_DECAY);
          const fadeB = Math.max(0, 1 - (t - b.lastLit) / TRAIL_DECAY);
          const litA = a.litStrength * fadeA;
          const litB = b.litStrength * fadeB;
          const lit = Math.max(litA, litB);
          const alpha = LINE_BASE_ALPHA + (LINE_LIT_ALPHA - LINE_BASE_ALPHA) * lit;
          if (lit > 0.04) {
            ctx.strokeStyle = `rgba(255, 89, 0, ${(alpha * 0.85).toFixed(3)})`;
          } else {
            ctx.strokeStyle = `rgba(170, 170, 170, ${LINE_BASE_ALPHA.toFixed(3)})`;
          }
          ctx.beginPath();
          ctx.moveTo(a.x, a.y);
          ctx.lineTo(b.x, b.y);
          ctx.stroke();
        }
      }
      // Verticals
      for (let x = 0; x < cols; x++) {
        for (let y = 0; y < rows - 1; y++) {
          const a = points[y * cols + x];
          const b = points[(y + 1) * cols + x];
          const fadeA = Math.max(0, 1 - (t - a.lastLit) / TRAIL_DECAY);
          const fadeB = Math.max(0, 1 - (t - b.lastLit) / TRAIL_DECAY);
          const litA = a.litStrength * fadeA;
          const litB = b.litStrength * fadeB;
          const lit = Math.max(litA, litB);
          const alpha = LINE_BASE_ALPHA + (LINE_LIT_ALPHA - LINE_BASE_ALPHA) * lit;
          if (lit > 0.04) {
            ctx.strokeStyle = `rgba(255, 89, 0, ${(alpha * 0.85).toFixed(3)})`;
          } else {
            ctx.strokeStyle = `rgba(170, 170, 170, ${LINE_BASE_ALPHA.toFixed(3)})`;
          }
          ctx.beginPath();
          ctx.moveTo(a.x, a.y);
          ctx.lineTo(b.x, b.y);
          ctx.stroke();
        }
      }

      // ---- dots ----
      for (let i = 0; i < points.length; i++) {
        const p = points[i];
        const sinceLit = t - p.lastLit;
        const fade = Math.max(0, 1 - sinceLit / TRAIL_DECAY);
        const lit = p.litStrength * fade;

        if (lit > 0.06) {
          const alpha = BASE_DOT_ALPHA + (LIT_DOT_ALPHA - BASE_DOT_ALPHA) * lit;
          ctx.fillStyle = `rgba(255, 89, 0, ${alpha.toFixed(3)})`;
          ctx.beginPath();
          ctx.arc(p.x, p.y, 0.95 + lit * 0.5, 0, Math.PI * 2);
          ctx.fill();
        } else {
          ctx.fillStyle = `rgba(170, 170, 170, ${BASE_DOT_ALPHA.toFixed(3)})`;
          ctx.beginPath();
          ctx.arc(p.x, p.y, 0.85, 0, Math.PI * 2);
          ctx.fill();
        }
      }

      raf = requestAnimationFrame(draw);
    };

    resize();
    raf = requestAnimationFrame(draw);
    // Window resize only — avoids the (harmless but noisy) ResizeObserver
    // loop notifications that fire when the observer's callback itself
    // changes layout (e.g. canvas size).
    window.addEventListener('resize', resize);
    return () => { cancelAnimationFrame(raf); window.removeEventListener('resize', resize); };
  }, [density]);

  return <canvas ref={ref} className="bg-grid" aria-hidden="true" />;
}

window.BackgroundGrid = BackgroundGrid;
