/* electronicslab.jsx — ElectronicsTutor: the series-circuit bench. Built on window.BenchKit
   (loaded first), so this file is just the FIGURE + the circuit physics. Defines
   window.ElectronicsTutor. report(sim) emits the live circuit state; tasks are graded
   against it (the same fields server/benches/electronics.js declares). Ported from the
   original CircuitLab in app.jsx so electronics wears the shared workbench look. */
(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;
  const tt = (s) => (window.I18NUtils ? window.I18NUtils.t(s) : String(s == null ? "" : s));
  const tf = (tmpl, vars) => tt(tmpl).replace(/\{(\w+)\}/g, (m, k) => (vars && vars[k] != null ? vars[k] : m));

  const LED_VF = 2.0, LED_RD = 10, LED_SAFE = 20, LED_BURN = 40, R_RATING = 0.25;
  const PALETTE = [
    { type: "resistor", label: "Resistor", hint: "Limits current (Ω)" },
    { type: "led", label: "LED", hint: "Lights when forward-biased" },
    { type: "wire", label: "Wire", hint: "Ideal conductor (~0 Ω)" },
  ];

  /* the physics — series loop, battery + two slots */
  function simulate(V, slotA, slotB) {
    const slots = [slotA, slotB];
    const closed = slots.every(Boolean);
    const hasLED = slots.some((c) => c && c.type === "led");
    const hasResistor = slots.some((c) => c && c.type === "resistor");
    const damaged = slots.some((c) => c && c.type === "led" && c.damaged);
    const base = { closed, hasLED, hasResistor, damaged, Ima: 0, R: 0, Vf: 0, Vr: 0, Pr: 0, status: "open", short: false, brightness: 0, willBurn: false };
    if (!closed) return { ...base, status: "open" };
    if (damaged) return { ...base, status: "burnt" };
    let R = 0, Vf = 0;
    slots.forEach((c) => { if (!c) return; if (c.type === "resistor") R += c.R; else if (c.type === "wire") R += 0.01; else if (c.type === "led") { R += LED_RD; Vf += LED_VF; } });
    const drive = V - Vf;
    let Ima = drive <= 0 ? 0 : (drive / R) * 1000;
    const short = Ima > 1000;
    Ima = Math.min(Ima, 8000);
    const I = Ima / 1000;
    const Rres = slots.reduce((a, c) => a + (c && c.type === "resistor" ? c.R : 0), 0);
    const Vr = I * Rres, Pr = I * I * Rres;
    let status = "off", brightness = 0;
    if (!hasLED) status = Ima > 0.5 ? "flow" : "off";
    else if (Ima < 1) status = "off";
    else if (Ima > LED_SAFE) { status = "overdrive"; brightness = 1; }
    else { status = "lit"; brightness = Math.max(0.12, Math.min(1, Ima / LED_SAFE)); }
    return { ...base, Ima, R, Vf, Vr, Pr, short, status, brightness, willBurn: hasLED && Ima > LED_BURN };
  }
  const meterColor = (sim) => (sim.status === "overdrive" || sim.status === "burnt" ? C.crimson : sim.Ima > 0.5 ? C.teal : C.mute);

  /* schematic sub-symbols */
  function Battery({ x, y, v }) {
    return (
      <g transform={`rotate(90 ${x} ${y})`}>
        <line x1={x - 26} y1={y} x2={x - 8} y2={y} stroke={C.faint} strokeWidth="2.5" />
        <line x1={x - 8} y1={y - 16} x2={x - 8} y2={y + 16} stroke={C.amber} strokeWidth="3" />
        <line x1={x + 2} y1={y - 8} x2={x + 2} y2={y + 8} stroke={C.amber} strokeWidth="3" />
        <line x1={x + 12} y1={y - 16} x2={x + 12} y2={y + 16} stroke={C.amber} strokeWidth="3" />
        <line x1={x + 22} y1={y - 8} x2={x + 22} y2={y + 8} stroke={C.amber} strokeWidth="3" />
        <line x1={x + 22} y1={y} x2={x + 30} y2={y} stroke={C.faint} strokeWidth="2.5" />
        <text x={x} y={y + 40} textAnchor="middle" fill={C.amber} fontSize="13" transform={`rotate(-90 ${x} ${y + 40})`}>{v}V</text>
      </g>
    );
  }
  function ResistorSym({ x, y, R }) {
    return (
      <g transform={`translate(${x} ${y})`}>
        <path d="M-44 0 L-30 0 L-26 -10 L-18 10 L-10 -10 L-2 10 L6 -10 L14 10 L22 -10 L30 0 L44 0" fill="none" stroke={C.gold} strokeWidth="2.5" />
        <text x="0" y="24" textAnchor="middle" fill={C.gold} fontSize="12">{R} Ω</text>
      </g>
    );
  }
  function LedSym({ x, y, sim, damaged }) {
    const lit = !damaged && sim.brightness > 0 && sim.hasLED, over = sim.status === "overdrive";
    const col = damaged ? C.crimson : over ? C.crimson : C.teal;
    return (
      <g transform={`translate(${x} ${y})`}>
        {lit && <circle cx="0" cy="0" r={26 + sim.brightness * 22} fill="url(#glow)" opacity={sim.brightness} />}
        <line x1="-44" y1="0" x2="-12" y2="0" stroke={C.faint} strokeWidth="2.5" />
        <polygon points="-12,-12 -12,12 10,0" fill={lit ? "#ffe08a" : "#243"} stroke={col} strokeWidth="2" />
        <line x1="10" y1="-13" x2="10" y2="13" stroke={col} strokeWidth="2.5" />
        <line x1="10" y1="0" x2="44" y2="0" stroke={C.faint} strokeWidth="2.5" />
        {damaged && <text x="0" y="6" textAnchor="middle" fill={C.crimson} fontSize="22">✕</text>}
        <text x="0" y="30" textAnchor="middle" fill={col} fontSize="11">{damaged ? "burnt" : "LED"}</text>
      </g>
    );
  }
  function Slot({ k, x, y, horizontal, slot, sim, selected, onSelect }) {
    const w = 116, h = 64;
    return (
      <g transform={horizontal ? "" : `rotate(90 ${x} ${y})`}>
        <rect x={x - w / 2} y={y - h / 2} width={w} height={h} fill="#0a1620" rx="6" stroke={selected ? C.blue : "transparent"} strokeWidth="2" strokeDasharray={slot ? "0" : "5 4"} style={{ cursor: "pointer" }} onClick={() => onSelect(k)} />
        {!slot && (<>
          <rect x={x - w / 2} y={y - h / 2} width={w} height={h} fill="none" rx="6" stroke={selected ? C.blue : C.faint} strokeWidth="1.5" strokeDasharray="5 4" />
          <text x={x} y={y - 4} textAnchor="middle" fill={selected ? C.blue : C.faint} fontSize="13">{`Slot ${k}`}</text>
          <text x={x} y={y + 14} textAnchor="middle" fill={C.faint} fontSize="10">empty · tap +</text>
        </>)}
        {slot && slot.type === "resistor" && <ResistorSym x={x} y={y} R={slot.R} />}
        {slot && slot.type === "wire" && (<><line x1={x - w / 2} y1={y} x2={x + w / 2} y2={y} stroke={C.faint} strokeWidth="2.5" /><text x={x} y={y + 22} textAnchor="middle" fill={C.faint} fontSize="10">wire</text></>)}
        {slot && slot.type === "led" && <LedSym x={x} y={y} sim={sim} damaged={slot.damaged} />}
      </g>
    );
  }
  function Schematic({ sim, voltage, slots, selected, onSelect }) {
    const L = 120, R = 480, T = 90, B = 300, MIDY = 195;
    const dur = sim.Ima > 0.5 && sim.status !== "burnt" ? Math.max(0.5, Math.min(6, 90 / sim.Ima)) : 0;
    const flowing = dur > 0, dots = 9;
    return (
      <svg viewBox="0 0 600 380" style={{ width: "100%", background: SVG_BG, border: `1px solid ${C.line}`, borderRadius: 8 }}>
        <defs>
          <radialGradient id="glow" cx="50%" cy="50%" r="50%"><stop offset="0%" stopColor="#ffe08a" stopOpacity="0.95" /><stop offset="100%" stopColor="#ffe08a" stopOpacity="0" /></radialGradient>
          <path id="loopPath" d={`M${L},${T} L${R},${T} L${R},${B} L${L},${B} Z`} fill="none" />
        </defs>
        <path d={`M${L},${T} L${R},${T} L${R},${B} L${L},${B} Z`} fill="none" stroke={flowing ? C.teal : C.faint} strokeWidth="2.5" opacity={flowing ? 0.9 : 0.55} />
        {flowing && (
          <g key={`flow-${Math.round(sim.Ima)}`}>
            {Array.from({ length: dots }).map((_, i) => (
              <circle key={i} r="3.4" fill={C.teal}><animateMotion dur={`${dur}s`} repeatCount="indefinite" begin={`-${(i / dots) * dur}s`}><mpath href="#loopPath" /></animateMotion></circle>
            ))}
          </g>
        )}
        <Battery x={L} y={MIDY} v={voltage} />
        <Slot k="A" x={300} y={T} horizontal slot={slots.A} sim={sim} selected={selected === "A"} onSelect={onSelect} />
        <Slot k="B" x={R} y={MIDY} slot={slots.B} sim={sim} selected={selected === "B"} onSelect={onSelect} />
        <text x="300" y="350" textAnchor="middle" fill={C.faint} fontSize="11">● = conventional current (+ → −) · bottom &amp; left rails = wire</text>
      </svg>
    );
  }
  function StatusBar({ sim }) {
    const map = {
      open: { txt: "Open circuit — no complete path. Fill both slots.", c: C.faint },
      off: { txt: "Closed, but no light. Check forward voltage / current.", c: C.mute },
      flow: { txt: "Current flows, but there's no LED in the loop.", c: C.blue },
      lit: { txt: `LED lit · ${sim.Ima.toFixed(1)} mA (safe ≤ ${LED_SAFE} mA).`, c: C.teal },
      overdrive: { txt: `OVERDRIVE · ${sim.Ima.toFixed(0)} mA exceeds ${LED_SAFE} mA — heading for burnout.`, c: C.crimson },
      burnt: { txt: "LED destroyed. Replace it and add current limiting.", c: C.crimson },
    };
    const s = map[sim.status] || map.off;
    return <div style={{ marginTop: 10, padding: "8px 12px", borderRadius: 6, background: C.panel, border: `1px solid ${s.c}`, color: s.c, fontSize: 12.5 }}>{sim.short ? "⚠ Near short-circuit — almost no resistance. " : ""}{s.txt}</div>;
  }

  /* ── the figure: build a series loop; current/brightness/burn update live ── */
  function CircuitFig({ task, setTask, tasks, report, event, done, spec }) {
    const [voltage, setVoltage] = useState((spec && spec.supplyDefault) || 9);
    const [slots, setSlots] = useState({ A: { type: "wire" }, B: null });
    const [selected, setSelected] = useState("B");
    const sim = useMemo(() => simulate(voltage, slots.A, slots.B), [voltage, slots]);

    useEffect(() => { report(sim); }, [sim]); // eslint-disable-line
    // destructive overcurrent: burn any LED, once
    useEffect(() => {
      if (!sim.willBurn) return;
      setSlots((s) => { const n = { ...s }; ["A", "B"].forEach((k) => { if (n[k] && n[k].type === "led" && !n[k].damaged) n[k] = { ...n[k], damaged: true }; }); return n; });
      event("failed", "LED destroyed by overcurrent", { response: `${Math.round(sim.Ima)} mA > ${LED_BURN} mA` }, C.crimson);
    }, [sim.willBurn]); // eslint-disable-line

    function place(type) { if (!selected) return; const comp = type === "resistor" ? { type, R: 470 } : { type }; setSlots((s) => ({ ...s, [selected]: comp })); event("placed", `Placed ${type} in slot ${selected}`, { response: type }, C.blue); }
    function clearSlot(k) { setSlots((s) => ({ ...s, [k]: null })); event("removed", `Cleared slot ${k}`, {}, C.faint); }
    const t = tasks.find((x) => x.id === task) || tasks[0];

    return (
      <>
        <TaskStrip tasks={tasks} cur={task} setCur={setTask} done={done} goal={t && t.goal} />
        <Schematic sim={sim} voltage={voltage} slots={slots} selected={selected} onSelect={setSelected} />
        <StatusBar sim={sim} />
        <div style={{ display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 8, marginTop: 12 }}>
          <StatMeter label="Current" v={sim.Ima} unit="mA" color={meterColor(sim)} />
          <StatMeter label="Supply" v={voltage} unit="V" color={C.amber} />
          <StatMeter label="V·R" v={sim.Vr} d={2} unit="V" color={C.blue} />
          <StatMeter label="P·R" v={sim.Pr} d={3} unit="W" color={sim.Pr > R_RATING ? C.crimson : C.mute} />
        </div>

        <div style={{ marginTop: 14, display: "flex", alignItems: "center", gap: 10 }}>
          <span style={{ fontSize: 12, color: C.mute, width: 96 }}>supply V</span>
          <input type="range" min={0} max={12} step={0.5} value={voltage} onChange={(e) => setVoltage(parseFloat(e.target.value))} onPointerUp={() => event("adjusted", `Set supply to ${voltage} V`, { response: `${voltage} V` }, C.amber)} style={{ flex: 1 }} />
          <span style={{ color: C.amber, width: 52, textAlign: "right" }}>{voltage.toFixed(1)} V</span>
        </div>
        <div style={{ display: "flex", gap: 8, marginTop: 10 }}>
          {["A", "B"].map((k) => (
            <button key={k} onClick={() => setSelected(k)} style={{ ...btn(selected === k ? C.blue : C.panel2, selected === k ? C.bg : C.mute), flex: 1, border: `1px solid ${selected === k ? C.blue : C.line}` }}>
              {slots[k] ? `Slot ${k} · ${slots[k].type}` : `Slot ${k} · empty`}
            </button>
          ))}
        </div>
        <div style={{ display: "flex", gap: 8, marginTop: 10, flexWrap: "wrap" }}>
          {PALETTE.map((p) => <button key={p.type} onClick={() => place(p.type)} style={{ ...btn(C.panel2, C.ink), border: `1px solid ${C.line}` }} title={p.hint}>+ {p.label}</button>)}
          <button onClick={() => selected && clearSlot(selected)} style={btn(C.panel2, C.mute)}>Clear {selected}</button>
        </div>
        {["A", "B"].map((k) => slots[k] && slots[k].type === "resistor" ? (
          <div key={k} style={{ display: "flex", alignItems: "center", gap: 10, marginTop: 10 }}>
            <span style={{ fontSize: 12, color: C.mute, width: 96 }}>R slot {k}</span>
            <input type="range" min={10} max={2200} step={10} value={slots[k].R} onChange={(e) => setSlots((s) => ({ ...s, [k]: { ...s[k], R: parseInt(e.target.value) } }))} onPointerUp={(e) => event("adjusted", `Set slot ${k} resistor to ${slots[k].R} Ω`, { response: `${slots[k].R} Ω` }, C.amber)} style={{ flex: 1 }} />
            <span style={{ color: C.gold, width: 64, textAlign: "right" }}>{slots[k].R} Ω</span>
          </div>
        ) : null)}
        {sim.damaged && <button onClick={() => setSlots((s) => { const n = { ...s }; ["A", "B"].forEach((k) => { if (n[k] && n[k].type === "led") n[k] = { type: "led" }; }); return n; })} style={{ ...btn(C.crimson, C.bg), marginTop: 10 }}>⟳ Replace burnt-out LED</button>}
      </>
    );
  }

  window.ElectronicsTutor = K.makeTutor(CircuitFig, { moduleLabel: "Series-circuit bench", benchId: "electronics" });
})();
