// Hero Network — soft glowing 3D-ish dots + lines representing the
// portfolio + shared-services lattice. Drives a <canvas class="mq-hero__network">
// element placed inside .mq-hero. Tweakable via the in-page Tweaks panel.

const { useEffect, useRef } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  show: true,
  density: 31,
  speed: 0.6,
  glow: 8,
  dotSize: 1.0,
  lineOpacity: 0.5,
  connectRange: 320,
  palette: ['#F05828', '#20A8E0', '#1F8A7A', '#C28319', '#FFFFFF'],
}/*EDITMODE-END*/;

const PALETTE_OPTIONS = [
  ['#F05828', '#20A8E0', '#1F8A7A', '#C28319', '#FFFFFF'],   // Forward Rise — full
  ['#F05828', '#FF9A6E', '#FFC0A2'],                          // Embers (orange-only)
  ['#20A8E0', '#3AB6E7', '#6BC9F0'],                          // Sky tones
  ['#FFFFFF', '#E3E7EE', '#97A2B5'],                          // Whites (subtle)
  ['#F05828', '#FFFFFF'],                                     // High-contrast pair
];

function hexAlpha(hex, a) {
  const r = parseInt(hex.slice(1, 3), 16);
  const g = parseInt(hex.slice(3, 5), 16);
  const b = parseInt(hex.slice(5, 7), 16);
  return `rgba(${r},${g},${b},${a})`;
}

