// ============================================================
// motor.jsx — pseudo-CAD / simulation visuals
// All SVG, all animated. No real assets needed.
// ============================================================

// ------------------------------------------------------------
// 3D-ish wireframe motor (rotating)
// Built from concentric polygons (rotor + stator slots) projected
// with a perspective tilt. Auto-rotates; pauses on hover.
// ------------------------------------------------------------
function WireframeMotor({ size = 360, slots = 24, poles = 12, accent, paused: pausedProp, speed = 1, label = "WINDING REF · LIVE" }) {
  const [t, setT] = React.useState(0);
  const [hovered, setHovered] = React.useState(false);
  const rafRef = React.useRef(0);
  const paused = pausedProp ?? hovered;

  React.useEffect(() => {
    let start = performance.now(), last = start;
    const tick = (now) => {
      const dt = (now - last) / 1000;
      last = now;
      if (!paused) setT(s => s + dt * speed);
      rafRef.current = requestAnimationFrame(tick);
    };
    rafRef.current = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(rafRef.current);
  }, [paused, speed]);

  const cx = size/2, cy = size/2;
  const phos = accent || "var(--phosphor-500)";
  const dim  = "var(--phosphor-700)";
  const tiltY = 0.42;     // perspective squash
  const tiltX = Math.sin(t * 0.18) * 0.10 + 0.06; // tiny wobble

  // Project a polar coord onto an ellipse (axial-flux disc viewed at angle)
  const proj = (r, a) => {
    const x = Math.cos(a) * r;
    const y = Math.sin(a) * r * tiltY;
    // small parallax for wobble
    const z = Math.sin(a) * r * tiltX;
    return { x: cx + x, y: cy + y + z*0.15 };
  };

  // Stator outer + inner ring
  const outerR = size * 0.42;
  const innerR = size * 0.30;
  const rotorOR = size * 0.27;
  const rotorIR = size * 0.10;
  const hubR    = size * 0.05;

  const ringPath = (r, segments = 96) => {
    let d = "";
    for (let i = 0; i <= segments; i++) {
      const a = (i / segments) * Math.PI * 2;
      const p = proj(r, a);
      d += (i === 0 ? "M" : "L") + p.x.toFixed(2) + "," + p.y.toFixed(2) + " ";
    }
    return d + "Z";
  };

  // Stator slots (radial bars)
  const slotLines = [];
  for (let i = 0; i < slots; i++) {
    const a = (i / slots) * Math.PI * 2;
    const p1 = proj(innerR, a);
    const p2 = proj(outerR, a);
    slotLines.push(<line key={"s"+i} x1={p1.x} y1={p1.y} x2={p2.x} y2={p2.y} stroke={dim} strokeWidth="0.5" />);
  }

  // Rotor poles (alternating N/S filled wedges)
  const rotorPoles = [];
  for (let i = 0; i < poles; i++) {
    const a0 = (i / poles) * Math.PI * 2 + t;
    const a1 = ((i+1) / poles) * Math.PI * 2 + t;
    const am = (a0 + a1) / 2;
    const isN = i % 2 === 0;
    const p0in = proj(rotorIR, a0);
    const p1in = proj(rotorIR, a1);
    const p0out = proj(rotorOR, a0);
    const p1out = proj(rotorOR, a1);
    // approximate the curve with the midpoint
    const pmid_out = proj(rotorOR, am);
    const pmid_in  = proj(rotorIR, am);

    const d = `M${p0in.x},${p0in.y}
               L${p0out.x},${p0out.y}
               Q${pmid_out.x},${pmid_out.y - (isN?-1:1)} ${p1out.x},${p1out.y}
               L${p1in.x},${p1in.y}
               Q${pmid_in.x},${pmid_in.y - (isN?-1:1)} ${p0in.x},${p0in.y} Z`;
    rotorPoles.push(
      <path key={"p"+i} d={d}
            fill={isN ? phos : "transparent"}
            fillOpacity={isN ? 0.18 : 0}
            stroke={phos} strokeWidth="0.75"/>
    );
    // pole label
    rotorPoles.push(
      <text key={"pl"+i} x={proj((rotorIR+rotorOR)/2, am).x} y={proj((rotorIR+rotorOR)/2, am).y + 3}
            fill={phos} fontSize="7" fontFamily="JetBrains Mono, monospace"
            textAnchor="middle" opacity="0.75">{isN ? "N" : "S"}</text>
    );
  }

  // Tick marks around outer
  const ticks = [];
  for (let i = 0; i < 72; i++) {
    const a = (i / 72) * Math.PI * 2;
    const long = i % 6 === 0;
    const p1 = proj(outerR + (long ? 4 : 2), a);
    const p2 = proj(outerR + (long ? 12 : 6), a);
    ticks.push(<line key={"t"+i} x1={p1.x} y1={p1.y} x2={p2.x} y2={p2.y}
                     stroke={dim} strokeWidth={long ? 0.8 : 0.5}/>);
  }

  // Shaft (vertical bar through center)
  const shaftTop = proj(0, 0);
  const shaftBot = { x: cx, y: cy + size*0.46 };
  const shaftSky = { x: cx, y: cy - size*0.46 };

  return (
    <svg viewBox={`0 0 ${size} ${size}`} width="100%" height="100%"
         onMouseEnter={() => setHovered(true)}
         onMouseLeave={() => setHovered(false)}
         style={{ background: "transparent", overflow: "visible" }}>
      <defs>
        <filter id="phos-glow">
          <feGaussianBlur stdDeviation="1.2" result="b"/>
          <feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
        </filter>
      </defs>

      {/* Cross-hairs / framing */}
      <g stroke="var(--steel-700)" strokeWidth="0.5">
        <line x1="0" y1={cy} x2={size} y2={cy}/>
        <line x1={cx} y1="0" x2={cx} y2={size}/>
      </g>

      {/* Outer ticks */}
      {ticks}

      {/* Stator outer/inner rings */}
      <path d={ringPath(outerR)} fill="transparent" stroke={phos} strokeWidth="1.25" opacity="0.9"/>
      <path d={ringPath(innerR)} fill="transparent" stroke={phos} strokeWidth="0.75" opacity="0.7"/>

      {/* Slots */}
      <g>{slotLines}</g>

      {/* Rotor outer/inner (rotates) */}
      <g style={{ transformOrigin: `${cx}px ${cy}px`, filter: "url(#phos-glow)" }}>
        <path d={ringPath(rotorOR)} fill="transparent" stroke={phos} strokeWidth="1"/>
        <path d={ringPath(rotorIR)} fill="transparent" stroke={phos} strokeWidth="0.8"/>
        {rotorPoles}
      </g>

      {/* Hub */}
      <circle cx={cx} cy={cy} r={hubR} fill="var(--black-100)" stroke={phos} strokeWidth="1"/>
      <circle cx={cx} cy={cy} r={hubR*0.4} fill={phos} opacity="0.9"/>

      {/* Shaft */}
      <line x1={cx} y1={shaftSky.y} x2={cx} y2={shaftBot.y}
            stroke="var(--steel-500)" strokeWidth="3" strokeLinecap="square" opacity="0.5"/>

      {/* Datum labels */}
      <text x={cx} y={12} fontFamily="JetBrains Mono, monospace" fontSize="8"
            fill="var(--fg-muted)" textAnchor="middle" letterSpacing="3">{label}</text>
      <text x={cx} y={size-6} fontFamily="JetBrains Mono, monospace" fontSize="7"
            fill="var(--fg-faint)" textAnchor="middle" letterSpacing="2">
        ω = — rpm · Φ = — T · {slots}-slot / {poles}-pole · indicative
      </text>

      {/* Dimension lines */}
      <g stroke="var(--steel-500)" strokeWidth="0.4" opacity="0.6"
         fontFamily="JetBrains Mono, monospace" fontSize="6" fill="var(--steel-300)">
        <line x1={cx + outerR} y1={cy - 6} x2={cx + outerR + 30} y2={cy - 6}/>
        <line x1={cx + outerR} y1={cy - 4} x2={cx + outerR} y2={cy - 8}/>
        <line x1={cx + outerR + 30} y1={cy - 4} x2={cx + outerR + 30} y2={cy - 8}/>
        <text x={cx + outerR + 32} y={cy - 4} letterSpacing="1">Ø —</text>
      </g>
    </svg>
  );
}

