// circuit-progress.jsx — the run's actual quantum circuit as a progress
// instrument, drawn in the artwork's light-trail language (design handoff
// 2026-07-14). Geometry is generated from the server timeline's gate lists:
// init H → scramble layers (rotations + 2 CX pairs each) → ancilla damage
// (star on the damaged wire) → mirrored heal layers → measurement.
//
// Gates light EVENT-DRIVEN via 'qbf:circuit' CustomEvents from
// circuitController.js — a CX link lights the moment its butterfly pair
// completes the entanglement dance (thread creation), not on a timer.
//
// Exposes: window.CircuitProgress, window.useQuantumRun.

(() => {
  const W = 260, H = 56;
  const wireY = [6, 17, 28, 39, 50];
  const NUM_QUBITS = 5;
  const iriAt = (f) => "hsl(" + (205 + f * 195) + " 85% 78%)";

  /* ---- geometry from the run's snapshot timeline ----
     One snapshot = one executed gate group:
       init      : H on bath qubits            → diamond column
       scramble  : rotations (+ 1st CX) / 2nd CX → rot column + CX link
       damage    : ancilla CX                   → barrier · star · barrier
       heal      : CX (+ rotations, mirrored)   → CX link + rot column
     Ids match the controller's snapshot indices: r{k} rotations, c{k} CX. */
  function parseRun(snapshots, damagedQubit) {
    const items = [];
    snapshots.forEach((snap, k) => {
      const gates = snap.gates || [];
      // Gate tuples: ["h", q], ["cx", ctrl, tgt] — but ["rx"/"rz", angle, q]
      const rotQubits = [...new Set(
        gates.filter((g) => g[0] === "rx" || g[0] === "rz").map((g) => g[2])
      )];
      const cx = gates.find((g) => g[0] === "cx");

      if (snap.phase === "init") {
        items.push({ id: "h0", type: "h", qubits: gates.filter((g) => g[0] === "h").map((g) => g[1]) });
      } else if (snap.phase === "damage") {
        items.push({ type: "barrier" });
        items.push({ id: "d", type: "damage", wire: damagedQubit });
        items.push({ type: "barrier" });
      } else if (snap.phase === "scramble") {
        if (rotQubits.length) items.push({ id: "r" + k, type: "rot", qubits: rotQubits });
        if (cx && cx[1] < NUM_QUBITS && cx[2] < NUM_QUBITS)
          items.push({ id: "c" + k, type: "cx", a: cx[1], b: cx[2] });
      } else if (snap.phase === "heal") {
        if (cx && cx[1] < NUM_QUBITS && cx[2] < NUM_QUBITS)
          items.push({ id: "c" + k, type: "cx", a: cx[1], b: cx[2] });
        if (rotQubits.length) items.push({ id: "r" + k, type: "rot", qubits: rotQubits });
      }
    });
    items.push({ id: "m", type: "meas" });

    const X0 = 14, X1 = 246;
    const stepX = (X1 - X0) / (items.length - 1);
    items.forEach((it, i) => { it.x = X0 + i * stepX; });
    return items;
  }

  /* ---- live run state, fed by circuitController events ----
     Returns null until a run is loaded. State:
       items       : circuit geometry (parseRun)
       lit         : Set of lit gate ids
       purities    : per-qubit reduced-state purity (updates on pair/damage)
       colors      : live wing colors (uColorALight after QMI hue blending)
       damagedQubit, fDamage, fHeal
       fid         : damaged qubit's fidelity readout (null before rupture)
       stage       : 'scramble' | 'rupture' | 'heal' | 'done'              */
  function useQuantumRun() {
    const [state, setState] = React.useState(null);

    React.useEffect(() => {
      let cur = null;
      const withFid = (c) => {
        if (c.stage === "scramble") return { ...c, fid: null };
        const t = c.healTotal > 0 ? c.healSeen / c.healTotal : 1;
        return { ...c, fid: c.fDamage + (c.fHeal - c.fDamage) * t };
      };

      const onEvent = (e) => {
        const d = e.detail;
        if (d.type === "loaded") {
          cur = withFid({
            items: parseRun(d.snapshots, d.damagedQubit),
            lit: new Set(["h0"]),
            purities: d.purities,
            colors: d.colors,
            damagedQubit: d.damagedQubit,
            fDamage: d.fDamage,
            fHeal: d.fHeal,
            healTotal: d.snapshots.filter((s) => s.phase === "heal").length,
            healSeen: 0,
            stage: "scramble",
          });
        } else if (!cur) {
          return;
        } else if (d.type === "snapshot") {
          // Lights the rot column only. Purities/colors ride 'pair' events —
          // they change via CX, which visually completes at the dance.
          cur = withFid({
            ...cur,
            lit: new Set(cur.lit).add("r" + d.index),
            stage: d.phase === "heal" ? "heal" : cur.stage,
          });
        } else if (d.type === "pair") {
          cur = withFid({
            ...cur,
            lit: new Set(cur.lit).add("c" + d.snapshotIndex),
            purities: d.purities || cur.purities,
            colors: d.colors || cur.colors,
            healSeen: cur.stage === "heal" ? cur.healSeen + 1 : cur.healSeen,
          });
        } else if (d.type === "damage") {
          cur = withFid({
            ...cur, lit: new Set(cur.lit).add("d"),
            purities: d.purities, colors: d.colors || cur.colors,
            stage: "rupture",
          });
        } else if (d.type === "done") {
          cur = withFid({
            ...cur, lit: new Set(cur.lit).add("m"),
            purities: d.purities || cur.purities,
            colors: d.colors || cur.colors,
            healSeen: cur.healTotal, stage: "done",
          });
        }
        setState(cur);
      };

      window.addEventListener("qbf:circuit", onEvent);
      // Catch up if events fired before this effect mounted (e.g. user opens
      // the panel before the 12s auto-open). Replay 'loaded' first to
      // bootstrap cur, then the latest event to restore mid-run state.
      if (window.__qbfCircuitLoaded) {
        onEvent({ detail: window.__qbfCircuitLoaded });
        if (window.__qbfCircuitLatest &&
            window.__qbfCircuitLatest !== window.__qbfCircuitLoaded) {
          onEvent({ detail: window.__qbfCircuitLatest });
        }
      }
      return () => window.removeEventListener("qbf:circuit", onEvent);
    }, []);

    return state;
  }

  /* ---- gate glyphs (handoff visual language) ---- */
  function Gate({ item, lit }) {
    const cls = "qcirc__g" + (lit ? " is-lit" : "") + (item.type === "damage" ? " qcirc__dmg" : "");
    const col = lit ? (item.type === "damage" ? "#ffe9df" : iriAt(item.x / W)) : null;

    if (item.type === "h")
      return (
        <g className={cls} style={col ? { fill: col } : null}>
          {item.qubits.map((q) => (
            <rect key={q} x={-2.1} y={-2.1} width={4.2} height={4.2}
                  transform={"translate(" + item.x + " " + wireY[q] + ") rotate(45)"} />
          ))}
        </g>
      );
    if (item.type === "rot")
      return (
        <g className={cls} style={col ? { fill: col } : null}>
          {item.qubits.map((q) => (
            <circle key={q} cx={item.x} cy={wireY[q]} r="1.25" />
          ))}
        </g>
      );
    if (item.type === "cx") {
      const y1 = wireY[item.a], y2 = wireY[item.b];
      return (
        <g className={cls} style={col ? { stroke: col, fill: col } : null}>
          <line x1={item.x} y1={y1} x2={item.x} y2={y2} />
          <circle cx={item.x} cy={y1} r="1.2" />
          <circle className="qcirc__tgt" cx={item.x} cy={y2} r="2.1" />
        </g>
      );
    }
    if (item.type === "damage") {
      const s = 3.6, x = item.x, y = wireY[item.wire];
      return <path className={cls}
                   d={"M" + x + " " + (y - s) + " L" + (x + 1) + " " + (y - 1) +
                      " L" + (x + s) + " " + y + " L" + (x + 1) + " " + (y + 1) +
                      " L" + x + " " + (y + s) + " L" + (x - 1) + " " + (y + 1) +
                      " L" + (x - s) + " " + y + " L" + (x - 1) + " " + (y - 1) + " Z"} />;
    }
    if (item.type === "meas")
      return (
        <g className={cls} style={col ? { stroke: col } : null}>
          {wireY.map((y) => (
            <g key={y}>
              <path d={"M" + (item.x - 2.4) + " " + (y + 1.4) + " A2.4 2.4 0 0 1 " + (item.x + 2.4) + " " + (y + 1.4)} />
              <line x1={item.x} y1={y + 1.2} x2={item.x + 1.8} y2={y - 1.6} />
            </g>
          ))}
        </g>
      );
    return null;
  }

  function CircuitProgress({ items, lit, vertical }) {
    // Lit-gate frontier drives the executed-wire gradient (no playhead).
    let px = 0;
    for (const it of items) {
      if (it.id && lit.has(it.id) && it.x + 6 > px) px = it.x + 6;
    }
    if (lit.has("m")) px = W;

    return (
      <svg className={"qcirc" + (vertical ? " qcirc--v" : "")}
           viewBox={vertical ? "0 0 " + H + " " + W : "0 0 " + W + " " + H}
           aria-hidden="true">
        <defs>
          <linearGradient id="qcircIri" x1="0" y1="0" x2="1" y2="0">
            <stop offset="0" stopColor="#9ad7ff" />
            <stop offset="0.5" stopColor="#eaa6ff" />
            <stop offset="1" stopColor="#ffd59a" />
          </linearGradient>
          <clipPath id="qcircDone"><rect x="0" y="0" width={px} height={H} /></clipPath>
        </defs>
        <g transform={vertical ? "translate(" + H + " 0) rotate(90)" : undefined}>
          {wireY.map((y, i) => <line key={i} className="qcirc__wire" x1="4" y1={y} x2="256" y2={y} />)}
          <g clipPath="url(#qcircDone)">
            {wireY.map((y, i) => (
              <line key={i} className="qcirc__wiredone" x1="4" y1={y} x2="256" y2={y} stroke="url(#qcircIri)" />
            ))}
          </g>
          {items.map((it, i) => it.type === "barrier"
            ? <line key={i} className="qcirc__barrier" x1={it.x} y1="2" x2={it.x} y2="54" />
            : <Gate key={i} item={it} lit={it.id != null && lit.has(it.id)} />)}
        </g>
      </svg>
    );
  }

  window.CircuitProgress = CircuitProgress;
  window.useQuantumRun = useQuantumRun;
})();
