// Living network background — brand-violet bloom over #06030f.
// Ported from the BloomNetwork wallpaper: k-nearest graph, BFS wavefront pulses.
// Full-viewport, mouse-reactive (via window), ambient auto-pulses, pointer-events:none.
function BloomBackground() {
  const canvasRef = React.useRef(null);
  React.useEffect(() => {
    const canvas = canvasRef.current;
    const ctx = canvas.getContext("2d");
    let width = 0, height = 0, dpr = 1;
    let nodes = [], edges = [], adj = [], depthCache = new Map();
    const mouse = { x: -1e4, y: -1e4 };
    let nearestIdx = -1;
    const pulses = [];
    const reduce = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;

    function build() {
      width = window.innerWidth;
      height = window.innerHeight;
      dpr = Math.min(window.devicePixelRatio || 1, 2);
      canvas.width = width * dpr;
      canvas.height = height * dpr;
      canvas.style.width = width + "px";
      canvas.style.height = height + "px";
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
      const N = Math.max(42, Math.min(96, Math.round((width * height) / 20000)));
      const minD = Math.max(58, Math.min(96, Math.sqrt((width * height) / N) * 0.82));
      nodes = []; let tries = 0;
      while (nodes.length < N && tries < 6000) {
        tries++;
        const cand = { x: Math.random() * width, y: Math.random() * height };
        if (nodes.every((n) => Math.hypot(n.x - cand.x, n.y - cand.y) > minD))
          nodes.push({ ...cand, ox: cand.x, oy: cand.y });
      }
      edges = []; adj = nodes.map(() => []);
      nodes.forEach((a, i) => {
        nodes.map((b, j) => ({ j, d: Math.hypot(a.x - b.x, a.y - b.y) }))
          .filter((o) => o.j !== i).sort((x, y) => x.d - y.d).slice(0, 3)
          .forEach(({ j }) => { if (!adj[i].includes(j)) { adj[i].push(j); adj[j].push(i); edges.push([i, j]); } });
      });
      depthCache = new Map();
    }

    const getDepths = (src) => {
      if (depthCache.has(src)) return depthCache.get(src);
      const depth = new Array(nodes.length).fill(-1);
      depth[src] = 0; const q = [src];
      while (q.length) { const v = q.shift(); for (const u of adj[v]) if (depth[u] === -1) { depth[u] = depth[v] + 1; q.push(u); } }
      depthCache.set(src, depth); return depth;
    };
    const ignite = (idx, intensity = 1) => { if (idx >= 0) pulses.push({ source: idx, t: 0, intensity }); };

    const onMove = (e) => {
      mouse.x = e.clientX; mouse.y = e.clientY;
      let best = -1, bd = Infinity;
      nodes.forEach((n, i) => { const d = Math.hypot(n.x - mouse.x, n.y - mouse.y); if (d < bd) { bd = d; best = i; } });
      if (best !== nearestIdx) { nearestIdx = best; if (bd < 200) ignite(nearestIdx, 0.55); }
    };
    build();
    window.addEventListener("resize", build);
    window.addEventListener("mousemove", onMove);
    // ambient life — a soft pulse from a random node every few seconds
    let amb = 0;
    if (!reduce) amb = setInterval(() => { if (nodes.length && pulses.length < 4) ignite(Math.floor(Math.random() * nodes.length), 0.5); }, 3600);

    let raf, t0 = performance.now();
    const NW = () => nodes.length;
    const step = () => {
      const t = (performance.now() - t0) / 1000;
      ctx.fillStyle = "#06030f"; ctx.fillRect(0, 0, width, height);
      const g1 = ctx.createRadialGradient(width * 0.16, height * 0.22, 0, width * 0.16, height * 0.22, Math.max(width, height) * 0.62);
      g1.addColorStop(0, "rgba(124,58,237,0.24)"); g1.addColorStop(1, "rgba(0,0,0,0)");
      ctx.fillStyle = g1; ctx.fillRect(0, 0, width, height);
      const g2 = ctx.createRadialGradient(width * 0.88, height * 0.82, 0, width * 0.88, height * 0.82, Math.max(width, height) * 0.6);
      g2.addColorStop(0, "rgba(59,110,235,0.16)"); g2.addColorStop(1, "rgba(0,0,0,0)");
      ctx.fillStyle = g2; ctx.fillRect(0, 0, width, height);
      const g3 = ctx.createRadialGradient(width * 0.62, height * 0.05, 0, width * 0.62, height * 0.05, Math.max(width, height) * 0.45);
      g3.addColorStop(0, "rgba(168,85,247,0.12)"); g3.addColorStop(1, "rgba(0,0,0,0)");
      ctx.fillStyle = g3; ctx.fillRect(0, 0, width, height);

      for (const n of nodes) { n.x = n.ox + Math.sin(t * 0.45 + n.ox * 0.01) * 3.2; n.y = n.oy + Math.cos(t * 0.4 + n.oy * 0.01) * 3.2; }
      const NODE_E = new Array(NW()).fill(0), EDGE_E = new Map();
      for (let i = pulses.length - 1; i >= 0; i--) {
        const p = pulses[i]; p.t += 0.038; const depth = getDepths(p.source); const front = p.t * 4; let any = false;
        for (let k = 0; k < NW(); k++) { if (depth[k] < 0) continue; const phase = front - depth[k];
          if (phase > -0.5 && phase < 2) { const v = Math.max(0, 1 - Math.abs(phase - 0.4)) * p.intensity * Math.max(0, 1 - p.t * 0.4); NODE_E[k] = Math.max(NODE_E[k], v); any = true; } }
        for (const [a, b] of edges) { if (depth[a] < 0 || depth[b] < 0) continue; const phase = front - (Math.min(depth[a], depth[b]) + 0.5);
          if (phase > -0.5 && phase < 2) { const v = Math.max(0, 1 - Math.abs(phase - 0.4)) * p.intensity * Math.max(0, 1 - p.t * 0.4); const key = Math.min(a, b) * NW() + Math.max(a, b); EDGE_E.set(key, Math.max(EDGE_E.get(key) || 0, v)); } }
        if ((!any && p.t > 0.3) || p.t > 4) pulses.splice(i, 1);
      }
      if (nearestIdx >= 0) NODE_E[nearestIdx] = Math.max(NODE_E[nearestIdx], 0.55);
      ctx.globalCompositeOperation = "lighter";
      for (const [a, b] of edges) { const e = EDGE_E.get(Math.min(a, b) * NW() + Math.max(a, b)) || 0; const na = nodes[a], nb = nodes[b];
        ctx.strokeStyle = "rgba(196,178,255," + (0.05 + e * 0.85) + ")"; ctx.lineWidth = 0.55 + e * 2.3;
        ctx.beginPath(); ctx.moveTo(na.x, na.y); ctx.lineTo(nb.x, nb.y); ctx.stroke(); }
      for (let i = 0; i < NW(); i++) { const n = nodes[i], e = NODE_E[i];
        if (e > 0.04) { ctx.shadowColor = "rgba(198,178,255," + e + ")"; ctx.shadowBlur = 14 + e * 30; }
        else { ctx.shadowColor = "rgba(160,140,230,0.35)"; ctx.shadowBlur = 7; }
        ctx.fillStyle = "rgba(233,226,255," + (0.55 + e * 0.45) + ")";
        ctx.beginPath(); ctx.arc(n.x, n.y, 1.7 + e * 4, 0, Math.PI * 2); ctx.fill(); }
      ctx.shadowBlur = 0; ctx.globalCompositeOperation = "source-over";
      raf = requestAnimationFrame(step);
    };
    step();
    return () => { cancelAnimationFrame(raf); clearInterval(amb); window.removeEventListener("resize", build); window.removeEventListener("mousemove", onMove); };
  }, []);
  return (
    <div style={{ position: "fixed", inset: 0, zIndex: -1, overflow: "hidden", pointerEvents: "none", background: "#06030f" }}>
      <canvas ref={canvasRef} style={{ display: "block" }} />
      <div style={{ position: "absolute", inset: 0, background: "linear-gradient(180deg, rgba(6,3,15,0.42) 0%, rgba(6,3,15,0.58) 46%, rgba(6,3,15,0.78) 100%)" }}></div>
    </div>
  );
}
window.BloomBackground = BloomBackground;
