/* curvesketchlab.jsx — CurveSketchTutor: the curve-analysis bench. Built on
   window.BenchKit (harness) + window.BenchFields (shared field math), so this file is just the
   FIGURE. computeFields = window.BenchFields.curvesketch — the SAME function
   server/benches/curvesketch.js calls, so client and server fields are identical by construction.
   The learner sets the coefficient b and moves a point x along the cubic f(x) = x³ + b·x; the
   tangent slope (f′), the concavity (f″), the critical points (where 3x²+b = 0) and the inflection
   at x = 0 are all made visible as the point moves. */
(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.curvesketch(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 CurveSketchFig({ task, setTask, tasks, report, event, done }) {
    const [b, setB] = useState(-3);
    const [x, setX] = useState(1);
    const fld = useMemo(() => compute({ b, x }), [b, x]);
    useEffect(() => { report(fld); }, [b, x]); // eslint-disable-line

    // plot f(x) = x³ + b·x over [-3,3], scaled to fit the SVG.
    const X0 = 60, X1 = 380, midY = 150, plotW = X1 - X0;
    const xLo = -3, xHi = 3;
    const f = (xx) => xx * xx * xx + b * xx;
    // y-extent over the domain (sample) to scale vertically.
    let yMax = 0.0001;
    for (let xx = xLo; xx <= xHi + 1e-9; xx += 0.05) yMax = Math.max(yMax, Math.abs(f(xx)));
    const sx = (xx) => X0 + ((xx - xLo) / (xHi - xLo)) * plotW;
    const sy = (yy) => midY - (yy / yMax) * 110;

    // the curve path
    let d = "";
    for (let xx = xLo, first = true; xx <= xHi + 1e-9; xx += 0.05) {
      d += `${first ? "M" : "L"} ${sx(xx).toFixed(1)} ${sy(f(xx)).toFixed(1)} `;
      first = false;
    }

    // the movable point + a short tangent segment (slope = f′ = 3x²+b)
    const px = sx(x), py = sy(f(x));
    const slope = fld.slope;                 // df/dx in data units
    // pixel slope: dY_px/dX_px. dX_px = (1/(xHi-xLo))*plotW per unit x; dY_px = -(1/yMax)*110 per unit y.
    const dXpx = plotW / (xHi - xLo), dYpx = -110 / yMax;
    const tanLen = 42;
    const tdx = dXpx, tdy = slope * dYpx;     // direction (px) along the tangent
    const tmag = Math.hypot(tdx, tdy) || 1;
    const ux = (tdx / tmag) * tanLen, uy = (tdy / tmag) * tanLen;

    // critical points where 3x²+b = 0  ⇒  x = ±√(−b/3)  (real only when b<0)
    const crit = [];
    if (b < 0) { const xc = Math.sqrt(-b / 3); crit.push(xc, -xc); }

    const t = tasks.find((q) => q.id === task) || tasks[0];
    const slopeColor = fld.isCritical ? C.teal : (slope > 0 ? C.gold : C.amber);
    const concColor = fld.concaveUp ? C.blue : C.amber;

    const status = fld.isInflection
      ? "Inflection point — f″ = 0, concavity is changing here (x ≈ 0)."
      : fld.isCritical
        ? (fld.concaveUp ? "Critical point — f′ = 0 and concave up: a local minimum." : "Critical point — f′ = 0 and concave down: a local maximum.")
        : `${slope > 0 ? "Increasing" : "Decreasing"} (f′ ${slope > 0 ? ">" : "<"} 0) · ${fld.concaveUp ? "concave up" : "concave down"} (f″ ${fld.concaveUp ? ">" : "<"} 0).`;

    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={midY} x2={X1 + 4} y2={midY} stroke={C.faint} strokeWidth="1.1" strokeDasharray="4 3" />
          <line x1={sx(0)} y1={36} x2={sx(0)} y2={264} stroke={C.faint} strokeWidth="1.1" strokeDasharray="4 3" />
          {/* the cubic */}
          <path d={d} fill="none" stroke={C.gold} strokeWidth="2" />
          {/* critical-point markers */}
          {crit.map((xc, i) => (
            <circle key={i} cx={sx(xc).toFixed(1)} cy={sy(f(xc)).toFixed(1)} r="4.5" fill="none" stroke={C.teal} strokeWidth="1.6" />
          ))}
          {/* inflection at x = 0 */}
          <circle cx={sx(0).toFixed(1)} cy={sy(f(0)).toFixed(1)} r="3.2" fill={C.blue} fillOpacity="0.85" />
          {/* tangent segment at the movable point (slope = f′) */}
          <line x1={(px - ux).toFixed(1)} y1={(py - uy).toFixed(1)} x2={(px + ux).toFixed(1)} y2={(py + uy).toFixed(1)} stroke={slopeColor} strokeWidth="1.8" />
          {/* concavity indicator: a small up/down chevron near the point */}
          <path d={fld.concaveUp ? `M ${px - 9} ${py + 22} Q ${px} ${py + 14} ${px + 9} ${py + 22}` : `M ${px - 9} ${py - 22} Q ${px} ${py - 14} ${px + 9} ${py - 22}`} fill="none" stroke={concColor} strokeWidth="1.6" />
          {/* the movable point */}
          <circle cx={px.toFixed(1)} cy={py.toFixed(1)} r="5" fill={slopeColor} stroke={C.line} strokeWidth="1" />
          {/* readout panel on the right */}
          <text x={X1 + 36} y={40} fill={C.mute} fontSize="12" fontFamily="monospace">f(x) = x³ + b·x</text>
          <text x={X1 + 36} y={66} fill={C.gold} fontSize="13" fontFamily="monospace">f(x) = {fmt(fld.fValue)}</text>
          <text x={X1 + 36} y={88} fill={slopeColor} fontSize="13" fontFamily="monospace">f′(x) = {fmt(fld.slope)}</text>
          <text x={X1 + 36} y={110} fill={concColor} fontSize="13" fontFamily="monospace">f″(x) = {fmt(fld.curvature)}</text>
          <text x={X1 + 36} y={132} fill={C.mute} fontSize="11" fontFamily="monospace">{fld.isCritical ? "critical" : fld.isInflection ? "inflection" : (fld.concaveUp ? "concave up" : "concave down")}</text>
        </svg>
        <div style={{ marginTop: 10, padding: "8px 12px", borderRadius: 6, background: C.panel, border: `1px solid ${fld.isCritical ? C.teal : (fld.isInflection ? C.blue : C.line)}`, color: fld.isCritical ? C.teal : (fld.isInflection ? C.blue : C.mute), fontSize: 12.5 }}>
          <K.T>{status}</K.T>
        </div>
        <Slider label="coefficient b" value={b} set={setB} min={-6} max={6} step={0.5} unit="" color={C.amber} onUp={() => event("adjusted", `Set b = ${fmt(b)}`, { response: `f′ ${fmt(fld.slope)}` }, C.amber)} />
        <Slider label="point x" value={x} set={setX} min={-3} max={3} step={0.25} unit="" color={C.gold} onUp={() => event("adjusted", `Set x = ${fmt(x)}`, { response: `f′ ${fmt(fld.slope)} · f″ ${fmt(fld.curvature)}` }, C.gold)} />
        <div style={{ display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 8, marginTop: 12 }}>
          <StatMeter label="f(x)" v={fld.fValue} d={2} color={C.gold} />
          <StatMeter label="slope f′" v={fld.slope} d={2} color={slopeColor} />
          <StatMeter label="curvature f″" v={fld.curvature} d={2} color={concColor} />
          <StatMeter label="concave up" v={fld.concaveUp ? 1 : 0} d={0} color={fld.concaveUp ? C.blue : C.amber} />
        </div>
      </>
    );
  }

  window.CurveSketchTutor = K.makeTutor(CurveSketchFig, { moduleLabel: "Curve Analysis bench", benchId: "curvesketch" });
})();