function HeroNetwork() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);

  // Keep a ref to current tweak values so the animation loop reads fresh state
  // without re-running the effect (which would reset positions every change).
  const tRef = useRef(t);
  useEffect(() => { tRef.current = t; }, [t]);

  useEffect(() => {
    const canvases = document.querySelectorAll('.mq-network');
    if (!canvases.length) return;
    const dpr = Math.min(window.devicePixelRatio || 1, 2);
    const cleanups = [];

    canvases.forEach((canvas) => {
      const ctx = canvas.getContext('2d');
      let nodes = [];
      let lastDensity = -1;
      let lastPaletteKey = '';
      let raf = 0;
      let lastTime = performance.now();

      function size() {
        const r = canvas.getBoundingClientRect();
        canvas.width = Math.max(1, r.width * dpr);
        canvas.height = Math.max(1, r.height * dpr);
        ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
      }

      function initNodes(n, palette, w, h) {
        const arr = [];
        for (let i = 0; i < n; i++) {
          arr.push({
            x: Math.random() * w,
            y: Math.random() * h,
            vx: (Math.random() - 0.5) * 0.35,
            vy: (Math.random() - 0.5) * 0.35,
            z: Math.random(),                   // depth 0..1
            color: palette[i % palette.length],
            phase: Math.random() * Math.PI * 2,
            pulseFreq: 0.6 + Math.random() * 0.8,
          });
        }
        return arr;
      }

      function tick(now) {
        const dt = Math.min(2.5, (now - lastTime) / 16.67);
        lastTime = now;
        const tw = tRef.current;
        const r = canvas.getBoundingClientRect();

        const palette = Array.isArray(tw.palette) ? tw.palette : PALETTE_OPTIONS[0];
        const palKey = palette.join(',');
        if (tw.density !== lastDensity || palKey !== lastPaletteKey) {
          nodes = initNodes(tw.density, palette, r.width, r.height);
          lastDensity = tw.density;
          lastPaletteKey = palKey;
        }

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

        if (!tw.show) {
          raf = requestAnimationFrame(tick);
          return;
        }

        for (const n of nodes) {
          const sf = 0.25 + n.z * 0.85;
          n.x += n.vx * dt * tw.speed * sf;
          n.y += n.vy * dt * tw.speed * sf;
          n.phase += 0.014 * dt * n.pulseFreq;
          if (n.x < -24) n.x = r.width + 24;
          else if (n.x > r.width + 24) n.x = -24;
          if (n.y < -24) n.y = r.height + 24;
          else if (n.y > r.height + 24) n.y = -24;
        }

        ctx.lineWidth = 1;
        const thr = tw.connectRange;
        const sortByZ = nodes.slice().sort((a, b) => a.z - b.z);
        for (let i = 0; i < sortByZ.length; i++) {
          const a = sortByZ[i];
          for (let j = i + 1; j < sortByZ.length; j++) {
            const b = sortByZ[j];
            const dx = a.x - b.x;
            const dy = a.y - b.y;
            const d2 = dx * dx + dy * dy;
            if (d2 > thr * thr) continue;
            const d = Math.sqrt(d2);
            const t01 = 1 - d / thr;
            const alpha = t01 * tw.lineOpacity * ((a.z + b.z) / 2 + 0.15);
            if (alpha < 0.01) continue;
            const grad = ctx.createLinearGradient(a.x, a.y, b.x, b.y);
            grad.addColorStop(0, hexAlpha(a.color, alpha));
            grad.addColorStop(1, hexAlpha(b.color, alpha));
            ctx.strokeStyle = grad;
            ctx.beginPath();
            ctx.moveTo(a.x, a.y);
            ctx.lineTo(b.x, b.y);
            ctx.stroke();
          }
        }

        for (const n of sortByZ) {
          const radius = (1.4 + n.z * 4.2) * tw.dotSize + Math.sin(n.phase) * 0.7;
          const alpha = 0.35 + n.z * 0.65;
          ctx.fillStyle = hexAlpha(n.color, alpha);
          ctx.shadowBlur = tw.glow * (0.5 + n.z);
          ctx.shadowColor = n.color;
          ctx.beginPath();
          ctx.arc(n.x, n.y, radius, 0, Math.PI * 2);
          ctx.fill();
        }
        ctx.shadowBlur = 0;

        raf = requestAnimationFrame(tick);
      }

      size();
      const ro = ('ResizeObserver' in window) ? new ResizeObserver(size) : null;
      if (ro) ro.observe(canvas);
      else window.addEventListener('resize', size);

      raf = requestAnimationFrame(tick);

      cleanups.push(() => {
        cancelAnimationFrame(raf);
        if (ro) ro.disconnect();
        else window.removeEventListener('resize', size);
      });
    });

    return () => cleanups.forEach((fn) => fn());
  }, []);

  return (
    <TweaksPanel title="Background network">
      <TweakSection label="Visibility" />
      <TweakToggle label="Show pattern" value={t.show}
                   onChange={(v) => setTweak('show', v)} />

      <TweakSection label="Pattern" />
      <TweakSlider label="Density" value={t.density} min={6} max={80} step={1}
                   onChange={(v) => setTweak('density', v)} />
      <TweakSlider label="Connect range" value={t.connectRange} min={80} max={320} step={10} unit="px"
                   onChange={(v) => setTweak('connectRange', v)} />
      <TweakSlider label="Line strength" value={t.lineOpacity} min={0} max={0.5} step={0.02}
                   onChange={(v) => setTweak('lineOpacity', v)} />

      <TweakSection label="Motion" />
      <TweakSlider label="Drift speed" value={t.speed} min={0} max={1.5} step={0.05}
                   onChange={(v) => setTweak('speed', v)} />

      <TweakSection label="Glow" />
      <TweakSlider label="Glow blur" value={t.glow} min={0} max={20} step={1} unit="px"
                   onChange={(v) => setTweak('glow', v)} />
      <TweakSlider label="Dot size" value={t.dotSize} min={0.4} max={2.5} step={0.05} unit="×"
                   onChange={(v) => setTweak('dotSize', v)} />

      <TweakSection label="Palette" />
      <TweakColor  label="Colors" value={t.palette} options={PALETTE_OPTIONS}
                   onChange={(v) => setTweak('palette', v)} />
    </TweaksPanel>
  );
}

const mount = document.getElementById('tweaks-root');
if (mount) {
  ReactDOM.createRoot(mount).render(<HeroNetwork />);
}
