/* SectionBackground — section-aware node network.
   One canvas engine that reads which section is centered in the viewport
   and eases between 7 visual modes. The world evolves from chaos → clarity.

   Modes (set via data-bg on each <section>):
     splash      — sparse ambient drift, barely visible
     problem     — scattered nodes, fragmented dashed lines (chaos)
     disconnect  — nodes cluster in silos, lines reach but stop short
     solution    — clean connected paths, smooth flowing pulses
     approval    — all routes converge to one calm central node
     industries  — clusters light up in quiet sequence
     cta         — one glowing path draws toward the right
*/
function SectionBackground() {
  const canvasRef = React.useRef(null);

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

    // ── Palette ────────────────────────────────────────────────────────────
    const TEAL  = [45, 212, 191];
    const SLATE = [148, 163, 184];

    // ── Config ─────────────────────────────────────────────────────────────
    const MAX_EDGE  = 190;
    const N_CLUSTER = 6;

    // ── State ──────────────────────────────────────────────────────────────
    let w = 0, h = 0, dpr = 1;
    let nodes = [], edges = [], clusters = [];
    let pulses = [], ctaPulse = null;
    let raf, lastT = 0, nextPulse = 0, lastRebuild = 0;

    const MODES = ["splash","problem","disconnect","solution","approval","industries","cta"];
    let targetMode = "splash";
    const blend = Object.fromEntries(MODES.map(k => [k, k === "splash" ? 1 : 0]));

    // ── Layout ─────────────────────────────────────────────────────────────
    function resize() {
      dpr = Math.min(window.devicePixelRatio || 1, 2);
      w = window.innerWidth;
      h = window.innerHeight;
      canvas.width  = w * dpr;
      canvas.height = h * dpr;
      canvas.style.width  = w + "px";
      canvas.style.height = h + "px";
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
      init();
    }

    function init() {
      const count = Math.max(40, Math.min(90, Math.round((w * h) / 18000)));
      nodes = Array.from({ length: count }, (_, id) => ({
        id,
        x:     Math.random() * w,
        y:     Math.random() * h,
        vx:    (Math.random() - 0.5) * 0.0013,
        vy:    (Math.random() - 0.5) * 0.0013,
        r:     0.8 + Math.random() * 0.9,
        phase: Math.random() * Math.PI * 2,
        cluster: id % N_CLUSTER,
        lit:   0
      }));

      // Fixed cluster anchors in a 3×2 grid with slight jitter
      clusters = Array.from({ length: N_CLUSTER }, (_, i) => ({
        x: w * (0.18 + 0.32 * (i % 3) + (Math.random() - 0.5) * 0.05),
        y: h * (0.30 + 0.40 * Math.floor(i / 3) + (Math.random() - 0.5) * 0.05)
      }));

      pulses = [];
      ctaPulse = null;
      buildEdges();
    }

    function buildEdges() {
      edges = [];
      for (let i = 0; i < nodes.length; i++) {
        for (let j = i + 1; j < nodes.length; j++) {
          const dx = nodes[i].x - nodes[j].x;
          const dy = nodes[i].y - nodes[j].y;
          if (dx * dx + dy * dy < MAX_EDGE * MAX_EDGE)
            edges.push({ a: i, b: j, sameCluster: nodes[i].cluster === nodes[j].cluster });
        }
      }
      lastRebuild = lastT;
    }

    // ── Mode observation ───────────────────────────────────────────────────
    function pickMode() {
      const sections = document.querySelectorAll("section[data-bg]");
      if (!sections.length) return;
      const vMid = window.innerHeight / 2;
      let best = null, bestDist = Infinity;
      sections.forEach(s => {
        const r = s.getBoundingClientRect();
        const d = Math.abs(r.top + r.height / 2 - vMid);
        if (d < bestDist) { bestDist = d; best = s.dataset.bg; }
      });
      if (best && MODES.includes(best)) targetMode = best;
    }

    function easeBlend(dt) {
      const spd = 0.003;
      for (const k of MODES) {
        const goal = k === targetMode ? 1 : 0;
        blend[k] += (goal - blend[k]) * Math.min(1, dt * spd);
      }
    }

    // ── Pulse helpers ──────────────────────────────────────────────────────
    function findPath(start, steps) {
      const path = [start], visited = new Set([start]);
      for (let s = 0; s < steps; s++) {
        const cur = path[path.length - 1];
        const nbrs = edges
          .filter(e => e.a === cur || e.b === cur)
          .map(e => e.a === cur ? e.b : e.a)
          .filter(n => !visited.has(n));
        if (!nbrs.length) break;
        const free = nbrs.filter(n => !pulses.some(p => p.ids.includes(n)));
        const pool = free.length ? free : nbrs;
        const next = pool[Math.floor(Math.random() * pool.length)];
        visited.add(next); path.push(next);
      }
      return path.length >= 2 ? path : null;
    }

    function makePulse(ids, speed) {
      const dists = [0];
      for (let i = 1; i < ids.length; i++) {
        const a = nodes[ids[i - 1]], b = nodes[ids[i]];
        dists.push(dists[i - 1] + Math.hypot(b.x - a.x, b.y - a.y));
      }
      return { ids, dists, total: dists[dists.length - 1], progress: 0, speed, lastSeg: -1 };
    }

    function posAt(p, t) {
      const dist = t * p.total;
      for (let i = 0; i < p.dists.length - 1; i++) {
        if (dist <= p.dists[i + 1]) {
          const segT = (dist - p.dists[i]) / (p.dists[i + 1] - p.dists[i]);
          const a = nodes[p.ids[i]], b = nodes[p.ids[i + 1]];
          return { x: a.x + (b.x - a.x) * segT, y: a.y + (b.y - a.y) * segT, seg: i };
        }
      }
      const last = nodes[p.ids[p.ids.length - 1]];
      return { x: last.x, y: last.y, seg: p.ids.length - 2 };
    }

    // ── Update ─────────────────────────────────────────────────────────────
    function update(now) {
      const dt = Math.min(now - lastT, 50);
      lastT = now;
      easeBlend(dt);

      // Central point for approval mode
      const cx = w * 0.5, cy = h * 0.52;

      // Cluster-attract force (disconnect, industries, approval)
      const cStr = blend.disconnect * 0.55 + blend.industries * 0.45 + blend.approval * 0.35;

      for (const n of nodes) {
        // Base drift
        n.x += n.vx * dt;
        n.y += n.vy * dt;

        // Cluster attraction
        if (cStr > 0.02) {
          const c = clusters[n.cluster];
          const dx = c.x - n.x, dy = c.y - n.y;
          const d = Math.hypot(dx, dy) || 1;
          const f = cStr * 0.00012 * d;
          n.vx += (dx / d) * f * dt;
          n.vy += (dy / d) * f * dt;
        }

        // Approval: converge toward central node
        if (blend.approval > 0.05) {
          const dx = cx - n.x, dy = cy - n.y;
          const d = Math.hypot(dx, dy) || 1;
          const f = blend.approval * 0.00008 * Math.min(d, 600);
          n.vx += (dx / d) * f * dt;
          n.vy += (dy / d) * f * dt;
        }

        // Solution: gentle rightward flow
        if (blend.solution > 0.05) {
          n.vx += blend.solution * 0.00004 * dt;
        }

        // Velocity cap + damping
        n.vx *= 0.986; n.vy *= 0.986;
        const sp = Math.hypot(n.vx, n.vy);
        if (sp > 0.16) { n.vx *= 0.16 / sp; n.vy *= 0.16 / sp; }

        // Boundary
        if (n.x < -20)  n.vx =  Math.abs(n.vx);
        if (n.x > w+20) n.vx = -Math.abs(n.vx);
        if (n.y < -20)  n.vy =  Math.abs(n.vy);
        if (n.y > h+20) n.vy = -Math.abs(n.vy);

        n.lit = Math.max(0, n.lit - dt * 0.0014);
      }

      if (now - lastRebuild > 30000) buildEdges();

      // Advance regular pulses
      for (const p of pulses) {
        p.progress = Math.min(1, p.progress + (p.speed / p.total) * (dt / 1000));
        // Disconnect: stall and kill pulses mid-path
        if (blend.disconnect > 0.6 && p.progress > 0.5 + Math.random() * 0.15) {
          p.progress = 1; // kill early
        }
        const { seg } = posAt(p, p.progress);
        if (seg !== p.lastSeg) { nodes[p.ids[seg]].lit = 1; p.lastSeg = seg; }
      }
      pulses = pulses.filter(p => p.progress < 1);

      // Spawn regular pulses (solution + industries modes)
      const solActive = blend.solution > 0.3 || blend.industries > 0.3 || blend.splash > 0.5;
      if (now > nextPulse && pulses.length < 2 && solActive && blend.cta < 0.5) {
        const ids = findPath(Math.floor(Math.random() * nodes.length), 3 + Math.floor(Math.random() * 3));
        if (ids) pulses.push(makePulse(ids, 45 + Math.random() * 25));
        nextPulse = now + 4000 + Math.random() * 5000;
      }

      // CTA: one slow horizontal pulse
      if (blend.cta > 0.4) {
        if (!ctaPulse || ctaPulse.done) {
          ctaPulse = { born: now, life: 4200, y: h * 0.52, x0: w * 0.06, x1: w * 0.90, done: false };
        }
        if (now - ctaPulse.born > ctaPulse.life) { ctaPulse.done = true; }
      } else {
        ctaPulse = null;
      }
    }

    // ── Draw ───────────────────────────────────────────────────────────────
    function draw(now) {
      ctx.clearRect(0, 0, w, h);

      const cx = w * 0.5, cy = h * 0.52;
      const isDisconnect = blend.disconnect > 0.3;
      const isProblem    = blend.problem   > 0.3;
      const isApproval   = blend.approval  > 0.3;

      // ── Approval: convergence lines toward central node ──────────────────
      if (isApproval) {
        ctx.lineWidth = 0.75;
        for (const n of nodes) {
          const d = Math.hypot(cx - n.x, cy - n.y);
          const a = blend.approval * Math.max(0, 1 - d / 560) * 0.14;
          if (a < 0.008) continue;
          ctx.strokeStyle = `rgba(${TEAL},${a})`;
          ctx.setLineDash([]);
          ctx.beginPath(); ctx.moveTo(n.x, n.y); ctx.lineTo(cx, cy); ctx.stroke();
        }
        // Central node halo
        const grd = ctx.createRadialGradient(cx, cy, 0, cx, cy, 56);
        grd.addColorStop(0, `rgba(${TEAL},${0.16 * blend.approval})`);
        grd.addColorStop(1, `rgba(${TEAL},0)`);
        ctx.fillStyle = grd;
        ctx.beginPath(); ctx.arc(cx, cy, 56, 0, Math.PI * 2); ctx.fill();
        // Central dot
        ctx.fillStyle = `rgba(${TEAL},${0.55 * blend.approval})`;
        ctx.beginPath(); ctx.arc(cx, cy, 3.5, 0, Math.PI * 2); ctx.fill();
      }

      // ── Proximity edges ──────────────────────────────────────────────────
      for (const e of edges) {
        const a = nodes[e.a], b = nodes[e.b];
        const dx = a.x - b.x, dy = a.y - b.y;
        const d  = Math.sqrt(dx * dx + dy * dy);
        if (d >= MAX_EDGE) continue;
        const close = 1 - d / MAX_EDGE;

        let alpha = 0;

        // Splash: faint ambient
        alpha += blend.splash * close * 0.07;

        // Problem: sparse dashed (all edges)
        if (isProblem) alpha += blend.problem * close * 0.08;

        // Disconnect: within-cluster solid, cross-cluster dashed and stops short
        if (isDisconnect) {
          if (e.sameCluster) alpha += blend.disconnect * close * 0.16;
          else               alpha += blend.disconnect * close * 0.05;
        }

        // Solution: clean solid, slightly brighter
        alpha += blend.solution * close * 0.14;

        // Industries: within-cluster
        if (e.sameCluster) alpha += blend.industries * close * 0.16;

        if (alpha < 0.008) continue;

        // Dash style
        const useDash = (isProblem && !isDisconnect) ||
                        (isDisconnect && !e.sameCluster && blend.disconnect > 0.4);
        ctx.setLineDash(useDash ? [2, 6] : []);
        ctx.lineWidth = 0.75;

        // Disconnect: cross-cluster lines stop short (reach but don't connect)
        if (isDisconnect && !e.sameCluster && blend.disconnect > 0.4) {
          const stopAt = 0.78; // draw only 78% of the line
          const mx = a.x + (b.x - a.x) * stopAt;
          const my = a.y + (b.y - a.y) * stopAt;
          ctx.strokeStyle = `rgba(${SLATE},${alpha * 0.7})`;
          ctx.beginPath(); ctx.moveTo(a.x, a.y); ctx.lineTo(mx, my); ctx.stroke();
          // Mirror from b side — two lines reaching toward each other with a gap
          const mx2 = b.x + (a.x - b.x) * stopAt;
          const my2 = b.y + (a.y - b.y) * stopAt;
          ctx.beginPath(); ctx.moveTo(b.x, b.y); ctx.lineTo(mx2, my2); ctx.stroke();
        } else {
          ctx.strokeStyle = `rgba(${SLATE},${alpha})`;
          ctx.beginPath(); ctx.moveTo(a.x, a.y); ctx.lineTo(b.x, b.y); ctx.stroke();
        }
      }
      ctx.setLineDash([]);

      // ── Pulse trails ─────────────────────────────────────────────────────
      for (const p of pulses) {
        const pos  = posAt(p, p.progress);
        const fade = Math.sin(Math.PI * p.progress);
        const str  = Math.max(blend.solution, blend.splash, blend.industries) * fade;
        if (str < 0.02) continue;

        // Completed segments — faint lingering glow
        for (let s = 0; s < pos.seg; s++) {
          const age = p.progress - p.dists[s + 1] / p.total;
          const a   = Math.max(0, 0.18 - age * 0.5) * str;
          if (a < 0.005) continue;
          const na = nodes[p.ids[s]], nb = nodes[p.ids[s + 1]];
          ctx.strokeStyle = `rgba(${TEAL},${a})`;
          ctx.lineWidth = 1;
          ctx.beginPath(); ctx.moveTo(na.x, na.y); ctx.lineTo(nb.x, nb.y); ctx.stroke();
        }

        // Current segment gradient
        const origin = nodes[p.ids[pos.seg]];
        const grad = ctx.createLinearGradient(origin.x, origin.y, pos.x, pos.y);
        grad.addColorStop(0, `rgba(${TEAL},0)`);
        grad.addColorStop(1, `rgba(${TEAL},${0.55 * str})`);
        ctx.strokeStyle = grad; ctx.lineWidth = 1;
        ctx.beginPath(); ctx.moveTo(origin.x, origin.y); ctx.lineTo(pos.x, pos.y); ctx.stroke();

        // Halo + dot
        const halo = ctx.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, 18);
        halo.addColorStop(0, `rgba(${TEAL},${0.38 * str})`);
        halo.addColorStop(1, `rgba(${TEAL},0)`);
        ctx.fillStyle = halo;
        ctx.beginPath(); ctx.arc(pos.x, pos.y, 18, 0, Math.PI * 2); ctx.fill();
        ctx.fillStyle = `rgba(${TEAL},${0.9 * str})`;
        ctx.beginPath(); ctx.arc(pos.x, pos.y, 2.2, 0, Math.PI * 2); ctx.fill();
      }

      // ── CTA pulse — one glowing line drawing left → right ────────────────
      if (ctaPulse && blend.cta > 0.05) {
        const t   = Math.min(1, (now - ctaPulse.born) / ctaPulse.life);
        const ease = t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
        const x   = ctaPulse.x0 + (ctaPulse.x1 - ctaPulse.x0) * ease;
        const y   = ctaPulse.y;
        const str = blend.cta;

        // Drawn line — gradient from left to tip
        const lg = ctx.createLinearGradient(ctaPulse.x0, y, x, y);
        lg.addColorStop(0, `rgba(${TEAL},0)`);
        lg.addColorStop(0.7, `rgba(${TEAL},${0.22 * str})`);
        lg.addColorStop(1, `rgba(${TEAL},${0.50 * str})`);
        ctx.strokeStyle = lg; ctx.lineWidth = 1;
        ctx.beginPath(); ctx.moveTo(ctaPulse.x0, y); ctx.lineTo(x, y); ctx.stroke();

        // Leading halo + dot
        const halo = ctx.createRadialGradient(x, y, 0, x, y, 24);
        halo.addColorStop(0, `rgba(${TEAL},${0.40 * str})`);
        halo.addColorStop(1, `rgba(${TEAL},0)`);
        ctx.fillStyle = halo;
        ctx.beginPath(); ctx.arc(x, y, 24, 0, Math.PI * 2); ctx.fill();
        ctx.fillStyle = `rgba(${TEAL},${0.9 * str})`;
        ctx.beginPath(); ctx.arc(x, y, 2.6, 0, Math.PI * 2); ctx.fill();
      }

      // ── Nodes ─────────────────────────────────────────────────────────────
      for (const n of nodes) {
        const breathe = 0.5 + 0.5 * Math.sin(now * 0.0005 + n.phase);

        // Industries: per-cluster cycling brightness
        let industryBoost = 0;
        if (blend.industries > 0.05) {
          const clusterT = (now / 1000 + n.cluster * 0.8) % (N_CLUSTER * 0.9);
          industryBoost = blend.industries * Math.max(0, Math.sin(clusterT)) * 0.3;
        }

        const alpha = Math.min(0.65,
          0.14 + 0.09 * breathe + n.lit * 0.38 + industryBoost
        );

        // Teal glow when lit
        if (n.lit > 0.05) {
          const ng = ctx.createRadialGradient(n.x, n.y, 0, n.x, n.y, 11);
          ng.addColorStop(0, `rgba(${TEAL},${n.lit * 0.33})`);
          ng.addColorStop(1, `rgba(${TEAL},0)`);
          ctx.fillStyle = ng;
          ctx.beginPath(); ctx.arc(n.x, n.y, 11, 0, Math.PI * 2); ctx.fill();
        }

        ctx.fillStyle = `rgba(${SLATE},${alpha})`;
        ctx.beginPath(); ctx.arc(n.x, n.y, n.r, 0, Math.PI * 2); ctx.fill();
      }
    }

    // ── Loop ──────────────────────────────────────────────────────────────
    function frame(now) {
      update(now);
      draw(now);
      raf = requestAnimationFrame(frame);
    }

    const onScroll = () => pickMode();
    const reduced  = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;

    resize();
    pickMode();
    window.addEventListener("resize", resize);
    window.addEventListener("scroll", onScroll, { passive: true });

    if (!reduced) {
      raf = requestAnimationFrame(t => { lastT = t; frame(t); });
    } else {
      draw(performance.now());
    }

    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("resize", resize);
      window.removeEventListener("scroll", onScroll);
    };
  }, []);

  return (
    <canvas
      ref={canvasRef}
      aria-hidden="true"
      style={{
        position: "fixed",
        inset: 0,
        width: "100%",
        height: "100%",
        zIndex: 0,
        pointerEvents: "none"
      }}
    />
  );
}

window.SectionBackground = SectionBackground;