// ------------------------------------------------------------
// FluxPlot — animated magneto-FEA-style heat map
// ------------------------------------------------------------
function FluxPlot({ width = 480, height = 280, paletteAnom = false }) {
  const canvasRef = React.useRef(null);
  const rafRef = React.useRef(0);
  const tRef = React.useRef(0);
  const lastRef = React.useRef(performance.now());

  React.useEffect(() => {
    const c = canvasRef.current;
    if (!c) return;
    const dpr = Math.min(window.devicePixelRatio || 1, 2);
    c.width = width * dpr; c.height = height * dpr;
    const ctx = c.getContext("2d");
    ctx.scale(dpr, dpr);

    // Build palette
    const palette = [];
    for (let i = 0; i < 256; i++) {
      const v = i / 255;
      let r, g, b;
      if (paletteAnom) {
        // off-spec mode — magenta drift
        r = Math.min(255, Math.floor(v * 320));
        g = Math.floor(Math.pow(v, 2.6) * 60);
        b = Math.min(255, Math.floor(60 + v * 230));
      } else {
        // black -> green phosphor -> bone
        r = Math.floor(Math.pow(v, 2.0) * 244 * (v > 0.78 ? 1 : 0.1));
        g = Math.floor(Math.min(255, v * 320));
        b = Math.floor(Math.pow(v, 2.6) * 226 + (v > 0.85 ? 30 : 0));
      }
      palette.push([Math.min(255,r), Math.min(255,g), Math.min(255,b)]);
    }

    // Static "dipoles" — pretend permanent-magnet rotor poles
    const W = width, H = height;
    const cell = 6; // resolution
    const cols = Math.ceil(W / cell);
    const rows = Math.ceil(H / cell);

    let stopped = false;
    const tick = (now) => {
      if (stopped) return;
      const dt = (now - lastRef.current) / 1000;
      lastRef.current = now;
      tRef.current += dt * 0.6;

      const t = tRef.current;
      const imgData = ctx.createImageData(W, H);
      const data = imgData.data;

      // dipoles rotating around the center
      const cxC = W*0.5, cyC = H*0.5;
      const R = Math.min(W, H) * 0.28;
      const dipoles = [];
      const N = 6;
      for (let i = 0; i < N; i++) {
        const a = (i / N) * Math.PI * 2 + t;
        dipoles.push({
          x: cxC + Math.cos(a) * R,
          y: cyC + Math.sin(a) * R * 0.55, // squash for "axial view"
          sign: i % 2 === 0 ? 1 : -1
        });
      }

      // sample at cell resolution, then upsample
      for (let r = 0; r < rows; r++) {
        const py = r * cell;
        for (let q = 0; q < cols; q++) {
          const px = q * cell;
          // sum 1/r² field magnitudes
          let mag = 0;
          for (let i = 0; i < dipoles.length; i++) {
            const d = dipoles[i];
            const dx = px - d.x, dy = py - d.y;
            const r2 = dx*dx + dy*dy + 400;
            mag += d.sign * 12000 / r2;
          }
          // contour-band: take absolute, modulate
          const v = Math.min(1, Math.max(0, (Math.abs(mag) * 0.55)));
          const idx = Math.min(255, Math.floor(v * 255));
          const [R0, G0, B0] = palette[idx];

          // fill cell
          for (let dy = 0; dy < cell; dy++) {
            for (let dx = 0; dx < cell; dx++) {
              const xx = px + dx, yy = py + dy;
              if (xx >= W || yy >= H) continue;
              const di = (yy * W + xx) * 4;
              data[di] = R0;
              data[di+1] = G0;
              data[di+2] = B0;
              data[di+3] = 255;
            }
          }
        }
      }
      ctx.putImageData(imgData, 0, 0);

      // overlay contour lines + grid
      ctx.strokeStyle = "rgba(180,180,180,0.08)";
      ctx.lineWidth = 1;
      ctx.beginPath();
      for (let x = 0; x < W; x += 24) { ctx.moveTo(x, 0); ctx.lineTo(x, H); }
      for (let y = 0; y < H; y += 24) { ctx.moveTo(0, y); ctx.lineTo(W, y); }
      ctx.stroke();

      // crosshair
      ctx.strokeStyle = "rgba(244,239,226,0.25)";
      ctx.lineWidth = 0.6;
      ctx.beginPath();
      ctx.moveTo(0, H/2); ctx.lineTo(W, H/2);
      ctx.moveTo(W/2, 0); ctx.lineTo(W/2, H);
      ctx.stroke();

      rafRef.current = requestAnimationFrame(tick);
    };
    rafRef.current = requestAnimationFrame(tick);
    return () => { stopped = true; cancelAnimationFrame(rafRef.current); };
  }, [width, height, paletteAnom]);

  return (
    <canvas ref={canvasRef}
            style={{ width: width+"px", height: height+"px", display: "block",
                     imageRendering: "pixelated", background: "var(--black-050)" }}/>
  );
}

