/* recursionlab.jsx — RecursionTutor: the recursion call-tree bench. Built on window.BenchKit
   (harness) + window.BenchFields (shared field math), so this file is just the FIGURE.
   compute = window.BenchFields.recursion — the SAME function server/benches/recursion.js
   calls, so client and server fields are identical by construction. Mirrors the gas-laws /
   big-o lab: controls → BenchFields.fn(state) → report + meters. */
(function () {
  const { useState, useMemo, useEffect } = React;
  const K = window.BenchKit, C = K.C, SVG_BG = K.SVG_BG, fmt = K.fmt;
  const TaskStrip = K.TaskStrip, StatMeter = K.StatMeter;
  const compute = (s) => window.BenchFields.recursion(s);
  const FUNCS = ["factorial", "fibonacci"];
  const FUNC_LABEL = { factorial: "factorial(n)", fibonacci: "naive fib(n)" };
  const FUNC_COL = { factorial: "#54f2b2", fibonacci: "#ff7a6b" };

  function Slider({ label, value, set, min, max, step, color, onUp }) {
    return (
      <div style={{ display: "flex", alignItems: "center", gap: 10, marginTop: 9 }}>
        <span style={{ fontSize: 12, color: C.mute, width: 116 }}>{label}</span>
        <input type="range" min={min} max={max} step={step} value={value} onChange={(e) => set(parseInt(e.target.value, 10))} onPointerUp={onUp} style={{ flex: 1 }} />
        <span style={{ color: color || C.amber, width: 72, textAlign: "right", fontFamily: "monospace" }}>{value}</span>
      </div>
    );
  }

  function RecursionFig({ task, setTask, tasks, report, event, done }) {
    const [functionType, setFunctionType] = useState("factorial");
    const [n, setN] = useState(5);
    const fld = useMemo(() => compute({ functionType, n }), [functionType, n]);
    useEffect(() => { report(fld); }, [functionType, n]); // eslint-disable-line

    const t = tasks.find((x) => x.id === task) || tasks[0];
    const col = FUNC_COL[functionType];

    // ── build the call tree for drawing ───────────────────────────────────────────
    // factorial(n): a single chain of n+1 nodes (linear). fibonacci(n): a branching
    // binary tree, truncated for large n with a note of the true call count.
    const X0 = 40, X1 = 560, Y0 = 36, Y1 = 250;
    const nodes = [];
    const edges = [];
    if (functionType === "factorial") {
      const total = n + 1; // factorial(n) .. factorial(0)
      const step = total > 1 ? (X1 - X0) / (total - 1) : 0;
      for (let i = 0; i < total; i++) {
        const x = X0 + i * step, y = (Y0 + Y1) / 2;
        nodes.push({ x, y, label: String(n - i), base: i === total - 1 });
        if (i > 0) edges.push({ x1: nodes[i - 1].x, y1: nodes[i - 1].y, x2: x, y2: y });
      }
    } else {
      // draw the binary call tree by depth; truncate to keep it readable.
      const maxDrawDepth = Math.min(n, 5);
      const levels = []; // levels[d] = array of {x,y,label}
      const place = (depth, val, lo, hi, parent) => {
        const x = (lo + hi) / 2, y = Y0 + (depth / Math.max(1, maxDrawDepth)) * (Y1 - Y0);
        const node = { x, y, label: String(val), base: val <= 1 };
        (levels[depth] = levels[depth] || []).push(node);
        nodes.push(node);
        if (parent) edges.push({ x1: parent.x, y1: parent.y, x2: x, y2: y });
        if (depth < maxDrawDepth && val > 1) {
          const mid = (lo + hi) / 2;
          place(depth + 1, val - 1, lo, mid, node);
          place(depth + 1, val - 2, mid, hi, node);
        }
      };
      place(0, n, X0, X1, null);
    }
    const truncated = functionType === "fibonacci" && n > 5;

    return (
      <>
        <TaskStrip tasks={tasks} cur={task} setCur={setTask} done={done} goal={t && t.goal} />
        <svg viewBox="0 0 600 300" style={{ width: "100%", background: SVG_BG, border: `1px solid ${C.line}`, borderRadius: 8 }}>
          {edges.map((e, i) => <line key={"e" + i} x1={e.x1} y1={e.y1} x2={e.x2} y2={e.y2} stroke={C.faint} strokeWidth="1.2" />)}
          {nodes.map((nd, i) => (
            <g key={"n" + i}>
              <circle cx={nd.x} cy={nd.y} r={11} fill={nd.base ? C.panel2 : col} opacity={nd.base ? 1 : 0.85} stroke={C.ink} strokeWidth="1" />
              <text x={nd.x} y={nd.y + 3.5} textAnchor="middle" fill={nd.base ? C.mute : "#0a1c2a"} fontSize="10" fontFamily="monospace">{nd.label}</text>
            </g>
          ))}
          <text x={X0} y={Y1 + 30} fill={C.mute} fontSize="11" fontFamily="monospace">{FUNC_LABEL[functionType]} · {fld.calls} calls · depth {fld.depth} → {fld.returnValue}</text>
          {truncated && <text x={X1} y={Y1 + 30} textAnchor="end" fill={C.amber} fontSize="10.5" fontFamily="monospace">tree truncated — true count: {fld.calls} calls</text>}
        </svg>
        <div style={{ marginTop: 10, padding: "8px 12px", borderRadius: 6, background: C.panel, border: `1px solid ${fld.isExponential ? C.crimson : C.line}`, color: fld.isExponential ? C.crimson : C.mute, fontSize: 12.5, fontFamily: "monospace" }}>
          {FUNC_LABEL[functionType]} makes {fld.calls} calls (depth {fld.depth}). {fld.isExponential ? "⚠ exponential call tree — calls explode with n." : "linear recursion — calls grow as n+1."}
        </div>
        <div style={{ display: "flex", gap: 6, marginTop: 10 }}>
          {FUNCS.map((f) => (
            <button key={f} onClick={() => { setFunctionType(f); event("selected", `Function = ${FUNC_LABEL[f]}`, { response: `${compute({ functionType: f, n }).calls} calls` }, FUNC_COL[f]); }}
              style={{ flex: 1, padding: "8px 6px", borderRadius: 6, cursor: "pointer", fontFamily: "monospace", fontSize: 13,
                background: functionType === f ? FUNC_COL[f] : C.panel2, color: functionType === f ? "#0a1c2a" : C.mute, fontWeight: functionType === f ? 600 : 400,
                border: `1px solid ${functionType === f ? FUNC_COL[f] : C.line}` }}>{FUNC_LABEL[f]}</button>
          ))}
        </div>
        <Slider label="input n" value={n} set={setN} min={0} max={20} step={1} color={col} onUp={() => event("adjusted", `Set n = ${n}`, { response: `${fld.calls} calls · depth ${fld.depth}` }, C.blue)} />
        <div style={{ display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 8, marginTop: 12 }}>
          <StatMeter label="calls" v={fld.calls} d={0} color={fld.isExponential ? C.crimson : C.teal} />
          <StatMeter label="depth" v={fld.depth} d={0} color={C.amber} />
          <StatMeter label="returnValue" v={fld.returnValue} d={0} color={C.blue} />
          <StatMeter label="n" v={fld.n} d={0} color={C.mute} />
        </div>
      </>
    );
  }

  window.RecursionTutor = K.makeTutor(RecursionFig, { moduleLabel: "Recursion bench", benchId: "recursion" });
})();
