/* linearlab.jsx — LinearTutor: the draggable-line bench. Built on window.BenchKit
   (harness) + window.BenchFields (shared field math), so this file is just the FIGURE.
   computeFields = window.BenchFields.linear — the SAME function server/benches/linear.js
   calls, so client and server fields are identical by construction. Follows the
   single-figure lab TEMPLATE (public/gaslawslab.jsx): sliders → 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.linear(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 }}>{label}</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 LinearFig({ task, setTask, tasks, report, event, done }) {
    const [slope, setSlope] = useState(2);
    const [intercept, setIntercept] = useState(-3);
    const [xEval, setXEval] = useState(2);
    const fld = useMemo(() => compute({ slope, intercept, xEval }), [slope, intercept, xEval]);
    useEffect(() => { report(fld); }, [slope, intercept, xEval]); // eslint-disable-line

    // coordinate-plane mapping: math x∈[-10,10] → px [0,600]; math y∈[-10,10] → px [300,0].
    const W = 600, H = 300, MIN = -10, MAX = 10;
    const sx = (x) => ((x - MIN) / (MAX - MIN)) * W;
    const sy = (y) => H - ((y - MIN) / (MAX - MIN)) * H;
    const Y = (x) => slope * x + intercept; // math y = mx + b
    // line endpoints across the full view in math-x.
    const p0 = { x: sx(MIN), y: sy(Y(MIN)) };
    const p1 = { x: sx(MAX), y: sy(Y(MAX)) };
    // gridlines every 2 units.
    const grid = [];
    for (let g = MIN + 2; g < MAX; g += 2) grid.push(g);

    const t = tasks.find((x) => x.id === task) || tasks[0];
    const dec = !fld.isIncreasing;

    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 }}>
          {/* gridlines */}
          {grid.map((g, i) => <line key={"gv" + i} x1={sx(g)} y1={0} x2={sx(g)} y2={H} stroke={C.line} strokeWidth="0.5" opacity="0.5" />)}
          {grid.map((g, i) => <line key={"gh" + i} x1={0} y1={sy(g)} x2={W} y2={sy(g)} stroke={C.line} strokeWidth="0.5" opacity="0.5" />)}
          {/* axes through the centre */}
          <line x1={0} y1={sy(0)} x2={W} y2={sy(0)} stroke={C.faint} strokeWidth="1.5" />
          <line x1={sx(0)} y1={0} x2={sx(0)} y2={H} stroke={C.faint} strokeWidth="1.5" />
          {/* the line y = mx + b */}
          <line x1={p0.x} y1={p0.y} x2={p1.x} y2={p1.y} stroke={dec ? C.crimson : C.teal} strokeWidth="2.5" />
          {/* y-intercept dot */}
          <circle cx={sx(0)} cy={sy(intercept)} r={5} fill={C.gold} stroke={SVG_BG} strokeWidth="1.5" />
          {/* x-intercept dot (only when finite/in-view) */}
          {Number.isFinite(fld.xIntercept) && fld.xIntercept >= MIN && fld.xIntercept <= MAX
            ? <circle cx={sx(fld.xIntercept)} cy={sy(0)} r={5} fill={C.blue} stroke={SVG_BG} strokeWidth="1.5" />
            : null}
          {/* (xEval, yAt) read-off point */}
          <circle cx={sx(xEval)} cy={sy(fld.yAt)} r={5} fill={C.ink} stroke={dec ? C.crimson : C.teal} strokeWidth="2" />
          <text x={8} y={16} fill={C.mute} fontSize="11" fontFamily="monospace">y = {fmt(slope)}·x {intercept >= 0 ? "+ " + fmt(intercept) : "− " + fmt(-intercept)}</text>
          <text x={sx(xEval) + 8} y={sy(fld.yAt) - 8} fill={C.ink} fontSize="11" fontFamily="monospace">({fmt(xEval)}, {fmt(fld.yAt)})</text>
          <text x={(p0.x + p1.x) / 2 + 6} y={(p0.y + p1.y) / 2 - 6} fill={dec ? C.crimson : C.teal} fontSize="11" fontFamily="monospace">slope {fmt(slope)}</text>
        </svg>
        <div style={{ marginTop: 10, padding: "8px 12px", borderRadius: 6, background: C.panel, border: `1px solid ${fld.isSteep ? C.gold : C.line}`, color: fld.isSteep ? C.gold : C.mute, fontSize: 12.5 }}>
          y = mx + b · slope = {fmt(slope)}, b = {fmt(intercept)}, y({fmt(xEval)}) = {fmt(fld.yAt)}. {dec ? "Decreasing line." : "Increasing line."}{fld.isSteep ? " ⚠ steep slope." : ""}
        </div>
        <Slider label="slope m" value={slope} set={setSlope} min={-5} max={5} step={0.5} color={C.teal} onUp={() => event("adjusted", `Set m = ${fmt(slope)}`, { response: `y(${fmt(xEval)}) = ${fmt(fld.yAt)}` }, C.teal)} />
        <Slider label="y-intercept b" value={intercept} set={setIntercept} min={-10} max={10} step={1} color={C.gold} onUp={() => event("adjusted", `Set b = ${fmt(intercept)}`, { response: `x-int ${fmt(fld.xIntercept)}` }, C.gold)} />
        <Slider label="read y at x" value={xEval} set={setXEval} min={-10} max={10} step={1} color={C.blue} onUp={() => event("adjusted", `Read at x = ${fmt(xEval)}`, { response: `y = ${fmt(fld.yAt)}` }, C.blue)} />
        <div style={{ display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 8, marginTop: 12 }}>
          <StatMeter label="slope m" v={fld.slope} color={dec ? C.crimson : C.teal} />
          <StatMeter label="y-intercept b" v={fld.intercept} color={C.gold} />
          <StatMeter label="y at x" v={fld.yAt} color={C.ink} />
          <StatMeter label="x-intercept" v={fld.xIntercept} color={C.blue} />
        </div>
      </>
    );
  }

  window.LinearTutor = K.makeTutor(LinearFig, { moduleLabel: "Linear functions bench", benchId: "linear" });
})();
