/* organicchemlab.jsx — OrganicChemTutor: the organic-chemistry molecule builder. Built on
   window.BenchKit (harness) + window.BenchFields (shared field math), so this file is just
   the FIGURE. compute = window.BenchFields.organicChem — the SAME function
   server/benches/organicchem.js calls, so client and server fields are identical by
   construction. The figure DRAWS the molecule as a skeletal structural formula (zig-zag
   backbone, real double/triple bonds, drawn functional groups) that rebuilds live as the
   learner sets the chain length, C–C bond order, functional group and branching. The SVG
   is illustration only — computeFields is untouched, so field parity is preserved. */
(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 MONO = "ui-monospace, SFMono-Regular, Menlo, monospace";
  const compute = (s) => window.BenchFields.organicChem(s);

  const BONDS = ["single", "double", "triple"];
  const BOND_LABEL = { single: "single (alkane)", double: "double (alkene)", triple: "triple (alkyne)" };
  const BOND_ORDER = { single: 1, double: 2, triple: 3 };
  const GROUPS = ["none", "alcohol", "aldehyde", "ketone", "carboxylic", "amine"];
  const GROUP_LABEL = { none: "none (hydrocarbon)", alcohol: "alcohol –OH", aldehyde: "aldehyde –CHO", ketone: "ketone C=O", carboxylic: "carboxylic acid –COOH", amine: "amine –NH₂" };
  const STEM = ["", "meth", "eth", "prop", "but", "pent", "hex", "hept", "oct", "non", "dec"];

  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 Picker({ label, value, set, options, labels, color, onPick }) {
    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>
        <div style={{ display: "flex", flexWrap: "wrap", gap: 6, flex: 1 }}>
          {options.map((o) => (
            <button key={o} onClick={() => { set(o); onPick && onPick(o); }}
              style={{ padding: "4px 9px", borderRadius: 6, fontSize: 11.5, cursor: "pointer",
                border: `1px solid ${value === o ? (color || C.amber) : C.line}`,
                background: value === o ? (color || C.amber) : C.panel,
                color: value === o ? C.bg : C.mute }}>
              <K.T>{(labels && labels[o]) || o}</K.T>
            </button>
          ))}
        </div>
      </div>
    );
  }

  // Molecular-formula string CcHhNnOo (C, H, then N, O).
  function formula(f) {
    let s = "C" + (f.carbons === 1 ? "" : f.carbons) + "H" + (f.hydrogens === 1 ? "" : f.hydrogens);
    if (f.nitrogens) s += "N" + (f.nitrogens === 1 ? "" : f.nitrogens);
    if (f.oxygens) s += "O" + (f.oxygens === 1 ? "" : f.oxygens);
    return s;
  }

  // Auto-generated IUPAC-style name (illustrative label; nomenclature is "stretch" here).
  function moleculeName(carbons, bonds, group) {
    if (carbons < 1 || carbons > 10) return "";
    const stem = STEM[carbons];
    let inf = bonds === "single" ? "an" : bonds === "double" ? "en" : "yn";
    if (carbons < 2 && inf !== "an") inf = "an";        // need ≥2 C for a multiple bond
    const suf = { none: "e", alcohol: "ol", aldehyde: "al", ketone: "one", carboxylic: "oic acid", amine: "amine" }[group] || "e";
    const name = stem + inf + suf;
    return name.charAt(0).toUpperCase() + name.slice(1);
  }

  // ── skeletal-formula geometry ─────────────────────────────────────────────────
  // Carbons sit at zig-zag vertices (terminal CH₃ and chain H implied, as in real
  // skeletal formulas); only heteroatoms (O, N) and their H are drawn explicitly.
  function bondLines(p, q, order, color, key, w) {
    const dx = q.x - p.x, dy = q.y - p.y, L = Math.hypot(dx, dy) || 1;
    const nx = -dy / L, ny = dx / L;                    // unit normal for parallel offsets
    const offs = order === 1 ? [0] : order === 2 ? [3, -3] : [0, 4.6, -4.6];
    return offs.map((o, i) => (
      <line key={key + "_" + i} x1={p.x + nx * o} y1={p.y + ny * o} x2={q.x + nx * o} y2={q.y + ny * o}
        stroke={color} strokeWidth={w || 2.2} strokeLinecap="round" />
    ));
  }
  function Atom({ x, y, text, color }) {
    return (
      <g>
        <circle cx={x} cy={y} r={text.length > 1 ? 13 : 10} fill={SVG_BG} />
        <text x={x} y={y + 5} textAnchor="middle" fill={color} fontSize="15" fontFamily={MONO} fontWeight="700">{text}</text>
      </g>
    );
  }

  function Molecule({ carbons, bonds, group, branched, fld }) {
    const n = carbons;
    const span = Math.max(1, n - 1);
    const L = Math.max(24, Math.min(42, 360 / (0.866 * span + 1.8)));
    const dx = 0.866 * L, dy = 0.5 * L;
    const BY = 188, X0 = 64;
    const v = [];
    for (let i = 0; i < n; i++) v.push({ x: X0 + i * dx, y: BY - (i % 2 ? dy : 0) });
    const last = v[n - 1];
    const outDir = { x: 0.866, y: (n - 1) % 2 === 0 ? -0.5 : 0.5 }; // continue the zig-zag
    const up = { x: 0, y: -1 };
    const els = [];

    // backbone C–C bonds (the first link carries the chosen C=C / C≡C bond order)
    for (let i = 0; i < n - 1; i++) {
      const order = i === 0 ? BOND_ORDER[bonds] : 1;
      els.push(...bondLines(v[i], v[i + 1], order, order > 1 ? C.crimson : C.ink, "b" + i));
    }
    // faint vertex dots so learners can count the carbons
    v.forEach((p, i) => els.push(<circle key={"v" + i} cx={p.x} cy={p.y} r="2.6" fill={C.ink} opacity="0.55" />));
    // a branch methyl off a middle carbon
    if (branched && n >= 3) {
      const m = v[Math.floor(n / 2)], b = { x: m.x, y: m.y - L };
      els.push(...bondLines(m, b, 1, C.ink, "br"));
      els.push(<Atom key="brc" x={b.x} y={b.y - 2} text="CH₃" color={C.mute} />);
    }
    // n === 1: nothing to zig-zag — show the lone carbon explicitly
    if (n === 1 && group === "none") els.push(<Atom key="ch4" x={last.x} y={last.y} text="CH₄" color={C.ink} />);

    // ── functional group, drawn on the terminal carbon (ketone → an inner carbon) ──
    const off = (base, dir, k) => ({ x: base.x + dir.x * L * k, y: base.y + dir.y * L * k });
    if (group === "alcohol") {
      const o = off(last, outDir, 1);
      els.push(...bondLines(last, o, 1, C.ink, "oh"));
      els.push(<Atom key="ohL" x={o.x} y={o.y} text="OH" color={C.crimson} />);
    } else if (group === "amine") {
      const o = off(last, outDir, 1);
      els.push(...bondLines(last, o, 1, C.ink, "nh"));
      els.push(<Atom key="nhL" x={o.x} y={o.y} text="NH₂" color={C.blue} />);
    } else if (group === "aldehyde") {
      const o = off(last, up, 1), h = off(last, outDir, 1);
      els.push(...bondLines(last, o, 2, C.crimson, "ald"));
      els.push(...bondLines(last, h, 1, C.ink, "aldh"));
      els.push(<Atom key="aldO" x={o.x} y={o.y} text="O" color={C.crimson} />);
      els.push(<Atom key="aldH" x={h.x} y={h.y} text="H" color={C.mute} />);
    } else if (group === "ketone") {
      const ki = n >= 3 ? 1 : n - 1, kv = v[ki], o = off(kv, up, 1);
      els.push(...bondLines(kv, o, 2, C.crimson, "ket"));
      els.push(<Atom key="ketO" x={o.x} y={o.y} text="O" color={C.crimson} />);
    } else if (group === "carboxylic") {
      const o = off(last, up, 1), oh = off(last, outDir, 1);
      els.push(...bondLines(last, o, 2, C.crimson, "cooO"));
      els.push(...bondLines(last, oh, 1, C.ink, "cooOH"));
      els.push(<Atom key="cooOa" x={o.x} y={o.y} text="O" color={C.crimson} />);
      els.push(<Atom key="cooOHa" x={oh.x} y={oh.y} text="OH" color={C.crimson} />);
    }
    return <g>{els}</g>;
  }

  function OrganicChemFig({ task, setTask, tasks, report, event, done }) {
    const [carbons, setCarbons] = useState(2);
    const [bonds, setBonds] = useState("single");
    const [group, setGroup] = useState("none");
    const [branched, setBranched] = useState(false);
    const fld = useMemo(() => compute({ carbons, bonds, group, branched }), [carbons, bonds, group, branched]);
    useEffect(() => { report(fld); }, [carbons, bonds, group, branched]); // eslint-disable-line

    const t = tasks.find((x) => x.id === task) || tasks[0];
    const name = moleculeName(carbons, bonds, group);

    // boiling-point thermometer (model range ≈ −205..+230 °C)
    const bpFrac = Math.max(0, Math.min(1, (fld.boilingPoint + 210) / 440));
    const TX = 556, TY0 = 96, TH = 150;

    return (
      <>
        <TaskStrip tasks={tasks} cur={task} setCur={setTask} done={done} goal={t && t.goal} />
        <svg viewBox="0 0 600 320" style={{ width: "100%", background: SVG_BG, border: `1px solid ${C.line}`, borderRadius: 8 }}>
          {/* header: formula + auto-name + DoU / molar mass */}
          <text x={22} y={34} fill={C.ink} fontSize="22" fontFamily={MONO} fontWeight="bold">{formula(fld)}</text>
          {name ? <text x={22} y={54} fill={C.teal} fontSize="13" fontFamily={MONO}>{name}</text> : null}
          <text x={22} y={300} fill={C.mute} fontSize="11.5" fontFamily={MONO}>
            DoU = {fld.degreeUnsat} · M = {fmt(fld.molarMass)} g/mol{fld.isSaturated ? " · saturated" : ""}{fld.hasHbond ? " · H-bonding" : ""}
          </text>

          {/* the molecule, drawn as a skeletal structural formula */}
          <Molecule carbons={carbons} bonds={bonds} group={group} branched={branched} fld={fld} />

          {/* boiling-point thermometer */}
          <text x={TX} y={TY0 - 12} textAnchor="middle" fill={C.mute} fontSize="10" fontFamily={MONO}>b.p.</text>
          <rect x={TX - 9} y={TY0} width="18" height={TH} rx="9" fill={C.panel} stroke={C.line} strokeWidth="1.5" />
          <rect x={TX - 6} y={TY0 + TH * (1 - bpFrac)} width="12" height={TH * bpFrac} rx="6"
            fill={fld.hasHbond ? C.crimson : (fld.boilingPoint > 30 ? C.amber : C.blue)} />
          <text x={TX} y={TY0 + TH + 18} textAnchor="middle" fill={C.ink} fontSize="12" fontFamily={MONO}>{fmt(fld.boilingPoint)}°C</text>
        </svg>

        <div style={{ marginTop: 10, padding: "8px 12px", borderRadius: 6, background: C.panel, border: `1px solid ${C.line}`, color: C.mute, fontSize: 12.5 }}>
          <K.T>The structure redraws live: each double bond, triple bond (×2) and C=O removes two hydrogens, so DoU = (2C + 2 + N − H)/2.</K.T>{" "}
          {fld.hasHbond ? <K.T>This molecule hydrogen-bonds, so it boils high for its size.</K.T> : <K.T>Boiling point climbs with chain length; a polar –OH or –COOH lifts it far more.</K.T>}
        </div>

        <Slider label="carbon chain (C)" value={carbons} set={setCarbons} min={1} max={10} step={1} color={C.blue}
          onUp={() => event("adjusted", `Set chain length C = ${carbons}`, { response: `${formula(fld)} · M ${fmt(fld.molarMass)}` }, C.blue)} />
        <Picker label="C–C bond order" value={bonds} set={setBonds} options={BONDS} labels={BOND_LABEL} color={C.crimson}
          onPick={(o) => event("adjusted", `Set bond order = ${o}`, { response: `DoU ${compute({ carbons, bonds: o, group, branched }).degreeUnsat}` }, C.crimson)} />
        <Picker label="functional group" value={group} set={setGroup} options={GROUPS} labels={GROUP_LABEL} color={C.teal}
          onPick={(o) => event("adjusted", `Set functional group = ${o}`, { response: `b.p. ${fmt(compute({ carbons, bonds, group: o, branched }).boilingPoint)}°C` }, C.teal)} />
        <Picker label="skeleton" value={branched ? "branched" : "straight"} set={(o) => setBranched(o === "branched")} options={["straight", "branched"]} color={C.gold}
          onPick={(o) => event("adjusted", `Set skeleton = ${o}`, { response: `b.p. ${fmt(compute({ carbons, bonds, group, branched: o === "branched" }).boilingPoint)}°C` }, C.gold)} />

        <div style={{ display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 8, marginTop: 12 }}>
          <StatMeter label="molar mass" v={fld.molarMass} color={C.teal} />
          <StatMeter label="degree unsat." v={fld.degreeUnsat} d={0} color={fld.degreeUnsat >= 2 ? C.crimson : C.amber} />
          <StatMeter label="boiling point" v={fld.boilingPoint} d={0} color={fld.boilingPoint > 30 ? C.crimson : C.blue} />
          <StatMeter label="H atoms" v={fld.hydrogens} d={0} color={C.mute} />
        </div>
      </>
    );
  }

  window.OrganicChemTutor = K.makeTutor(OrganicChemFig, { moduleLabel: "Organic chemistry bench", benchId: "organicchem" });
})();
