// shape-wave-hero.jsx — Hero canvas: colorful shape grid that reacts to
// pointer + emits waves on click. Adapted from the user's CodePen and
// scoped to the hero container; respects [data-shape-mask] rects so the
// headline/CTAs stay legible.

function ShapeWaveHero({ accent = "#3a8dff", palette, children, height = "clamp(640px, 90vh, 920px)" }) {
  const wrapRef = React.useRef(null);
  const canvasRef = React.useRef(null);

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

    // ── Config ──
    const gap = 38;
    const radiusVmin = 30;
    const speedIn = 0.5;
    const speedOut = 0.6;
    const restScale = 0.5;
    const minHoverScale = 1.2;
    const maxHoverScale = 3;
    const waveSpeed = 1200;
    const waveWidth = 180;

    const PALETTE = palette || [
      // Brand-tuned: accent blue + warm sparks, plus a few darker tones
      { type: "solid", value: "#3a8dff" },
      { type: "solid", value: "#5fa8ff" },
      { type: "solid", value: "#1f6fe0" },
      { type: "solid", value: "#a78bfa" },
      { type: "solid", value: "#ec4899" },
      { type: "solid", value: "#f97316" },
      { type: "solid", value: "#facc15" },
      { type: "solid", value: "#22c55e" },
      { type: "solid", value: "#06b6d4" },
      { type: "solid", value: "#9ca3af" },
      { type: "gradient", stops: ["#3a8dff", "#a78bfa"] },
      { type: "gradient", stops: ["#06b6d4", "#3a8dff"] },
      { type: "gradient", stops: ["#ec4899", "#3a8dff"] },
      { type: "gradient", stops: ["#22c55e", "#06b6d4"] },
      { type: "gradient", stops: ["#f97316", "#ec4899"] },
    ];
    const SHAPE_TYPES = ["circle", "pill", "star", "star"];

    let shapes = [];
    let W = 0, H = 0;
    let pointer = null;
    let activity = 0;
    let waves = [];
    let maskRects = [];
    let frame = 0;
    let maskOverride = false;
    let rafId = null;
    let canvasRect = null;

    const rnd = (a, b) => Math.random() * (b - a) + a;
    const rndInt = (a, b) => Math.floor(rnd(a, b + 1));
    const pick = (a) => a[Math.floor(Math.random() * a.length)];
    const smoothstep = (t) => { const c = Math.max(0, Math.min(1, t)); return c * c * (3 - 2 * c); };
    const dToF = (s) => s <= 0 ? 1 : 1 - Math.pow(0.05, 1 / (60 * s));

    function drawCircle(s) { ctx.beginPath(); ctx.arc(0, 0, s, 0, Math.PI * 2); ctx.fill(); }
    function drawPill(s) {
      const w = s * 0.48, h = s;
      ctx.beginPath();
      if (ctx.roundRect) ctx.roundRect(-w, -h, w * 2, h * 2, w);
      else { ctx.rect(-w, -h, w * 2, h * 2); }
      ctx.fill();
    }
    function drawStar(s, pts, ir) {
      ctx.beginPath();
      for (let i = 0; i < pts * 2; i++) {
        const a = (i * Math.PI) / pts - Math.PI / 2;
        const r = i % 2 === 0 ? s : s * ir;
        const x = Math.cos(a) * r, y = Math.sin(a) * r;
        i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
      }
      ctx.closePath(); ctx.fill();
    }
    function drawShape(sh) {
      switch (sh.type) {
        case "circle": return drawCircle(sh.size / 1.5);
        case "pill":   return drawPill(sh.size / 1.4);
        case "star":   return drawStar(sh.size, sh.points, sh.innerRatio);
      }
    }
    function resolveFill(c, sz) {
      if (c.type === "solid") return c.value;
      const g = ctx.createRadialGradient(0, -sz * 0.3, 0, 0, sz * 0.3, sz * 1.5);
      g.addColorStop(0, c.stops[0]); g.addColorStop(1, c.stops[1]);
      return g;
    }
    function randomStar() { return { points: rndInt(4, 10), innerRatio: rnd(0.1, 0.5) }; }

    function buildGrid() {
      const cols = Math.floor(W / gap);
      const rows = Math.floor(H / gap);
      const ox = (W - (cols - 1) * gap) / 2;
      const oy = (H - (rows - 1) * gap) / 2;
      shapes = [];
      for (let r = 0; r < rows; r++) {
        for (let c = 0; c < cols; c++) {
          const type = pick(SHAPE_TYPES);
          const sh = {
            x: ox + c * gap, y: oy + r * gap,
            type, color: pick(PALETTE),
            angle: rnd(0, Math.PI * 2),
            size: gap * 0.38,
            scale: restScale,
            maxScale: rnd(minHoverScale, maxHoverScale),
            hovered: false,
          };
          if (type === "star") Object.assign(sh, randomStar());
          shapes.push(sh);
        }
      }
    }

    function resize() {
      const rect = wrap.getBoundingClientRect();
      W = rect.width;
      H = rect.height;
      const dpr = window.devicePixelRatio || 1;
      canvas.width = W * dpr; canvas.height = H * dpr;
      canvas.style.width = W + "px"; canvas.style.height = H + "px";
      ctx.setTransform(1, 0, 0, 1, 0, 0);
      ctx.scale(dpr, dpr);
      buildGrid();
    }

    function tick() {
      const now = performance.now();
      ctx.clearRect(0, 0, W, H);
      // dark bg matching the site
      ctx.fillStyle = "#0a0a0c";
      ctx.fillRect(0, 0, W, H);
      activity *= 0.93;

      frame++;
      if (frame % 10 === 0) {
        canvasRect = canvas.getBoundingClientRect();
        maskRects = Array.from(wrap.querySelectorAll("[data-shape-mask]")).map((el) => {
          const r = el.getBoundingClientRect();
          // translate viewport rect → canvas-local rect
          return {
            left: r.left - canvasRect.left,
            right: r.right - canvasRect.left,
            top: r.top - canvasRect.top,
            bottom: r.bottom - canvasRect.top,
          };
        });
      }

      const maxDist = Math.sqrt(W * W + H * H);
      waves = waves.filter((w) => (now - w.startTime) / 1000 * waveSpeed < maxDist + waveWidth);

      const radius = Math.min(W, H) * (radiusVmin / 100);

      for (let i = 0; i < shapes.length; i++) {
        const sh = shapes[i];
        const pad = gap / 2;
        const masked = !maskOverride && maskRects.some((r) =>
          sh.x >= r.left - pad && sh.x <= r.right + pad &&
          sh.y >= r.top - pad && sh.y <= r.bottom + pad);

        if (masked) {
          sh.scale += (0 - sh.scale) * dToF(speedOut);
          if (sh.scale < 0.005) sh.scale = 0;
          continue;
        }

        let pInf = 0;
        if (pointer && activity > 0.001) {
          const dx = sh.x - pointer.x, dy = sh.y - pointer.y;
          const d = Math.sqrt(dx * dx + dy * dy);
          pInf = smoothstep(1 - d / radius) * activity;
          if (pInf > 0.05 && !sh.hovered) {
            sh.hovered = true;
            sh.maxScale = rnd(minHoverScale, maxHoverScale);
            sh.angle = rnd(0, Math.PI * 2);
            if (sh.type === "star") Object.assign(sh, randomStar());
          } else if (pInf <= 0.05) {
            sh.hovered = false;
          }
        } else {
          sh.hovered = false;
        }

        let wInf = 0;
        for (let j = 0; j < waves.length; j++) {
          const w = waves[j];
          const wr = (now - w.startTime) / 1000 * waveSpeed;
          const wdx = sh.x - w.x, wdy = sh.y - w.y;
          const wd = Math.sqrt(wdx * wdx + wdy * wdy);
          const t = 1 - Math.abs(wd - wr) / waveWidth;
          if (t > 0) wInf = Math.max(wInf, Math.sin(Math.PI * t));
        }

        const pT = restScale + pInf * (sh.maxScale - restScale);
        const wT = restScale + wInf * (sh.maxScale - restScale);
        const target = Math.max(pT, wT);

        const f = target > sh.scale ? dToF(speedIn) : dToF(speedOut);
        sh.scale += (target - sh.scale) * f;
        if (sh.scale < restScale * 0.15) continue;

        ctx.save();
        ctx.translate(sh.x, sh.y);
        ctx.rotate(sh.angle);
        ctx.scale(sh.scale, sh.scale);
        ctx.fillStyle = resolveFill(sh.color, sh.size);
        drawShape(sh);
        ctx.restore();
      }
      rafId = requestAnimationFrame(tick);
    }

    function onMove(e) {
      const r = canvas.getBoundingClientRect();
      pointer = { x: e.clientX - r.left, y: e.clientY - r.top };
      activity = 1;
    }
    function onLeave() {
      pointer = null;
      activity = 0;
    }
    function triggerWave(x, y) {
      x = x !== undefined ? x : W / 2;
      y = y !== undefined ? y : H / 2;
      waves.push({ x, y, startTime: performance.now() });
      maskOverride = true;
      const delay = Math.sqrt(W * W + H * H) / waveSpeed;
      setTimeout(() => { maskOverride = false; }, delay * 1000);
    }
    function onClick(e) {
      const r = canvas.getBoundingClientRect();
      // Don't trigger when clicking inside masked elements (CTAs, etc.)
      const targetIsInteractive = e.target.closest("[data-shape-mask] button, [data-shape-mask] a");
      if (targetIsInteractive) return;
      triggerWave(e.clientX - r.left, e.clientY - r.top);
    }

    resize();
    rafId = requestAnimationFrame(tick);
    // initial wave from center
    setTimeout(() => triggerWave(), 300);
    // ambient wave every ~5s so the hero feels alive at rest
    const ambient = setInterval(() => {
      // Skip if user is actively hovering (pointer activity present)
      if (activity > 0.05) return;
      triggerWave(W * (0.2 + Math.random() * 0.6), H * (0.2 + Math.random() * 0.6));
    }, 5200);

    const ro = new ResizeObserver(resize);
    ro.observe(wrap);
    wrap.addEventListener("pointermove", onMove);
    wrap.addEventListener("pointerleave", onLeave);
    wrap.addEventListener("click", onClick);

    return () => {
      cancelAnimationFrame(rafId);
      clearInterval(ambient);
      ro.disconnect();
      wrap.removeEventListener("pointermove", onMove);
      wrap.removeEventListener("pointerleave", onLeave);
      wrap.removeEventListener("click", onClick);
    };
  }, [palette]);

  return (
    <div
      ref={wrapRef}
      style={{
        position: "relative",
        width: "100%",
        height,
        minHeight: height,
        overflow: "hidden",
        background: "#0a0a0c",
      }}
    >
      <canvas
        ref={canvasRef}
        style={{ position: "absolute", inset: 0, display: "block", pointerEvents: "none" }}
      />
      <div style={{ position: "relative", width: "100%", height: "100%", zIndex: 2 }}>
        {children}
      </div>
    </div>
  );
}

Object.assign(window, { ShapeWaveHero });
