/* advgeometrylab.jsx — AdvGeometryTutor: the advanced (triangle-centre) geometry workbench.
   Three modules: circumcircle, incircle, Euler line / nine-point circle. Built on
   window.BenchKit (loaded first), so this file is just the FIGURES + their field computations.
   Each module drags the three vertices A,B,C; computeFields() MIRRORS
   server/benches/advgeometry.js simulate() EXACTLY (same 660×540 space, same kernel,
   same tolerances) so client and server agree on every graded field. The construction
   (centre/circle/Euler line) is computed; the learner drives the triangle to a target shape. */
(function () {
  const { useState, useMemo, useEffect, useRef } = React;
  const K = window.BenchKit, C = K.C, SVG_BG = K.SVG_BG, btn = K.btn, fmt = K.fmt;
  const TaskStrip = K.TaskStrip, StatMeter = K.StatMeter;

  /* ── kernel (mirrors the server kernel byte-for-byte) ── */
  const sub = (a, b) => ({ x: a.x - b.x, y: a.y - b.y });
  const add = (a, b) => ({ x: a.x + b.x, y: a.y + b.y });
  const mul = (a, s) => ({ x: a.x * s, y: a.y * s });
  const dot = (a, b) => a.x * b.x + a.y * b.y;
  const cross = (a, b) => a.x * b.y - a.y * b.x;
  const len = (a) => Math.hypot(a.x, a.y);
  const dist = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);
  const mid = (a, b) => ({ x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 });
  const r1 = (x) => Math.round(x * 10) / 10;
  const r2 = (x) => Math.round(x * 100) / 100;
  function angleAt(V, P, Q) { const a = sub(P, V), b = sub(Q, V); const c = dot(a, b) / ((len(a) * len(b)) || 1); return Math.acos(Math.max(-1, Math.min(1, c))) * 180 / Math.PI; }
  function triArea(A, B, Cc) { return Math.abs(cross(sub(B, A), sub(Cc, A))) / 2; }
  function circumcenter(A, B, Cc) {
    const d = 2 * (A.x * (B.y - Cc.y) + B.x * (Cc.y - A.y) + Cc.x * (A.y - B.y));
    if (Math.abs(d) < 1e-6) return { x: (A.x + B.x + Cc.x) / 3, y: (A.y + B.y + Cc.y) / 3 };
    const a2 = A.x * A.x + A.y * A.y, b2 = B.x * B.x + B.y * B.y, c2 = Cc.x * Cc.x + Cc.y * Cc.y;
    return {
      x: (a2 * (B.y - Cc.y) + b2 * (Cc.y - A.y) + c2 * (A.y - B.y)) / d,
      y: (a2 * (Cc.x - B.x) + b2 * (A.x - Cc.x) + c2 * (B.x - A.x)) / d,
    };
  }
  function distLine(P, A, B) { const ab = sub(B, A); const L = len(ab) || 1; return Math.abs(cross(ab, sub(P, A))) / L; }
  function foot(P, A, B) { const ab = sub(B, A); const t = dot(sub(P, A), ab) / (dot(ab, ab) || 1); return add(A, mul(ab, t)); }
  function pointInTri(P, A, B, Cc) {
    const s1 = cross(sub(B, A), sub(P, A)), s2 = cross(sub(Cc, B), sub(P, B)), s3 = cross(sub(A, Cc), sub(P, Cc));
    const hasNeg = s1 < 0 || s2 < 0 || s3 < 0, hasPos = s1 > 0 || s2 > 0 || s3 > 0;
    return !(hasNeg && hasPos);
  }
  function angles(A, B, Cc) { return { A: angleAt(A, B, Cc), B: angleAt(B, Cc, A), C: angleAt(Cc, A, B) }; }
  function incenter(A, B, Cc) { const a = dist(B, Cc), b = dist(Cc, A), c = dist(A, B), per = a + b + c || 1; return { x: (a * A.x + b * B.x + c * Cc.x) / per, y: (a * A.y + b * B.y + c * Cc.y) / per }; }

  /* ── field computations — EXACTLY the server's per-module simulate() ── */
  function fieldsCircum(A, B, Cc) {
    const ang = angles(A, B, Cc), maxAngle = Math.max(ang.A, ang.B, ang.C);
    const O = circumcenter(A, B, Cc), R = dist(O, A);
    const OA = dist(O, A), OB = dist(O, B), OC = dist(O, Cc);
    return {
      circumRadius: r1(R), OA: r1(OA), OB: r1(OB), OC: r1(OC),
      equidistant: (Math.max(OA, OB, OC) - Math.min(OA, OB, OC)) < 1.0,
      maxAngle: r1(maxAngle), isRight: Math.abs(maxAngle - 90) <= 2,
      isObtuse: maxAngle > 92, centerInside: pointInTri(O, A, B, Cc),
    };
  }
  function fieldsIncircle(A, B, Cc) {
    const ang = angles(A, B, Cc), maxAngle = Math.max(ang.A, ang.B, ang.C), minAngle = Math.min(ang.A, ang.B, ang.C);
    const a = dist(B, Cc), b = dist(Cc, A), c = dist(A, B), per = a + b + c || 1, s = per / 2;
    const I = incenter(A, B, Cc), area = triArea(A, B, Cc), r = area / (s || 1);
    const dBC = distLine(I, B, Cc), dCA = distLine(I, Cc, A), dAB = distLine(I, A, B);
    const spread = Math.max(dBC, dCA, dAB) - Math.min(dBC, dCA, dAB);
    return {
      inradius: r1(r), dBC: r1(dBC), dCA: r1(dCA), dAB: r1(dAB),
      tangentToAll: spread < 1.0 && area > 50, perimeter: r1(per), area: Math.round(area),
      minAngle: r1(minAngle), isEquilateral: (maxAngle - minAngle) <= 6,
    };
  }
  function fieldsEuler(A, B, Cc) {
    const ang = angles(A, B, Cc), maxAngle = Math.max(ang.A, ang.B, ang.C);
    const O = circumcenter(A, B, Cc), R = dist(O, A);
    const G = { x: (A.x + B.x + Cc.x) / 3, y: (A.y + B.y + Cc.y) / 3 };
    const H = { x: A.x + B.x + Cc.x - 2 * O.x, y: A.y + B.y + Cc.y - 2 * O.y };
    const OG = dist(O, G), GH = dist(G, H), ratio = OG > 1e-6 ? GH / OG : 0;
    return {
      OG: r1(OG), GH: r1(GH), ratioGH_OG: OG > 4 ? r2(ratio) : 0,
      circumRadius: r1(R), ninePointRadius: r1(R / 2),
      collinear: distLine(G, O, H) < 1.0 || OG < 4, eulerDegenerate: OG < 8,
      maxAngle: r1(maxAngle), isRight: Math.abs(maxAngle - 90) <= 2,
    };
  }

  /* ── shared figure chrome ── */
  const VB = "0 0 660 540";
  function svgPt(ref, e) { const r = ref.current.getBoundingClientRect(); return { x: (e.clientX - r.left) / r.width * 660, y: (e.clientY - r.top) / r.height * 540 }; }
  const clampV = (p) => ({ x: Math.max(45, Math.min(615, p.x)), y: Math.max(45, Math.min(495, p.y)) });
  function Grid() {
    const lines = [];
    for (let x = 0; x <= 660; x += 30) lines.push(<line key={`x${x}`} x1={x} y1={0} x2={x} y2={540} stroke="#163b50" strokeWidth={x % 150 === 0 ? 0.9 : 0.5} opacity={x % 150 === 0 ? 0.6 : 0.35} />);
    for (let y = 0; y <= 540; y += 30) lines.push(<line key={`y${y}`} x1={0} y1={y} x2={660} y2={y} stroke="#163b50" strokeWidth={y % 150 === 0 ? 0.9 : 0.5} opacity={y % 150 === 0 ? 0.6 : 0.35} />);
    return <g>{lines}</g>;
  }
  function Pt({ p, label, color, dx = 8, dy = -8, r = 4 }) {
    return <g><circle cx={p.x} cy={p.y} r={r} fill={color || C.ink} stroke={SVG_BG} strokeWidth="1.3" />{label ? <text x={p.x + dx} y={p.y + dy} fill={color || "#f4f1e8"} fontSize="15" fontStyle="italic" fontWeight="600" fontFamily="'IBM Plex Mono',monospace">{label}</text> : null}</g>;
  }
  function Seg({ a, b, color, w = 2, dash }) { return <line x1={a.x} y1={a.y} x2={b.x} y2={b.y} stroke={color || C.ink} strokeWidth={w} strokeLinecap="round" strokeDasharray={dash || "none"} />; }
  function Circle({ o, r, color, w = 1.6, dash }) { return <circle cx={o.x} cy={o.y} r={r} fill="none" stroke={color} strokeWidth={w} strokeDasharray={dash || "none"} />; }

  /* the draggable-triangle harness shared by all three modules. `construction(tri, fld)` draws
     the bench-specific geometry; the three vertex handles + drag are handled here. */
  function TriangleFig({ initial, compute, report, event, task, setTask, tasks, done, construction, status, meters }) {
    const [tri, setTri] = useState(initial);
    const [dragK, setDragK] = useState(null); // "A" | "B" | "C" | null
    const ref = useRef(null);
    const fld = useMemo(() => compute(tri.A, tri.B, tri.C), [tri]);
    useEffect(() => { report(fld); }, [tri]); // eslint-disable-line

    useEffect(() => {
      if (!dragK) return;
      const move = (e) => { const p = clampV(svgPt(ref, e)); setTri((t) => ({ ...t, [dragK]: p })); };
      const up = () => { setDragK(null); event("adjusted", `Moved vertex ${dragK}`, { response: status(compute(tri.A, tri.B, tri.C)).note }, C.blue); };
      window.addEventListener("pointermove", move); window.addEventListener("pointerup", up);
      return () => { window.removeEventListener("pointermove", move); window.removeEventListener("pointerup", up); };
    }, [dragK, tri]); // eslint-disable-line

    const t = tasks.find((x) => x.id === task) || tasks[0];
    const st = status(fld);
    return (
      <>
        <TaskStrip tasks={tasks} cur={task} setCur={setTask} done={done} goal={t && t.goal} />
        <svg ref={ref} viewBox={VB} style={{ width: "100%", background: SVG_BG, border: `1px solid ${C.line}`, borderRadius: 8, touchAction: "none", cursor: "crosshair" }}>
          <Grid />
          {construction(tri, fld)}
          {["A", "B", "C"].map((k) => (
            <g key={k} style={{ cursor: "grab" }} onPointerDown={() => setDragK(k)}>
              <circle cx={tri[k].x} cy={tri[k].y} r={11} fill={C.blue} opacity={dragK === k ? 0.95 : 0.45} />
              <Pt p={tri[k]} label={k} color={C.blue} dx={tri[k].x < 90 ? 10 : -16} dy={tri[k].y < 90 ? 22 : -12} r={4.6} />
            </g>
          ))}
        </svg>
        <div style={{ marginTop: 10, padding: "8px 12px", borderRadius: 6, background: C.panel, border: `1px solid ${st.ok ? C.gold : C.line}`, color: st.ok ? C.gold : C.mute, fontSize: 12.5 }}>
          {st.text} <K.T>Drag a vertex.</K.T>
        </div>
        <div style={{ display: "grid", gridTemplateColumns: `repeat(${meters(fld).length},1fr)`, gap: 8, marginTop: 12 }}>
          {meters(fld).map((m, i) => <StatMeter key={i} label={m.label} v={m.v} d={m.d} unit={m.unit} color={m.color} />)}
        </div>
      </>
    );
  }

  /* ===================== Module 1: Circumcircle & perpendicular bisectors ===================== */
  function CircumFig(props) {
    return <TriangleFig {...props}
      initial={{ A: { x: 330, y: 110 }, B: { x: 170, y: 430 }, C: { x: 500, y: 410 } }}
      compute={fieldsCircum}
      construction={(tri, fld) => {
        const { A, B, C: Cc } = tri, O = circumcenter(A, B, Cc), R = dist(O, A);
        const mAB = mid(A, B), mBC = mid(B, Cc), mCA = mid(Cc, A);
        return (<g>
          <Circle o={O} r={R} color={C.faint} w={1.5} />
          <Seg a={A} b={B} color={C.ink} /><Seg a={B} b={Cc} color={C.ink} /><Seg a={Cc} b={A} color={C.ink} />
          {[mAB, mBC, mCA].map((m, i) => <Seg key={i} a={m} b={O} color="#a98fd0" w={1.5} dash="5 4" />)}
          {[A, B, Cc].map((v, i) => <Seg key={`r${i}`} a={O} b={v} color={C.teal} w={1.2} dash="2 4" />)}
          <Pt p={O} label="O" color={fld.centerInside ? C.gold : C.crimson} dx={8} dy={-8} r={3.6} />
        </g>);
      }}
      status={(fld) => ({
        ok: fld.isRight,
        text: <>R = {fmt(fld.circumRadius)} · OA=OB=OC {fld.equidistant ? "✓" : ""} · {fld.isRight ? <K.T>right triangle (O on the hypotenuse).</K.T> : fld.isObtuse ? <K.T>obtuse (O outside).</K.T> : <K.T>acute (O inside).</K.T>}</>,
        note: `R ${fmt(fld.circumRadius)}, max∠ ${fmt(fld.maxAngle)}°`,
      })}
      meters={(fld) => [
        { label: "circumradius R", v: fld.circumRadius, color: C.gold },
        { label: "max angle", v: fld.maxAngle, unit: "°", color: fld.isRight ? C.gold : fld.isObtuse ? C.crimson : C.teal },
        { label: "OA / OB / OC", v: fld.equidistant ? "equal ✓" : "—", color: C.teal },
        { label: "centre O", v: fld.centerInside ? "inside" : "outside", color: fld.centerInside ? C.teal : C.amber },
      ]}
    />;
  }

  /* ===================== Module 2: Incircle & angle bisectors ===================== */
  function IncircleFig(props) {
    return <TriangleFig {...props}
      initial={{ A: { x: 330, y: 120 }, B: { x: 170, y: 420 }, C: { x: 500, y: 410 } }}
      compute={fieldsIncircle}
      construction={(tri, fld) => {
        const { A, B, C: Cc } = tri, I = incenter(A, B, Cc), r = fld.inradius;
        const fa = foot(I, B, Cc), fb = foot(I, Cc, A), fc = foot(I, A, B);
        return (<g>
          <Seg a={A} b={B} color={C.ink} /><Seg a={B} b={Cc} color={C.ink} /><Seg a={Cc} b={A} color={C.ink} />
          <Circle o={I} r={r} color={C.teal} w={1.9} />
          {[A, B, Cc].map((v, i) => <Seg key={i} a={v} b={I} color="#a98fd0" w={1.3} dash="5 4" />)}
          {[fa, fb, fc].map((f, i) => <circle key={`f${i}`} cx={f.x} cy={f.y} r={3} fill={C.gold} />)}
          <Pt p={I} label="I" color={C.gold} dx={8} dy={-8} r={3.6} />
        </g>);
      }}
      status={(fld) => ({
        ok: fld.tangentToAll,
        text: <>r = {fmt(fld.inradius)} · {fld.tangentToAll ? <K.T>tangent to all three sides ✓</K.T> : "…"} · {fld.isEquilateral ? <K.T>near-equilateral.</K.T> : <>min∠ {fmt(fld.minAngle)}°</>}</>,
        note: `r ${fmt(fld.inradius)}, min∠ ${fmt(fld.minAngle)}°`,
      })}
      meters={(fld) => [
        { label: "inradius r", v: fld.inradius, color: C.gold },
        { label: "min angle", v: fld.minAngle, unit: "°", color: fld.minAngle < 20 ? C.crimson : C.teal },
        { label: "area", v: fld.area, d: 0, color: C.blue },
        { label: "incircle", v: fld.tangentToAll ? "tangent ✓" : "—", color: C.teal },
      ]}
    />;
  }

  /* ===================== Module 3: Euler line & nine-point circle ===================== */
  function EulerFig(props) {
    return <TriangleFig {...props}
      initial={{ A: { x: 330, y: 110 }, B: { x: 165, y: 420 }, C: { x: 520, y: 400 } }}
      compute={fieldsEuler}
      construction={(tri, fld) => {
        const { A, B, C: Cc } = tri, O = circumcenter(A, B, Cc), R = dist(O, A);
        const G = { x: (A.x + B.x + Cc.x) / 3, y: (A.y + B.y + Cc.y) / 3 };
        const H = { x: A.x + B.x + Cc.x - 2 * O.x, y: A.y + B.y + Cc.y - 2 * O.y }, N = mid(O, H);
        const u = len(sub(H, O)) > 1 ? { x: (H.x - O.x) / len(sub(H, O)), y: (H.y - O.y) / len(sub(H, O)) } : { x: 1, y: 0 };
        const e1 = add(O, mul(u, -320)), e2 = add(H, mul(u, 160));
        const mids = [mid(A, B), mid(B, Cc), mid(Cc, A)], feet = [foot(A, B, Cc), foot(B, Cc, A), foot(Cc, A, B)];
        return (<g>
          <Seg a={A} b={B} color={C.ink} /><Seg a={B} b={Cc} color={C.ink} /><Seg a={Cc} b={A} color={C.ink} />
          <Circle o={O} r={R} color={C.faint} w={1.4} />
          <Circle o={N} r={R / 2} color={C.teal} w={1.8} />
          {!fld.eulerDegenerate && <Seg a={e1} b={e2} color={C.gold} w={1.8} />}
          {[...mids, ...feet].map((p, i) => <circle key={i} cx={p.x} cy={p.y} r={2.6} fill={C.teal} />)}
          <Pt p={O} label="O" color={C.gold} dx={8} dy={-6} r={3.4} />
          <Pt p={G} label="G" color={C.gold} dx={8} dy={12} r={3.4} />
          <Pt p={H} label="H" color={C.gold} dx={8} dy={-6} r={3.4} />
          <Pt p={N} label="N" color={C.teal} dx={-16} dy={-6} r={3.4} />
        </g>);
      }}
      status={(fld) => ({
        ok: fld.eulerDegenerate || fld.isRight,
        text: <>GH/OG = {fmt(fld.ratioGH_OG)} (≈2) · R₉ = {fmt(fld.ninePointRadius)} = R/2 · {fld.eulerDegenerate ? <K.T>centres coincide (equilateral).</K.T> : fld.isRight ? <K.T>right triangle.</K.T> : <K.T>O, G, H on the Euler line.</K.T>}</>,
        note: `OG ${fmt(fld.OG)}, GH ${fmt(fld.GH)}`,
      })}
      meters={(fld) => [
        { label: "OG", v: fld.OG, color: C.blue },
        { label: "GH", v: fld.GH, color: C.blue },
        { label: "GH ÷ OG", v: fld.ratioGH_OG, d: 2, color: C.gold },
        { label: "R₉ = R/2", v: fld.ninePointRadius, color: C.teal },
      ]}
    />;
  }

  const FIGS = { circum: CircumFig, incircle: IncircleFig, euler: EulerFig };
  const MODULE_LABEL = { circum: "Circumcircle & perpendicular bisectors", incircle: "Incircle & angle bisectors", euler: "Euler line & nine-point circle" };

  function AdvGeometryFig(props) {
    const module = FIGS[props.spec && props.spec.module] ? props.spec.module : "circum";
    const Fig = FIGS[module];
    return <Fig {...props} />;
  }

  window.AdvGeometryTutor = K.makeTutor(AdvGeometryFig, {
    benchId: "advgeometry",
    moduleLabel: (spec) => MODULE_LABEL[spec && spec.module] || "Advanced geometry workbench",
  });
})();
