/* visuals4.jsx — Capacitor as a water bucket.
   The bucket fills/drains over real time according to an RC equation.
   This is the first chapter with time-dependent state. */

const { useState: _us4, useEffect: _ue4, useRef: _ur4 } = React;

/* ─── Hook: simulate capacitor charge state ──────────────────────────────
   Returns { vCap, reset } where vCap is the live voltage on the capacitor.
   mode: "charge" → V_cap → V_in via R.
   mode: "discharge" → V_cap → 0 via R.
   mode: "hold" → no change.
   C is in arbitrary "size" units; combined with R produces a time constant
   tau = R * C (seconds). */
function useCapSim({ vIn, R, C, mode }) {
  const [vCap, setVCap] = React.useState(0);
  const lastTimeRef = React.useRef(performance.now());
  React.useEffect(() => {
    lastTimeRef.current = performance.now();
    let raf;
    const tick = (now) => {
      const dtMs = Math.min(80, now - lastTimeRef.current);
      const dt = dtMs / 1000;
      lastTimeRef.current = now;
      setVCap(prev => {
        const target = mode === "charge"   ? vIn
                     : mode === "discharge" ? 0
                     : prev;
        const tau = Math.max(0.05, R * C / 4); // /4 so it feels lively
        const next = prev + (target - prev) * (1 - Math.exp(-dt / tau));
        return next;
      });
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [vIn, R, C, mode]);
  const reset = React.useCallback(() => setVCap(0), []);
  return { vCap, reset, setVCap };
}

/* ─── BucketScene ────────────────────────────────────────────────────────
   A water tower (V_in) on the left → pipe with constriction (R) → bucket
   (C) on the right. The bucket fills from the top via a tap valve, drains
   from the bottom via a drain valve.
   ───────────────────────────────────────────────────────────────────────── */
function BucketScene({ vIn, R, C, vCap, mode, height = 380, showLabels = true, kids = false }) {
  const W = 820, H = 460;
  // Currents (sign depends on mode)
  const dV = mode === "charge" ? (vIn - vCap)
           : mode === "discharge" ? (0 - vCap)
           : 0;
  const I = Math.max(0, Math.abs(dV) / R);
  // Electron/water speed tracks how fast the LEVEL is actually changing:
  // rate = |dV| / tau (volts per second). Near-full → slow; small R·C → fast.
  const tauForRate = Math.max(0.05, (R * C) / 4);
  const levelRate = Math.abs(dV) / tauForRate;
  const period = flowPeriod(Math.max(0.05, levelRate * 0.6));

  // Barrel on left
  const barrelCx = 100, barrelTop = 40, barrelBot = 250;
  const rimRx = 50, midRx = 64;
  // Pipe from barrel — runs along, then elbows DOWN to pour into the bucket
  const pipeStart = barrelCx + 50;
  const pipeY = 150;
  const baseH = 24;
  const cXa = pipeStart + 70, cXb = pipeStart + 170;
  const narrowH = baseH * (0.14 + 0.86 * pipeOpenness(R));
  const bucketX = 520;
  const tapX = bucketX;       // pipe elbows down right above the bucket
  const pipeEnd = tapX;
  // Sluice-gate pinch — downstream water level drops to the gate's opening,
  // matching the resistance animation in Chapters 1–3.
  const constrictions = [{ xa: cXa, xb: cXb, narrowH, gate: true }];
  const pushFrac = narrowH / baseH;
  const pinchMid = (cXa + cXb) / 2;
  const pipeLevels = [
    { fromX: pipeStart, toX: pinchMid, frac: 1 },
    { fromX: pinchMid, toX: pipeEnd, frac: pushFrac },
  ];

  // Bucket on right — a real pail, sitting LOW so the pipe pours into it.
  // Buckets are nearly straight-walled with a mild taper + a bail handle.
  // Sized to stay proportional to the barrel (rim 50 / bulge 64): at the
  // default C it reads a touch smaller than the source, growing with C.
  const bucketTop = 212;
  const bucketBot = 392;
  const topHalf = 34 + C * 6;
  const botHalf = topHalf * 0.82;
  const rimRy = 11;
  const halfAt = (y) => {
    const f = (y - bucketTop) / (bucketBot - bucketTop);
    return topHalf + (botHalf - topHalf) * f;
  };
  const vMaxVisual = 12;
  const fillFrac = Math.max(0, Math.min(1, vCap / vMaxVisual));
  const waterTop = bucketBot - fillFrac * (bucketBot - bucketTop);

  // spigot/tap — comes out the SIDE of the bucket near the bottom, turns down
  const spigotY = bucketBot - 30;
  const spigotRootX = bucketX + halfAt(spigotY);
  const spigotOutX = spigotRootX + 40;
  const spigotMouthY = spigotY + 34;
  const drainExitX = spigotOutX;
  const drainY = spigotMouthY;

  // Determine which animations to run
  const charging = mode === "charge" && I > 0.02;
  const discharging = mode === "discharge" && I > 0.02;

  // On-screen settle time. The sim uses tau = R*C/4 (see useCapSim); a cap is
  // ~99% settled after ~5 tau. Report that so cause/effect is explicit.
  const tauScreen = Math.max(0.05, (R * C) / 4);
  const settleSecs = tauScreen * 5;

  return (
    <svg viewBox={`0 0 ${W} ${H}`} width="100%" height={height}
         preserveAspectRatio="xMidYMid meet" style={{ display: "block", maxHeight: height }}>
      {/* barrel (water source) */}
      {renderBarrel({ cx: barrelCx, top: barrelTop, bot: barrelBot, rimRx, midRx,
                      fill: fillRatio(vIn), idSuffix: "bk" })}

      {/* incoming pipe (from barrel to bucket top) — streaks only while the
          tap is actually open; during drain this pipe is shut and still */}
      {renderPipe({ pipeStart, pipeEnd, pipeY, baseH, constrictions, current: charging ? I : 0, period,
                    idSuffix: "bk-in", showOutletCap: false, levels: pipeLevels })}
      {/* the pipe elbows DOWN and pours into the bucket from above */}
      <path d={`M ${tapX - 20} ${pipeY - baseH} L ${tapX - 20} ${bucketTop - 18}
                M ${tapX + 20} ${pipeY - baseH} L ${tapX + 20} ${bucketTop - 18}`}
            fill="none" stroke="var(--ink)" strokeWidth="2.5" />
      <rect x={tapX - 20} y={pipeY} width={40} height={bucketTop - 18 - pipeY}
            fill="var(--water-faint)" opacity="0.45" />
      {/* water pouring in when charging */}
      {charging && (
        <line x1={tapX} y1={pipeY} x2={tapX} y2={waterTop}
              stroke="var(--water)" strokeWidth="4" strokeDasharray="5 9" strokeLinecap="round"
              className="flow-anim" style={{ "--flow-period": period }} />
      )}

      {/* tap valve sketch above the inlet — open when charging */}
      <g transform={`translate(${tapX} ${pipeY - baseH - 12}) rotate(${charging ? 0 : 60})`}>
        <line x1="-13" y1="0" x2="13" y2="0" stroke="var(--ink)" strokeWidth="3" strokeLinecap="round" />
      </g>

      {/* ── bucket (a real pail with a bail handle) ── */}
      <g>
        {/* bail handle — swings up over the rim, anchored to ears on each side */}
        <path d={`M ${bucketX - topHalf + 6} ${bucketTop + 2}
                  C ${bucketX - topHalf - 4} ${bucketTop - 48} ${bucketX + topHalf + 4} ${bucketTop - 48} ${bucketX + topHalf - 6} ${bucketTop + 2}`}
              fill="none" stroke="var(--ink)" strokeWidth="3.5" strokeLinecap="round" />
        {/* handle ears */}
        <circle cx={bucketX - topHalf + 6} cy={bucketTop + 2} r="4" fill="var(--bg-card)" stroke="var(--ink)" strokeWidth="2" />
        <circle cx={bucketX + topHalf - 6} cy={bucketTop + 2} r="4" fill="var(--bg-card)" stroke="var(--ink)" strokeWidth="2" />

        {/* body: near-straight walls, mild taper, rounded bottom */}
        <path d={`M ${bucketX - topHalf} ${bucketTop}
                  L ${bucketX - botHalf} ${bucketBot - 14}
                  Q ${bucketX - botHalf} ${bucketBot} ${bucketX - botHalf + 14} ${bucketBot}
                  L ${bucketX + botHalf - 14} ${bucketBot}
                  Q ${bucketX + botHalf} ${bucketBot} ${bucketX + botHalf} ${bucketBot - 14}
                  L ${bucketX + topHalf} ${bucketTop} Z`}
              fill="var(--bg-card)" stroke="var(--ink)" strokeWidth="2.5" strokeLinejoin="round" />

        {/* water (clipped to the bucket body) */}
        <clipPath id="bucket-clip">
          <path d={`M ${bucketX - topHalf + 2} ${bucketTop + 1}
                    L ${bucketX - botHalf + 2} ${bucketBot - 14}
                    Q ${bucketX - botHalf + 2} ${bucketBot - 2} ${bucketX - botHalf + 14} ${bucketBot - 2}
                    L ${bucketX + botHalf - 14} ${bucketBot - 2}
                    Q ${bucketX + botHalf - 2} ${bucketBot - 2} ${bucketX + botHalf - 2} ${bucketBot - 14}
                    L ${bucketX + topHalf - 2} ${bucketTop + 1} Z`} />
        </clipPath>
        <linearGradient id="bk-water" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor="var(--water-soft)" />
          <stop offset="100%" stopColor="var(--water)" />
        </linearGradient>
        <g clipPath="url(#bucket-clip)">
          <rect x={bucketX - topHalf} y={waterTop}
                width={topHalf * 2} height={bucketBot - waterTop}
                fill="url(#bk-water)" />
          {fillFrac > 0.02 && (
            <path d={`M ${bucketX - halfAt(waterTop)} ${waterTop + 3}
                       Q ${bucketX - halfAt(waterTop) * 0.5} ${waterTop - 4}, ${bucketX} ${waterTop + 3}
                       T ${bucketX + halfAt(waterTop)} ${waterTop + 3}`}
                  fill="none" stroke="var(--water-deep)" strokeWidth="2" opacity="0.55"/>
          )}
        </g>

        {/* rim ellipse (opening) */}
        <ellipse cx={bucketX} cy={bucketTop} rx={topHalf} ry={rimRy}
                 fill="none" stroke="var(--ink)" strokeWidth="2.5" />
        <ellipse cx={bucketX} cy={bucketTop} rx={topHalf - 5} ry={rimRy - 3}
                 fill="none" stroke="var(--ink-faint)" strokeWidth="1" opacity="0.5" />
        {/* a couple of band lines so it reads as a metal pail */}
        {[0.34, 0.66].map((f, i) => {
          const y = bucketTop + f * (bucketBot - bucketTop);
          const h = halfAt(y);
          return <line key={i} x1={bucketX - h + 2} y1={y} x2={bucketX + h - 2} y2={y}
                       stroke="var(--ink-faint)" strokeWidth="1" opacity="0.4" />;
        })}
      </g>

      {/* ── spigot/tap out the side near the bottom ── */}
      <g>
        {/* horizontal pipe out the side */}
        <rect x={spigotRootX - 2} y={spigotY - 7} width={spigotOutX - spigotRootX + 4} height={14}
              fill="var(--bg-card)" stroke="var(--ink)" strokeWidth="2.5" />
        {/* faucet body turning down */}
        <path d={`M ${spigotOutX - 8} ${spigotY - 12}
                  L ${spigotOutX + 10} ${spigotY - 12}
                  L ${spigotOutX + 10} ${spigotMouthY - 8}
                  L ${spigotOutX + 2} ${spigotMouthY}
                  L ${spigotOutX - 6} ${spigotMouthY - 8}
                  L ${spigotOutX - 8} ${spigotY - 12} Z`}
              fill="var(--bg-card)" stroke="var(--ink)" strokeWidth="2.5" strokeLinejoin="round" />
        {/* valve wheel on top of the faucet */}
        <g transform={`translate(${spigotOutX + 1} ${spigotY - 20})`}>
          <circle r="9" fill="var(--bg-card)" stroke={discharging ? "var(--current)" : "var(--ink)"} strokeWidth="2.5" />
          <line x1="-9" y1="0" x2="9" y2="0" stroke={discharging ? "var(--current)" : "var(--ink)"} strokeWidth="2" transform={`rotate(${discharging ? 0 : 45})`} />
          <line x1="0" y1="-9" x2="0" y2="9" stroke={discharging ? "var(--current)" : "var(--ink)"} strokeWidth="2" transform={`rotate(${discharging ? 0 : 45})`} />
        </g>
      </g>

      {/* water out the spigot when discharging */}
      {discharging && (
        <>
          <line x1={spigotOutX + 1} y1={spigotMouthY - 4} x2={spigotOutX + 1} y2={spigotMouthY + 28}
                stroke="var(--water)" strokeWidth="4" strokeDasharray="5 8" strokeLinecap="round"
                className="flow-anim" style={{ "--flow-period": period }} />
          {[0, 1, 2].map(i => (
            <circle key={i} cx={spigotOutX + 1} cy={spigotMouthY + 8 + i * 12} r="2.6" fill="var(--water)">
              <animate attributeName="cy" from={spigotMouthY} to={spigotMouthY + 44}
                       dur={period} begin={`-${i * 0.16}s`} repeatCount="indefinite" />
              <animate attributeName="opacity" from="0.9" to="0.1"
                       dur={period} begin={`-${i * 0.16}s`} repeatCount="indefinite" />
            </circle>
          ))}
        </>
      )}

      {/* labels */}
      {showLabels && (
        <g fontFamily="IBM Plex Mono, monospace" letterSpacing="0.06em">
          <text x={barrelCx - midRx + 2} y={barrelTop - 18} fontSize="24.5" fill="var(--water)" fontWeight="500">
            {kids ? <>Tower · {vIn.toFixed(1)}V</> : <>V<tspan fontSize="17.5">in</tspan> · {vIn.toFixed(1)}V</>}
          </text>
          <text x={(cXa + cXb) / 2} y={pipeY - baseH - 12} textAnchor="middle"
                fontSize="21.5" fill="var(--ink-faint)">
            {kids ? "Pinch" : `R · ${R.toFixed(1)}Ω`}
          </text>
          <text x={tapX} y={pipeY - baseH - 30} textAnchor="middle"
                fontSize="20.5" fontWeight="600" fill={charging ? "var(--water)" : "var(--ink-faint)"}>
            {charging ? "TAP: OPEN" : "TAP: SHUT"}
          </text>

          {/* bucket name + level, stacked clearly BELOW the pail */}
          <text x={bucketX} y={bucketBot + 28} textAnchor="middle"
                fontSize="25.5" fontWeight="600" fill="var(--ink-soft)">
            {kids ? "BUCKET" : "BUCKET · C"}
          </text>
          <text x={bucketX} y={bucketBot + 50} textAnchor="middle"
                fontSize="23" fill="var(--water-deep)" fontWeight="500">
            {kids ? <>Level · {vCap.toFixed(2)}V</> : <>V<tspan fontSize="17.5">cap</tspan> · {vCap.toFixed(2)}V</>}
          </text>

          {/* spigot label OUT to the right of the spout, clear of the pail */}
          <text x={spigotOutX + 10} y={spigotMouthY + 6} textAnchor="start"
                fontSize="20.5" fontWeight="600" fill={discharging ? "var(--current)" : "var(--ink-faint)"}>
            {discharging ? "SPIGOT: OPEN" : "SPIGOT: SHUT"}
          </text>
          {/* live settle-time readout */}
          <g transform={`translate(${spigotOutX + 10} ${spigotMouthY + 28})`}>
            <text x="0" y="0" fontSize="21.5" fontWeight="600"
                  fill={mode === "hold" ? "var(--ink-faint)" : "var(--water-deep)"}>
              ≈ {settleSecs.toFixed(1)}s
            </text>
            <text x="0" y="16" fontSize="15" fill="var(--ink-faint)" letterSpacing="0.08em">
              {mode === "charge" ? "TO FILL" : mode === "discharge" ? "TO EMPTY" : (kids ? "FILL/EMPTY" : "5·\u03c4 = 5RC")}
            </text>
          </g>
        </g>
      )}
    </svg>
  );
}

/* ─── CapCircuitScene ───────────────────────────────────────────────────
   Battery — switch (charge/discharge) — resistor — capacitor.
   When charging: battery + R + C in series, capacitor builds up V_cap.
   When discharging: battery removed, C discharges through R.
   ───────────────────────────────────────────────────────────────────────── */
function CapCircuitScene({ vIn, R, C, vCap, mode, height = 380, showLabels = true, kids = false }) {
  const W = 820, H = 460;
  const dV = mode === "charge" ? (vIn - vCap) : mode === "discharge" ? (-vCap) : 0;
  const I = Math.abs(dV) / R;
  const period = flowPeriod(Math.max(0.05, I));
  const flowing = I > 0.05 && mode !== "hold";

  const L = 130, R_ = 690, T = 130, B = 360;
  const batCx = L, batCy = (T + B) / 2, batHalfH = 32;
  const switchCx = 270, switchCy = T;
  const resCx = 430, resCy = T;
  const capCx = R_, capCy = (T + B) / 2;
  // Discharge bypass: when the battery is switched out, the cap's charge
  // coasts around THIS wire instead — it must never run through the battery
  // (that would read as the cap recharging the battery).
  const bypX = 196;

  // Wire path: from + terminal up, across top (with switch + resistor breaks), down to top cap plate
  // From bottom cap plate, across bottom, up to - terminal.
  const wirePath = `
    M ${L} ${batCy - batHalfH}
    L ${L} ${T}
    L ${switchCx - 22} ${T}
    M ${switchCx + 22} ${T}
    L ${resCx - 45} ${T}
    M ${resCx + 45} ${T}
    L ${R_} ${T}
    L ${R_} ${capCy - 24}
    M ${R_} ${capCy + 24}
    L ${R_} ${B}
    L ${L} ${B}
    L ${L} ${batCy + batHalfH}
  `;

  // The loop electrons travel: charging runs battery → R → cap; discharging
  // coasts cap → R → bypass — skipping the battery entirely.
  const loopPath = mode === "discharge"
    ? `M ${R_} ${capCy - 24} L ${R_} ${T} L ${bypX} ${T} L ${bypX} ${B} L ${R_} ${B} L ${R_} ${capCy + 24} L ${R_} ${capCy - 24} Z`
    : `M ${L} ${batCy - batHalfH} L ${L} ${T} L ${R_} ${T} L ${R_} ${B} L ${L} ${B} L ${L} ${batCy + batHalfH} Z`;

  return (
    <svg viewBox={`0 0 ${W} ${H}`} width="100%" height={height}
         preserveAspectRatio="xMidYMid meet" style={{ display: "block", maxHeight: height }}>
      <path d={wirePath} fill="none" stroke="var(--current-faint)"
            strokeWidth="9" strokeLinecap="round"
            opacity={flowing ? 0.5 : 0} style={{ transition: "opacity 200ms ease" }} />
      <path d={wirePath} fill="none" stroke="var(--ink)" strokeWidth="2.5" strokeLinecap="round" />

      {/* discharge bypass wire — dashed & idle until the cap is draining */}
      <line x1={bypX} y1={T} x2={bypX} y2={B}
            stroke={mode === "discharge" ? "var(--current)" : "var(--ink-faint)"}
            strokeWidth={mode === "discharge" ? 2.5 : 1.6}
            strokeDasharray={mode === "discharge" ? "" : "5 6"}
            style={{ transition: "stroke 200ms ease" }} />
      {showLabels && (
        <text x={bypX - 10} y={(T + B) / 2} textAnchor="end"
              fontFamily="IBM Plex Mono, monospace" fontSize="14.5"
              fill={mode === "discharge" ? "var(--current-deep)" : "var(--ink-faint)"}>
          {mode === "discharge" ? (kids ? "drain loop — battery's out!" : "discharge path · battery out") : (kids ? "drain loop" : "discharge path")}
        </text>
      )}

      {renderElectrons({ path: loopPath, period, flowing, count: 6 })}

      {/* battery fades while switched out of the loop — the drain current
          never touches it, and the dimming makes that unmistakable */}
      <g opacity={mode === "discharge" ? 0.3 : 1} style={{ transition: "opacity 250ms ease" }}>
        {renderBatterySymbol({ cx: batCx, cy: batCy, halfH: batHalfH, voltage: vIn, showLabel: showLabels })}
      </g>

      {/* Switch (open in "hold", closed-charge or closed-discharge in others) */}
      <g>
        <circle cx={switchCx - 22} cy={switchCy} r="3" fill="var(--ink)" />
        <circle cx={switchCx + 22} cy={switchCy} r="3" fill="var(--ink)" />
        {mode === "hold" ? (
          <line x1={switchCx - 22} y1={switchCy}
                x2={switchCx + 14} y2={switchCy - 22}
                stroke="var(--ink)" strokeWidth="2.5" strokeLinecap="round" />
        ) : (
          <line x1={switchCx - 22} y1={switchCy}
                x2={switchCx + 22} y2={switchCy}
                stroke={mode === "charge" ? "var(--water)" : "var(--current)"}
                strokeWidth="2.5" strokeLinecap="round" />
        )}
        {showLabels && (
          <text x={switchCx} y={switchCy - 28}
                fontFamily="IBM Plex Mono, monospace" fontSize="16"
                fill="var(--ink-faint)" textAnchor="middle" letterSpacing="0.14em">
            {mode === "hold" ? "OPEN" : mode.toUpperCase()}
          </text>
        )}
      </g>

      {renderResistorSymbol({ cx: resCx, cy: resCy, w: 90, h: 30, r: R,
                              label: showLabels && (kids ? "Pinch" : `R · ${R.toFixed(1)}Ω`) })}

      {/* Capacitor symbol — two parallel plates */}
      <g>
        <line x1={capCx - 28} y1={capCy - 24} x2={capCx + 28} y2={capCy - 24}
              stroke="var(--water)" strokeWidth="4" />
        <line x1={capCx - 28} y1={capCy + 24} x2={capCx + 28} y2={capCy + 24}
              stroke="var(--ink)" strokeWidth="4" />
        {/* charge indicator — + on top plate proportional to vCap */}
        <text x={capCx - 40} y={capCy - 26}
              fontFamily="IBM Plex Mono, monospace" fontSize="19"
              fill="var(--water)" fontWeight="600" textAnchor="end"
              opacity={Math.max(0.2, Math.min(1, vCap / 4))}>
          +
        </text>
        <text x={capCx - 40} y={capCy + 32}
              fontFamily="IBM Plex Mono, monospace" fontSize="19"
              fill="var(--ink-faint)" fontWeight="600" textAnchor="end"
              opacity={Math.max(0.2, Math.min(1, vCap / 4))}>
          −
        </text>
        {showLabels && (
          <text x={capCx + 40} y={capCy + 4}
                fontFamily="IBM Plex Mono, monospace" fontSize="19"
                fill="var(--ink-faint)" letterSpacing="0.14em">
            {kids ? "Bucket" : `C · ${C.toFixed(1)}`}
          </text>
        )}
        {showLabels && (
          <text x={capCx} y={capCy + 50}
                fontFamily="IBM Plex Mono, monospace" fontSize="19"
                fill="var(--water-deep)" textAnchor="middle" letterSpacing="0.14em">
            {kids ? <>Level = {vCap.toFixed(2)}V</> : <>V<tspan fontSize="13.5">cap</tspan> = {vCap.toFixed(2)}V</>}
          </text>
        )}
      </g>
    </svg>
  );
}

Object.assign(window, { useCapSim, BucketScene, CapCircuitScene });