// ------------------------------------------------------------
// ExplodedView — orthographic exploded assembly
// ------------------------------------------------------------
function ExplodedView({ width = 480, height = 320 }) {
  const [t, setT] = React.useState(0);
  React.useEffect(() => {
    let r;
    let last = performance.now();
    const step = (now) => {
      const dt = (now - last)/1000; last = now;
      setT(s => s + dt);
      r = requestAnimationFrame(step);
    };
    r = requestAnimationFrame(step);
    return () => cancelAnimationFrame(r);
  }, []);
  // breathing exploded distance
  const k = 0.5 + 0.5 * Math.sin(t * 0.5);   // 0..1
  const sep = 10 + k * 80;

  const parts = [
    { label: "BEARING CAP",  w: 80, h: 14, x: 0 },
    { label: "STATOR (FRONT)", w: 160, h: 60, x: 0 },
    { label: "ROTOR ASSY",     w: 140, h: 70, x: 0 },
    { label: "STATOR (REAR)",  w: 160, h: 60, x: 0 },
    { label: "BEARING CAP",    w: 80, h: 14, x: 0 },
  ];

  // total
  const totalH = parts.reduce((s,p) => s + p.h, 0) + (parts.length-1) * sep;
  const startY = (height - totalH) / 2;
  let y = startY;

  return (
    <svg viewBox={`0 0 ${width} ${height}`} width="100%" height="100%">
      <g stroke="var(--steel-700)" strokeWidth="0.4">
        <line x1="0" y1={height/2} x2={width} y2={height/2}/>
      </g>

      {/* assembly axis */}
      <line x1={width/2} y1={startY-30} x2={width/2} y2={startY + totalH + 30}
            stroke="var(--steel-500)" strokeWidth="1" strokeDasharray="4,3" opacity="0.7"/>

      {parts.map((p,i) => {
        const localY = y;
        y += p.h + sep;
        const cx = width/2;
        return (
          <g key={i}>
            {/* part rect */}
            <rect x={cx - p.w/2} y={localY} width={p.w} height={p.h}
                  fill="var(--black-200)" stroke="var(--phosphor-500)" strokeWidth="1"/>
            {/* hatching */}
            <rect x={cx - p.w/2} y={localY} width={p.w} height={p.h}
                  fill="url(#hatch)" opacity="0.18"/>
            {/* center hole */}
            <line x1={cx - 8} y1={localY + p.h/2} x2={cx + 8} y2={localY + p.h/2}
                  stroke="var(--phosphor-500)" strokeWidth="0.6"/>
            {/* leader */}
            <line x1={cx + p.w/2} y1={localY + p.h/2}
                  x2={cx + p.w/2 + 30} y2={localY + p.h/2}
                  stroke="var(--steel-500)" strokeWidth="0.5"/>
            <line x1={cx + p.w/2 + 30} y1={localY + p.h/2}
                  x2={width - 12} y2={localY + p.h/2}
                  stroke="var(--steel-500)" strokeWidth="0.5"/>
            <text x={width - 12} y={localY + p.h/2 + 3}
                  fontFamily="JetBrains Mono, monospace" fontSize="9"
                  fill="var(--bone-100)" textAnchor="end" letterSpacing="1">
              {String(i+1).padStart(2,"0")} · {p.label}
            </text>
          </g>
        );
      })}

      <defs>
        <pattern id="hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
          <line x1="0" y1="0" x2="0" y2="6" stroke="var(--phosphor-500)" strokeWidth="0.6"/>
        </pattern>
      </defs>
    </svg>
  );
}

