/* Mixto — landing-page constellation hero.
   A wider, ambient remix of the DS Mastery Map: every math fact is a node
   colored by the three sacred states (gray=unseen / amber=in-progress /
   green=mastered), grouped into operation clusters with connective lines,
   soft star-dust, and a green glow on mastered nodes. Parameterized by
   width/height + fractional cluster centers so it can fill any hero box. */

const MXH_FILL = { unseen: "#C8CCDE", progress: "#FFC23D", mastered: "#28D072" };

function mxhRng(seed) {
  let s = seed >>> 0;
  return () => {
    s = (s * 1664525 + 1013904223) >>> 0;
    return s / 4294967296;
  };
}

/* Build a loose elliptical blob of fact-nodes around a center (px coords). */
function mxhCluster(cx, cy, spreadX, spreadY, count, seed, mix) {
  const rnd = mxhRng(seed);
  const nodes = [];
  for (let i = 0; i < count; i++) {
    const ang = rnd() * Math.PI * 2;
    const rad = Math.sqrt(rnd());
    const r = rnd();
    let state = "unseen";
    if (r < mix[0]) state = "mastered";
    else if (r < mix[0] + mix[1]) state = "progress";
    nodes.push({
      x: cx + Math.cos(ang) * rad * spreadX,
      y: cy + Math.sin(ang) * rad * spreadY,
      state,
      rad: state === "unseen" ? 3.2 : 4.6,
      seed: seed * 31 + i,
    });
  }
  return nodes;
}

/* clusterDefs: [{op,label,color,fx,fy,sx,sy,count,seed,mix,focus}]
   fx/fy are fractions of W/H; sx/sy spreads as fractions. */
function HeroMap({
  width = 1180,
  height = 520,
  clusterDefs,
  showLabels = true,
  showBg = true,
  bgFrom = "#FFFFFF",
  bgTo = "#E7ECFA",
  dust = 60,
  idSuffix = "a",
  style,
}) {
  const W = width, H = height;
  const defs = clusterDefs || [
    { op: "×", label: "Multiply", color: "#18C9D6", fx: 0.34, fy: 0.42, sx: 0.20, sy: 0.34, count: 38, seed: 7, mix: [0.36, 0.34, 0.30], focus: true },
    { op: "÷", label: "Divide", color: "#FF7A2D", fx: 0.72, fy: 0.34, sx: 0.15, sy: 0.26, count: 22, seed: 21, mix: [0.20, 0.34, 0.46] },
    { op: "+", label: "Add", color: "#BFEF3C", fx: 0.16, fy: 0.74, sx: 0.12, sy: 0.22, count: 18, seed: 33, mix: [0.5, 0.3, 0.2] },
    { op: "−", label: "Subtract", color: "#84E8EF", fx: 0.62, fy: 0.78, sx: 0.16, sy: 0.20, count: 20, seed: 49, mix: [0.42, 0.34, 0.24] },
  ];

  const clusters = defs.map((c) => ({
    ...c,
    nodes: mxhCluster(c.fx * W, c.fy * H, c.sx * W, c.sy * H, c.count, c.seed, c.mix),
  }));

  // connective constellation lines: nearest-neighbor within a cluster
  const lines = [];
  const maxD = (W * 0.075) ** 2;
  clusters.forEach((c, ci) => {
    c.nodes.forEach((n, i) => {
      let best = null, bd = 1e12;
      c.nodes.forEach((m, j) => {
        if (j <= i) return;
        const d = (n.x - m.x) ** 2 + (n.y - m.y) ** 2;
        if (d < bd) { bd = d; best = m; }
      });
      if (best && bd < maxD) lines.push({ x1: n.x, y1: n.y, x2: best.x, y2: best.y, ci });
    });
  });

  const bgId = `mxh-bg-${idSuffix}`;
  const glowId = `mxh-glow-${idSuffix}`;

  return (
    <svg viewBox={`0 0 ${W} ${H}`} width="100%" height="100%" preserveAspectRatio="xMidYMid slice"
      style={{ display: "block", ...style }} aria-label="Mixto mastery map — facts filling from gray to amber to green">
      <defs>
        <radialGradient id={bgId} cx="50%" cy="18%" r="90%">
          <stop offset="0%" stopColor={bgFrom} />
          <stop offset="100%" stopColor={bgTo} />
        </radialGradient>
        <filter id={glowId} x="-120%" y="-120%" width="340%" height="340%">
          <feGaussianBlur stdDeviation="3.4" result="b" />
          <feMerge><feMergeNode in="b" /><feMergeNode in="SourceGraphic" /></feMerge>
        </filter>
      </defs>
      {showBg && <rect x="0" y="0" width={W} height={H} fill={`url(#${bgId})`} />}
      {/* faint star dust */}
      {Array.from({ length: dust }).map((_, i) => {
        const r = mxhRng(900 + i + (idSuffix.charCodeAt(0) || 0) * 13);
        return <circle key={"d" + i} cx={r() * W} cy={r() * H} r={r() * 1.1 + 0.3} fill="#C4CAE4" opacity={r() * 0.4 + 0.1} />;
      })}
      {/* constellation lines */}
      {lines.map((l, i) => (
        <line key={"l" + i} x1={l.x1} y1={l.y1} x2={l.x2} y2={l.y2} stroke="#D7DCEC" strokeWidth="1.1" opacity="0.85" />
      ))}
      {/* cluster labels */}
      {showLabels && clusters.map((c, ci) => {
        const cx = c.nodes.reduce((a, n) => a + n.x, 0) / c.nodes.length;
        const top = Math.min(...c.nodes.map((n) => n.y));
        return (
          <text key={"lab" + ci} x={cx} y={top - 18} textAnchor="middle"
            fontFamily="Fredoka, sans-serif" fontWeight="600" fontSize="15" fill="#3B4374" opacity="0.92">
            <tspan fill={c.color} fontSize="19">{c.op}</tspan>  {c.label}
          </text>
        );
      })}
      {/* nodes */}
      {clusters.map((c, ci) =>
        c.nodes.map((n, i) => {
          const fill = MXH_FILL[n.state];
          const isM = n.state === "mastered";
          return (
            <g key={ci + "-" + i} filter={isM ? `url(#${glowId})` : undefined}>
              <circle cx={n.x} cy={n.y} r={n.rad} fill={fill}
                stroke={n.state === "unseen" ? "#B4BAD2" : "rgba(255,255,255,.85)"}
                strokeWidth={n.state === "unseen" ? 1 : 1.3} />
              {/* shape cue (not color alone): mastered gets an inner dot */}
              {isM && <circle cx={n.x} cy={n.y} r={1.5} fill="#0B3D22" opacity="0.45" />}
            </g>
          );
        })
      )}
    </svg>
  );
}

Object.assign(window, { HeroMap, MXH_FILL });
