// strikeX — Wheel state graph. Single source of truth is the standalone asset
// assets/diagrams/wheel-state-graph.svg; we fetch it at runtime and inject it inline
// so its SMIL <animate> comet beams run in the live document (no duplicated markup).
// On scroll-into-view the graph's parts (edges, beams, nodes, labels) stagger-fade in;
// the optional `caption` rides the same reveal as the final staggered item.
// Each node is hover-shiny in a product color (CALL warm, PUT cold; T/Cash neutral) and,
// on hover, swaps its label to the product token (e.g. put_n). Exposes window.WheelGraph.
const WHEEL_SVG_URL = "assets/diagrams/wheel-state-graph.svg";

// Node → { palette p, token t }, keyed by the rect's x in the asset's viewBox.
// palette: warm = sun/flame/clay, cold = cobalt/azure/indigo (CALL → warm, PUT → cold).
// token: product token shown in the block label on hover (tokens/product-palettes.css).
const NODES = {
  "240":  { p: "clay",   t: "call_np" }, "976": { p: "clay",   t: "call_np" }, // CALL-CALL spreads
  "289":  { p: "sun",    t: "call_n"  }, "551": { p: "sun",    t: "call_n"  }, // +CALL legs
  "1048": { p: "sun",    t: "call_n"  },
  "414":  { p: "flame",  t: "call_p"  },                                       // T - CALL S0
  "793":  { p: "cobalt", t: "put_p"   },                                       // Cash - PUT S0
  "638":  { p: "azure",  t: "put_n"   }, "888": { p: "azure",  t: "put_n"   }, // +PUT legs
  "268":  { p: "azure",  t: "put_n"   },
  "853":  { p: "indigo", t: "put_np"  }, "233": { p: "indigo", t: "put_np"  }, // PUT-PUT spreads
};

function WheelGraph({ style, caption }) {
  const [svg, setSvg] = React.useState("");
  const ref = React.useRef(null);
  const captionRef = React.useRef(null);

  React.useEffect(() => {
    let alive = true;
    fetch(WHEEL_SVG_URL)
      .then((r) => (r.ok ? r.text() : Promise.reject(new Error("HTTP " + r.status))))
      .then((t) => { if (alive) setSvg(t); })
      .catch((e) => console.error("WheelGraph: failed to load " + WHEEL_SVG_URL, e));
    return () => { alive = false; };
  }, []);

  React.useLayoutEffect(() => {
    if (!svg || !ref.current) return;
    const root = ref.current.querySelector("svg");
    if (!root) return;

    // Tag each node with its palette class (CSS :hover shine), and on hover swap the
    // block's label to its product token name — reverting on mouse-leave.
    const texts = Array.from(root.querySelectorAll("text"));
    const labelInside = (r) => {
      const x = +r.getAttribute("x"), y = +r.getAttribute("y");
      const w = +r.getAttribute("width"), h = +r.getAttribute("height");
      return texts.find((t) => {
        const tx = +t.getAttribute("x"), ty = +t.getAttribute("y");
        return tx >= x && tx <= x + w && ty >= y && ty <= y + h;
      });
    };
    root.querySelectorAll("rect").forEach((r) => {
      const node = NODES[r.getAttribute("x")];
      if (!node) return;
      r.classList.add("wg-node", "wg-" + node.p);
      const label = labelInside(r);
      if (label) {
        const original = label.innerHTML;
        label.style.transition = "opacity .14s ease";
        let timer;
        // Crossfade the label: fade out, swap the text while invisible, fade back in.
        const swap = (apply) => {
          clearTimeout(timer);
          label.style.opacity = "0";
          timer = setTimeout(() => { apply(); label.style.opacity = "1"; }, 140);
        };
        r.addEventListener("mouseenter", () => swap(() => { label.textContent = node.t.toUpperCase(); }));
        r.addEventListener("mouseleave", () => swap(() => { label.innerHTML = original; }));
      }
    });
    // T & Cash: circles (ellipses) + entry arrows (polygons) shine neutral; labels stay.
    root.querySelectorAll("ellipse, polygon").forEach((el) => el.classList.add("wg-node", "wg-neutral"));

    // Hide each top-level part (+ caption) and stagger their fade-in when in view.
    // useLayoutEffect so parts are hidden before first paint (no flash).
    const parts = Array.from(root.children).filter(
      (el) => el.tagName !== "defs" && !(el.tagName === "g" && el.children.length === 0)
    );
    if (captionRef.current) parts.push(captionRef.current); // caption reveals last
    const reduce = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    if (reduce || typeof IntersectionObserver === "undefined") return; // leave fully visible

    parts.forEach((el) => { el.style.opacity = "0"; });
    const reveal = () => parts.forEach((el, i) => {
      el.style.animation = `wg-fade-in 900ms cubic-bezier(.22,1,.36,1) ${i * 90}ms both`;
    });
    // Fire only once the graph is well into view (top above ~65% of the viewport),
    // so the cascade plays while it's on screen rather than as it first peeks in.
    const io = new IntersectionObserver((entries, obs) => {
      if (entries.some((e) => e.isIntersecting)) { reveal(); obs.disconnect(); }
    }, { rootMargin: "0px 0px -35% 0px", threshold: 0 });
    io.observe(ref.current);
    return () => io.disconnect();
  }, [svg]);

  return (
    <div ref={ref} className="wg-mount" style={{ width: "100%", maxWidth: 1100, margin: "0 auto", minHeight: 320, ...style }}>
      <style>{`
        .wg-mount svg { width: 100%; height: auto; display: block; }
        /* Labels sit on top of the blocks — let pointer events fall through so hovering
           a block's text still triggers the block's :hover shine. */
        .wg-mount svg text { pointer-events: none; }
        @keyframes wg-fade-in { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: translateY(0); } }

        /* Per-node hover shine — each block lights up in its product-palette color. */
        .wg-mount .wg-node { fill: #121215; stroke: #3C3C45; cursor: pointer; transition: stroke .35s ease, fill .35s ease, filter .35s ease; }
        .wg-mount .wg-sun:hover    { stroke: var(--sun-400);    fill: var(--sun-900);    filter: drop-shadow(0 0 13px var(--sun-500)); }
        .wg-mount .wg-flame:hover  { stroke: var(--flame-400);  fill: var(--flame-900);  filter: drop-shadow(0 0 13px var(--flame-500)); }
        .wg-mount .wg-clay:hover   { stroke: var(--clay-400);   fill: var(--clay-900);   filter: drop-shadow(0 0 13px var(--clay-500)); }
        .wg-mount .wg-cobalt:hover { stroke: var(--cobalt-400); fill: var(--cobalt-900); filter: drop-shadow(0 0 13px var(--cobalt-500)); }
        .wg-mount .wg-azure:hover  { stroke: var(--azure-400);  fill: var(--azure-900);  filter: drop-shadow(0 0 13px var(--azure-500)); }
        .wg-mount .wg-indigo:hover { stroke: var(--indigo-400); fill: var(--indigo-900); filter: drop-shadow(0 0 13px var(--indigo-500)); }
        .wg-mount .wg-neutral:hover { stroke: var(--surface-card); fill: var(--gray-800); filter: drop-shadow(0 0 13px var(--surface-card)); }
      `}</style>
      <div dangerouslySetInnerHTML={{ __html: svg }} />
      {caption != null && (
        <div ref={captionRef} style={{ marginTop: 22, textAlign: "center" }}>{caption}</div>
      )}
    </div>
  );
}

window.WheelGraph = WheelGraph;