// ------------------------------------------------------------
// ThermalProfile — animated thermal line + filled gradient
// ------------------------------------------------------------
function ThermalProfile({ width = 480, height = 240, anom = false }) {
  const [t, setT] = React.useState(0);
  React.useEffect(() => {
    let r, last = performance.now();
    const step = (now) => { setT(s => s + (now - last)/1000); last = now; r = requestAnimationFrame(step); };
    r = requestAnimationFrame(step);
    return () => cancelAnimationFrame(r);
  }, []);

  const N = 80;
  const pts = [];
  for (let i = 0; i < N; i++) {
    const x = (i / (N-1)) * width;
    // simulated thermal gradient peaking at center, with high-freq noise
    const base = Math.exp(-Math.pow((i/N - 0.5)*3, 2)) * 0.75;
    const n = 0.04 * Math.sin(i*0.4 + t*1.6) + 0.02 * Math.sin(i*1.3 + t*2.4);
    const v = base + n;
    const y = height - 18 - v * (height - 36);
    pts.push([x, y, v]);
  }
  const linePath = pts.map((p,i) => (i?"L":"M") + p[0].toFixed(1) + "," + p[1].toFixed(1)).join(" ");
  const fillPath = linePath + ` L${width},${height-18} L0,${height-18} Z`;

  const color = anom ? "var(--offspec-500)" : "var(--phosphor-500)";

  return (
    <svg viewBox={`0 0 ${width} ${height}`} width="100%" height="100%" preserveAspectRatio="none">
      {/* grid */}
      <g stroke="var(--steel-700)" strokeWidth="0.4" opacity="0.6">
        {Array.from({length: 6}, (_, i) =>
          <line key={"h"+i} x1="0" y1={(i+1)*height/7} x2={width} y2={(i+1)*height/7}/>
        )}
        {Array.from({length: 10}, (_, i) =>
          <line key={"v"+i} x1={(i+1)*width/11} y1="0" x2={(i+1)*width/11} y2={height-18}/>
        )}
      </g>

      {/* y-axis labels */}
      {[0, 25, 50, 75, 100, 125, 150].map((v, i) => (
        <text key={"yl"+i} x="4" y={height - 18 - (v/175) * (height - 36) + 3}
              fontFamily="JetBrains Mono, monospace" fontSize="8" fill="var(--fg-faint)" letterSpacing="1">{v}</text>
      ))}

      <path d={fillPath} fill={color} opacity="0.12"/>
      <path d={linePath} fill="none" stroke={color} strokeWidth="1.5" style={{ filter: "drop-shadow(0 0 4px " + (anom ? "#ff2bd388" : "#33ff6688") + ")" }}/>

      {/* peak marker */}
      <line x1={width/2} y1="0" x2={width/2} y2={height - 18} stroke={color} strokeDasharray="2,3" strokeWidth="0.6" opacity="0.7"/>

      {/* baseline */}
      <line x1="0" y1={height-18} x2={width} y2={height-18} stroke="var(--steel-500)" strokeWidth="0.6"/>

      {/* x-axis ticks */}
      {Array.from({length: 11}, (_, i) => (
        <g key={"xt"+i}>
          <line x1={i*width/10} y1={height-18} x2={i*width/10} y2={height-14} stroke="var(--steel-500)" strokeWidth="0.5"/>
          <text x={i*width/10} y={height-4} fontFamily="JetBrains Mono, monospace" fontSize="7"
                fill="var(--fg-faint)" textAnchor="middle">{i*40}</text>
        </g>
      ))}

      <text x={width-6} y="14" fontFamily="JetBrains Mono, monospace" fontSize="8"
            fill="var(--fg-muted)" textAnchor="end" letterSpacing="2">
        WINDING REF · INDICATIVE · NO UNITS
      </text>
    </svg>
  );
}

