/* gradientlab.jsx — GradientTutor: the gradient & directional-derivative bench. Built on
   window.BenchKit (harness) + window.BenchFields (shared field math), so this file is just the
   FIGURE. computeFields = window.BenchFields.gradient — the SAME function
   server/benches/gradient.js calls, so client and server fields are identical by construction.
   The learner places a point (x,y) on the bowl f = x²+y² and aims a direction φ; the contour map,
   the gradient arrow (steepest ascent) and the chosen-direction arrow make the directional
   derivative Dᵤf = ∇f·u visible against the maximum rate |∇f|. */
(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, Scene3D = K.Scene3D;
  const compute = (s) => window.BenchFields.gradient(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 GradientFig({ task, setTask, tasks, report, event, done }) {
    const [x, setX] = useState(1);
    const [y, setY] = useState(1);
    const [phi, setPhi] = useState(45);
    const [az, setAz] = useState(42);
    const [el, setEl] = useState(24);
    const fld = useMemo(() => compute({ x, y, phi }), [x, y, phi]);
    useEffect(() => { report(fld); }, [x, y, phi]); // eslint-disable-line

    const zAt = (X, Y) => X * X + Y * Y;            // the bowl surface
    const pz = zAt(x, y);
    const arrowColor = fld.aligned ? C.teal : (fld.isLevel ? C.gold : C.amber);

    // frame the surface over [-3,3]² plus the base plane and the point
    const fit = [];
    for (const xx of [-3, 3]) for (const yy of [-3, 3]) { fit.push([xx, yy, zAt(xx, yy)]); fit.push([xx, yy, 0]); }
    fit.push([x, y, pz]);

    const build = (screen) => {
      // base plane + axes (drawn first, behind the surface)
      let out = K.poly3d(screen, [[-3, -3, 0], [3, -3, 0], [3, 3, 0], [-3, 3, 0]], "rgba(108,182,255,0.05)", C.line, 0.7);
      out += K.seg3d(screen, [-3, 0, 0], [3.2, 0, 0], C.faint, 0.8) + K.text3d(screen, [3.5, 0, 0], "x", C.faint);
      out += K.seg3d(screen, [0, -3, 0], [0, 3.2, 0], C.faint, 0.8) + K.text3d(screen, [0, 3.5, 0], "y", C.faint);
      // the surface mesh z = x²+y², height-coloured + Lambert-shaded
      out += K.surfaceMesh3d(screen, zAt, -3, 3, -3, 3, { n: 24 });
      // vertical drop from the surface point down to the base
      out += K.seg3d(screen, [x, y, 0], [x, y, pz], C.faint, 1, "3 3");
      out += K.dot3d(screen, [x, y, 0], 2.5, C.faint);
      // the directional-derivative slice ON the surface: slope = Dᵤf along u=(cosφ,sinφ)
      const phr = phi * Math.PI / 180, cf = Math.cos(phr), sf = Math.sin(phr), d = 0.95;
      out += K.seg3d(screen, [x - d * cf, y - d * sf, pz - fld.dirDeriv * d], [x + d * cf, y + d * sf, pz + fld.dirDeriv * d], arrowColor, 2.6);
      // gradient arrow on the base plane (direction of steepest ascent)
      const g = fld.gradMag || 1;
      if (fld.gradMag > 0.05) { const L = Math.min(2.4, 0.5 + fld.gradMag * 0.4); out += K.vec3d(screen, [x, y, 0], [x + (fld.fx / g) * L, y + (fld.fy / g) * L, 0], C.crimson, 2.5); }
      // chosen-direction arrow on the base plane (unit u=(cosφ,sinφ))
      out += K.vec3d(screen, [x, y, 0], [x + cf * 2.0, y + sf * 2.0, 0], arrowColor, 2.5);
      // the moved point on the surface
      out += K.dot3d(screen, [x, y, pz], 5, arrowColor, C.ink);
      return out;
    };

    const t = tasks.find((q) => q.id === task) || tasks[0];

    return (
      <>
        <TaskStrip tasks={tasks} cur={task} setCur={setTask} done={done} goal={t && t.goal} />
        <Scene3D az={az} el={el} setAz={setAz} setEl={setEl} fit={fit} width={600} height={320} build={build}
          onUp={() => event("rotated", `View az ${Math.round(az)}° · el ${Math.round(el)}°`, { response: `Dᵤf ${fmt(fld.dirDeriv)}` }, C.blue)} />
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: 8, fontSize: 11.5, color: C.faint, fontFamily: "monospace" }}>
          <span><K.T>drag to orbit</K.T> · z = x² + y² · |∇f| = {fmt(fld.gradMag)}</span>
          <span style={{ color: arrowColor }}>Dᵤf = {fmt(fld.dirDeriv)}</span>
        </div>
        <div style={{ marginTop: 10, padding: "8px 12px", borderRadius: 6, background: C.panel, border: `1px solid ${fld.aligned ? C.teal : C.line}`, color: fld.aligned ? C.teal : C.mute, fontSize: 12.5 }}>
          Directional derivative Dᵤf = {fmt(fld.dirDeriv)} vs maximum rate |∇f| = {fmt(fld.gradMag)}. {fld.aligned ? <>⚑ <K.T>Aimed along the gradient — steepest ascent.</K.T></> : fld.isLevel ? <K.T>Along a level curve — f is instantaneously flat here.</K.T> : <K.T>Rotate φ toward the dashed gradient arrow to climb faster.</K.T>}
        </div>
        <Slider label="point x" value={x} set={setX} min={-3} max={3} step={0.25} unit="" color={C.teal} onUp={() => event("adjusted", `Set x = ${fmt(x)}`, { response: `gradMag ${fmt(fld.gradMag)}` }, C.teal)} />
        <Slider label="point y" value={y} set={setY} min={-3} max={3} step={0.25} unit="" color={C.gold} onUp={() => event("adjusted", `Set y = ${fmt(y)}`, { response: `gradMag ${fmt(fld.gradMag)}` }, C.gold)} />
        <Slider label="direction φ" value={phi} set={(v) => setPhi(Math.round(v))} min={0} max={360} step={5} unit="°" color={C.blue} onUp={() => event("adjusted", `Set φ = ${Math.round(phi)}°`, { response: `dirDeriv ${fmt(fld.dirDeriv)}` }, C.blue)} />
        <div style={{ display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 8, marginTop: 12 }}>
          <StatMeter label="∂f/∂x" v={fld.fx} d={2} color={C.blue} />
          <StatMeter label="∂f/∂y" v={fld.fy} d={2} color={C.gold} />
          <StatMeter label="|∇f|" v={fld.gradMag} d={2} color={C.teal} />
          <StatMeter label="Dᵤf" v={fld.dirDeriv} d={2} color={arrowColor} />
        </div>
      </>
    );
  }

  window.GradientTutor = K.makeTutor(GradientFig, { moduleLabel: "Gradient & Directional Derivative bench", benchId: "gradient" });
})();
