/* forceslab.jsx — ForcesTutor: the block-on-a-friction-incline bench. Built on
   window.BenchKit (harness) + window.BenchFields (shared field math), so this file is
   just the FIGURE. computeFields = window.BenchFields.forces — the SAME function
   server/benches/forces.js calls, so client and server fields are identical by
   construction. Follows the gas-laws TEMPLATE: 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.forces(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 ForcesFig({ task, setTask, tasks, report, event, done }) {
    const [mass, setMass] = useState(5);
    const [inclineAngle, setInclineAngle] = useState(30);
    const [mu, setMu] = useState(0.2);
    const fld = useMemo(() => compute({ mass, inclineAngle, mu }), [mass, inclineAngle, mu]);
    useEffect(() => { report(fld); }, [mass, inclineAngle, mu]); // eslint-disable-line

    // incline: a right triangle with apex at the left, base along the bottom.
    const th = inclineAngle * Math.PI / 180;
    const BX = 60, BY = 250;            // bottom-left corner (the angle θ vertex)
    const RUN = 420;                    // horizontal run of the slope
    const TX = BX + RUN, TY = BY - RUN * Math.tan(th);   // top of the slope (right)
    // block sits on the slope at ~60% up the run
    const f = 0.55;
    const cx = BX + RUN * f, cy = BY - RUN * f * Math.tan(th);
    const ux = Math.cos(th), uy = -Math.sin(th);        // unit vector up the slope
    const nx = Math.sin(th), ny = Math.cos(th);         // unit normal (away from surface, pointing up-left)
    // block as a small square sitting on the surface (offset along +normal)
    const half = 20, lift = 16;
    const bx = cx + nx * lift, by = cy - ny * lift;     // block centre, lifted off surface
    const blockPts = [
      [bx + ux * half + nx * half, by - uy * half - ny * half],
      [bx - ux * half + nx * half, by + uy * half - ny * half],
      [bx - ux * half - nx * half, by + uy * half + ny * half],
      [bx + ux * half - nx * half, by - uy * half + ny * half],
    ].map((p) => p.join(",")).join(" ");

    // arrows (scaled), drawn from the block centre
    const S = 1.4;                       // px per newton (scaled down for big forces)
    const arrow = (dx, dy, len, color, label) => {
      const ex = bx + dx * len, ey = by + dy * len;
      const ang = Math.atan2(ey - by, ex - bx);
      const ah = 7;
      return { x2: ex, y2: ey, color, label, ang, ah, lx: ex + dx * 10, ly: ey + dy * 10 };
    };
    const wLen = Math.min(80, fld.weight * S);          // weight straight down
    const nLen = Math.min(80, fld.normal * S);          // normal along +normal
    const fLen = Math.min(80, fld.friction * S);        // friction up the slope (if not sliding) / down-slope opposing motion
    const netLen = Math.min(80, fld.netForce * S);      // net force down the slope
    const aW = arrow(0, 1, wLen, C.gold, "mg");
    const aN = arrow(nx, -ny, nLen, C.blue, "N");
    const aF = arrow(ux, uy, fLen, C.teal, "f");        // up-slope
    const aNet = fld.netForce > 0.01 ? arrow(-ux, -uy, netLen, C.crimson, "F_net") : null;  // down-slope

    const drawArrow = (a, key) => a && (
      <g key={key}>
        <line x1={bx} y1={by} x2={a.x2} y2={a.y2} stroke={a.color} strokeWidth="2.5" />
        <polygon points={`${a.x2},${a.y2} ${a.x2 - a.ah * Math.cos(a.ang - 0.4)},${a.y2 - a.ah * Math.sin(a.ang - 0.4)} ${a.x2 - a.ah * Math.cos(a.ang + 0.4)},${a.y2 - a.ah * Math.sin(a.ang + 0.4)}`} fill={a.color} />
        <text x={a.lx} y={a.ly} fill={a.color} fontSize="11" fontFamily="monospace" textAnchor="middle">{a.label}</text>
      </g>
    );

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

    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 }}>
          {/* ground */}
          <line x1={BX} y1={BY} x2={TX} y2={BY} stroke={C.faint} strokeWidth="1.5" />
          {/* incline triangle */}
          <polygon points={`${BX},${BY} ${TX},${BY} ${TX},${TY}`} fill={C.teal} opacity="0.06" stroke={C.faint} strokeWidth="1.5" />
          <line x1={BX} y1={BY} x2={TX} y2={TY} stroke={C.faint} strokeWidth="2" />
          {/* angle label */}
          <text x={BX + 34} y={BY - 6} fill={C.mute} fontSize="11" fontFamily="monospace">θ = {Math.round(inclineAngle)}°</text>
          {/* block */}
          <polygon points={blockPts} fill={sliding ? C.crimson : C.gold} opacity="0.85" stroke={C.line} strokeWidth="1" />
          {/* force arrows */}
          {drawArrow(aW, "w")}
          {drawArrow(aN, "n")}
          {drawArrow(aF, "f")}
          {drawArrow(aNet, "net")}
          <text x={BX} y={28} fill={C.mute} fontSize="11" fontFamily="monospace">m = {fmt(mass)} kg · θ = {Math.round(inclineAngle)}° · μ = {fmt(mu)}</text>
          <text x={BX} y={46} fill={sliding ? C.crimson : C.teal} fontSize="12" fontFamily="monospace">{sliding ? `sliding · a = ${fmt(fld.accel)} m/s²` : "static — friction holds it"}</text>
        </svg>
        <div style={{ marginTop: 10, padding: "8px 12px", borderRadius: 6, background: C.panel, border: `1px solid ${sliding ? C.crimson : C.line}`, color: sliding ? C.crimson : C.mute, fontSize: 12.5 }}>
          F_net = mg·sinθ − μN = {fmt(fld.netForce)} N · a = F_net/m = {fmt(fld.accel)} m/s². {sliding ? "⚠ block is sliding." : "Steepen θ or lower μ to make it slide."}
        </div>
        <Slider label="mass m" value={mass} set={setMass} min={0.5} max={20} step={0.5} unit="kg" color={C.gold} onUp={() => event("adjusted", `Set m = ${fmt(mass)} kg`, { response: `a ${fmt(fld.accel)} m/s²` }, C.gold)} />
        <Slider label="incline angle θ" value={inclineAngle} set={setInclineAngle} min={0} max={60} step={1} unit="°" color={C.crimson} onUp={() => event("adjusted", `Set θ = ${Math.round(inclineAngle)}°`, { response: `a ${fmt(fld.accel)} m/s²` }, C.crimson)} />
        <Slider label="friction μ" value={mu} set={setMu} min={0} max={1} step={0.05} unit="" color={C.blue} onUp={() => event("adjusted", `Set μ = ${fmt(mu)}`, { response: `a ${fmt(fld.accel)} m/s²` }, C.blue)} />
        <div style={{ display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 8, marginTop: 12 }}>
          <StatMeter label="net force" v={fld.netForce} d={2} unit="N" color={sliding ? C.crimson : C.teal} />
          <StatMeter label="acceleration" v={fld.accel} d={2} unit="m/s²" color={C.crimson} />
          <StatMeter label="normal N" v={fld.normal} d={1} unit="N" color={C.blue} />
          <StatMeter label="friction f" v={fld.friction} d={2} unit="N" color={C.teal} />
        </div>
      </>
    );
  }

  window.ForcesTutor = K.makeTutor(ForcesFig, { moduleLabel: "Forces bench", benchId: "forces" });
})();