// ------------------------------------------------------------
// Sim scrubber wrapper — adds play/pause/scrub controls
// ------------------------------------------------------------
function SimScrubber({ children, label = "SAMPLE", duration = 60 }) {
  const [pos, setPos] = React.useState(0);
  const [playing, setPlaying] = React.useState(true);
  React.useEffect(() => {
    if (!playing) return;
    let r, last = performance.now();
    const step = (now) => {
      const dt = (now - last)/1000; last = now;
      setPos(p => (p + dt / duration) % 1);
      r = requestAnimationFrame(step);
    };
    r = requestAnimationFrame(step);
    return () => cancelAnimationFrame(r);
  }, [playing, duration]);

  const time = (pos * duration).toFixed(2);

  return (
    <div style={{ position: "relative" }}>
      {children}
      <div style={{
        display: "flex", alignItems: "center", gap: 12,
        padding: "10px 12px",
        borderTop: "1px solid var(--border-default)",
        background: "var(--black-200)",
        fontFamily: "var(--font-mono)", fontSize: 11,
        color: "var(--fg-muted)",
      }}>
        <button onClick={() => setPlaying(p => !p)}
                style={{
                  background: "transparent", border: "1px solid var(--border-default)",
                  color: "var(--phosphor-500)", cursor: "pointer",
                  width: 28, height: 22, fontFamily: "inherit", fontSize: 11,
                  padding: 0,
                }}>
          {playing ? "❚❚" : "▶"}
        </button>
        <span style={{ color: "var(--bone-100)", letterSpacing: "0.18em" }}>{label}</span>
        <input type="range" min="0" max="1000" value={pos*1000}
               onChange={e => { setPos(+e.target.value/1000); setPlaying(false); }}
               style={{ flex: 1, accentColor: "var(--phosphor-500)" }}/>
        <span className="mono-num" style={{ color: "var(--phosphor-500)" }}>
          T+{time}s / {duration.toFixed(2)}s
        </span>
      </div>
    </div>
  );
}

Object.assign(window, {
  WireframeMotor, FluxPlot, ExplodedView, ThermalProfile, SimScrubber
});
