/* partialslab.jsx — PartialsTutor: the partial derivatives bench. Built on window.BenchKit
   (harness) + window.BenchFields (shared field math), so this file is just the FIGURE.
   computeFields = window.BenchFields.partials — the SAME function server/benches/partials.js
   calls, so client and server fields are identical by construction. The learner moves a point
   (x, y) across the surface z = x² + y² − c·x and tilts it with c; a contour plot (top-down view)
   plus a gradient arrow through the point make the two partials and steepest ascent visible. */
(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.partials(s);

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

  function PartialsFig({ task, setTask, tasks, report, event, done }) {
    const [x, setX] = useState(1);
    const [y, setY] = useState(1);
    const [c, setC] = useState(0);
    const fld = useMemo(() => compute({ x, y, c }), [x, y, c]);
    useEffect(() => { report(fld); }, [x, y, c]); // eslint-disable-line

    // plot window: a top-down (x,y) view, x,y ∈ [-3,3]; map math coords → svg px.
    const X0 = 60, X1 = 560, Y0 = 30, Y1 = 270;
    const XMIN = -3, XMAX = 3, YMIN = -3, YMAX = 3;
    const sx = (mx) => X0 + ((mx - XMIN) / (XMAX - XMIN)) * (X1 - X0);
    const sy = (my) => Y1 - ((my - YMIN) / (YMAX - YMIN)) * (Y1 - Y0); // y up

    // the critical point (c/2, 0) — centre of the level curves.
    const cx0 = c / 2, cy0 = 0;
    // level curves of z = x² + y² − c·x = (x − c/2)² + y² − c²/4 are circles centred at (c/2,0).
    const radii = [0.5, 1, 1.5, 2, 2.5];

    // the moved point + gradient arrow (direction steepDir, length ∝ gradMag) in screen px.
    const px = sx(x), py = sy(y);
    const dirRad = fld.steepDir * Math.PI / 180;
    const arrowLen = Math.min(70, 8 + fld.gradMag * 8); // px, capped
    const ax = px + Math.cos(dirRad) * arrowLen;
    const ay = py - Math.sin(dirRad) * arrowLen; // screen y is inverted
    // arrowhead
    const ah = 7, aw = 0.42;
    const hx1 = ax - Math.cos(dirRad - aw) * ah, hy1 = ay + Math.sin(dirRad - aw) * ah;
    const hx2 = ax - Math.cos(dirRad + aw) * ah, hy2 = ay + Math.sin(dirRad + aw) * ah;

    const t = tasks.find((q) => q.id === task) || tasks[0];
    const ptColor = fld.atCritical ? C.teal : C.amber;

    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 }}>
          {/* axes */}
          <line x1={X0} y1={sy(0)} x2={X1} y2={sy(0)} stroke={C.faint} strokeWidth="1" />
          <line x1={sx(0)} y1={Y0} x2={sx(0)} y2={Y1} stroke={C.faint} strokeWidth="1" />
          <text x={X1 - 6} y={sy(0) - 5} fill={C.faint} fontSize="10" fontFamily="monospace" textAnchor="end">x</text>
          <text x={sx(0) + 6} y={Y0 + 12} fill={C.faint} fontSize="10" fontFamily="monospace">y</text>
          {/* level curves (circles centred at the critical point) — top-down contour plot */}
          {radii.map((r) => (
            <ellipse key={r} cx={sx(cx0)} cy={sy(cy0)} rx={(r / (XMAX - XMIN)) * (X1 - X0)} ry={(r / (YMAX - YMIN)) * (Y1 - Y0)} fill="none" stroke={C.blue} strokeWidth="1" opacity="0.5" />
          ))}
          {/* the critical point (c/2, 0) */}
          <circle cx={sx(cx0)} cy={sy(cy0)} r={4} fill="none" stroke={C.teal} strokeWidth="1.5" />
          <text x={sx(cx0) + 7} y={sy(cy0) + 14} fill={C.teal} fontSize="10" fontFamily="monospace">∇z = 0</text>
          {/* gradient arrow from the moved point */}
          <line x1={px} y1={py} x2={ax} y2={ay} stroke={C.crimson} strokeWidth="2.5" />
          <polygon points={`${ax},${ay} ${hx1},${hy1} ${hx2},${hy2}`} fill={C.crimson} />
          {/* the moved point */}
          <circle cx={px} cy={py} r={5} fill={ptColor} stroke={C.ink} strokeWidth="1" />
          <text x={px + 9} y={py - 8} fill={ptColor} fontSize="12" fontFamily="monospace">∇z = ({fmt(fld.fx)}, {fmt(fld.fy)})</text>
          <text x={X0 + 4} y={Y0 + 14} fill={C.mute} fontSize="11" fontFamily="monospace">z = x² + y² − {fmt(c)}·x · ({fmt(x)}, {fmt(y)}) · z = {fmt(fld.zValue)}</text>
        </svg>
        <div style={{ marginTop: 10, padding: "8px 12px", borderRadius: 6, background: C.panel, border: `1px solid ${fld.atCritical ? C.teal : C.line}`, color: fld.atCritical ? C.teal : C.mute, fontSize: 12.5 }}>
          ∂z/∂x = 2x − {fmt(c)} = {fmt(fld.fx)}, ∂z/∂y = 2y = {fmt(fld.fy)}. {fld.atCritical ? <>⚑ <K.T>critical point — the gradient is zero.</K.T></> : <><K.T>Gradient magnitude</K.T> ‖∇z‖ = {fmt(fld.gradMag)} <K.T>pointing</K.T> {fmt(fld.steepDir)}°.</>}
        </div>
        <Slider label="point x" value={x} set={setX} min={-3} max={3} step={0.1} unit="" color={C.teal} onUp={() => event("adjusted", `Moved to x = ${fmt(x)}`, { response: `∂z/∂x ${fmt(fld.fx)}` }, C.teal)} />
        <Slider label="point y" value={y} set={setY} min={-3} max={3} step={0.1} unit="" color={C.blue} onUp={() => event("adjusted", `Moved to y = ${fmt(y)}`, { response: `∂z/∂y ${fmt(fld.fy)}` }, C.blue)} />
        <Slider label="x-tilt c" value={c} set={setC} min={-4} max={4} step={0.5} unit="" color={C.gold} onUp={() => event("adjusted", `Set c = ${fmt(c)}`, { response: `∇z ‖${fmt(fld.gradMag)}‖` }, C.gold)} />
        <div style={{ display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 8, marginTop: 12 }}>
          <StatMeter label="∂z/∂x" v={fld.fx} d={2} color={C.teal} />
          <StatMeter label="∂z/∂y" v={fld.fy} d={2} color={C.blue} />
          <StatMeter label="‖∇z‖" v={fld.gradMag} d={2} color={C.crimson} />
          <StatMeter label="z" v={fld.zValue} d={2} color={C.amber} />
        </div>
      </>
    );
  }

  window.PartialsTutor = K.makeTutor(PartialsFig, { moduleLabel: "Partial Derivatives bench", benchId: "partials" });
})();
