/* ========================================================================
   LUMINA — Primitive components (Knob, Fader, LED, VU, Flip, Chips)
   Exported to window for cross-file access.
   ======================================================================== */
const { useState, useEffect, useRef, useCallback, useMemo } = React;

/* ---------- helpers ---------- */
function clamp(v, lo, hi) { return Math.max(lo, Math.min(hi, v)); }
function fmt(v, digits = 0) {
  if (Math.abs(v) >= 100) return v.toFixed(0);
  if (Math.abs(v) >= 10)  return v.toFixed(Math.max(0, digits));
  return v.toFixed(digits);
}

/* ---------- Knob (rotary) ---------- */
function Knob({
  value, onChange, min = 0, max = 1, step = 0.001,
  size = "md", label, unit = "", display, bipolar = false,
  midiId,
}) {
  const ref = useRef(null);
  const startRef = useRef({ y: 0, v: value });
  const sizeClass = size === "lg" ? "knob-large" : size === "sm" ? "knob-small" : "";
  const norm = (value - min) / (max - min);
  const angle = -135 + norm * 270;
  const [learning, setLearning] = useState(false);
  const [bound, setBound] = useState(false);

  // Register as MIDI target if midiId provided
  useEffect(() => {
    if (!midiId || !window.MidiTargets) return;
    window.MidiTargets[midiId] = {
      onValue: (v01) => onChange(min + clamp(v01, 0, 1) * (max - min)),
    };
    const check = () => setBound(!!window.LuminaMidi?.findBinding?.(midiId));
    check();
    const unsub = window.LuminaMidi?.on?.(check);
    return () => { unsub?.(); delete window.MidiTargets[midiId]; };
  }, [midiId, min, max, onChange]);

  const onContextMenu = (e) => {
    if (!midiId || !window.LuminaMidi) return;
    e.preventDefault();
    if (bound) {
      if (confirm(`Unbind MIDI from "${label || midiId}"?`)) {
        window.LuminaMidi.unbind(midiId);
        setBound(false);
      }
      return;
    }
    setLearning(true);
    window.LuminaMidi.startLearn({
      id: midiId, kind: "knob",
      onValue: (v01) => onChange(min + clamp(v01, 0, 1) * (max - min)),
    });
    setTimeout(() => setLearning(false), 15_000);
  };

  const dragMove = useCallback((e) => {
    e.preventDefault?.();
    const y = e.touches ? e.touches[0].clientY : e.clientY;
    const dy = startRef.current.y - y;
    const range = max - min;
    const sens = e.shiftKey ? 0.25 : 1;
    const next = clamp(startRef.current.v + (dy / 140) * range * sens, min, max);
    onChange(step >= 1 ? Math.round(next) : next);
  }, [min, max, step, onChange]);
  const dragEnd = useCallback(() => {
    window.removeEventListener("mousemove", dragMove);
    window.removeEventListener("touchmove", dragMove);
    window.removeEventListener("mouseup", dragEnd);
    window.removeEventListener("touchend", dragEnd);
  }, [dragMove]);
  const dragStart = (e) => {
    const y = e.touches ? e.touches[0].clientY : e.clientY;
    startRef.current = { y, v: value };
    window.addEventListener("mousemove", dragMove);
    window.addEventListener("touchmove", dragMove, { passive: false });
    window.addEventListener("mouseup", dragEnd);
    window.addEventListener("touchend", dragEnd);
  };
  const onDouble = () => onChange((max + min) / (bipolar ? 2 : 2));

  // arc dashoffset
  const radius = 30;
  const circ = 2 * Math.PI * radius;
  const arcLen = (270 / 360) * circ;
  let fillLen, fillStart;
  if (bipolar) {
    const center = arcLen / 2;
    const halfNorm = (norm - 0.5) * 2;
    fillLen = Math.abs(halfNorm) * (arcLen / 2);
    fillStart = halfNorm >= 0 ? center : center - fillLen;
  } else {
    fillLen = norm * arcLen;
    fillStart = 0;
  }

  return (
    <div className="knob-wrap">
      <div className={`knob ${sizeClass}`} ref={ref}
        onMouseDown={dragStart} onTouchStart={dragStart}
        onDoubleClick={onDouble} onContextMenu={onContextMenu}
        style={{
          boxShadow: learning
            ? "0 0 0 2px var(--accent), 0 0 14px var(--accent-glow)"
            : bound
              ? "0 0 0 1.5px var(--led-cyan)"
              : undefined,
        }}>
        <div className="knob-ring">
          <svg viewBox="0 0 70 70">
            <circle className="track" cx="35" cy="35" r={radius}
              strokeDasharray={`${arcLen} ${circ}`} />
            <circle className="fill" cx="35" cy="35" r={radius}
              strokeDasharray={`${fillLen} ${circ}`}
              strokeDashoffset={-fillStart} />
          </svg>
        </div>
        <div className="knob-body" />
        <div className="knob-indicator" style={{ "--angle": `${angle}deg` }} />
        {(learning || bound) && (
          <div style={{
            position: "absolute", top: -4, right: -4,
            fontFamily: "var(--font-mono)", fontSize: 7, fontWeight: 700,
            color: learning ? "var(--accent)" : "var(--led-cyan)",
            background: "rgba(0,0,0,0.7)",
            padding: "1px 4px", borderRadius: 2, lineHeight: 1,
            textShadow: `0 0 4px ${learning ? "var(--accent-glow)" : "var(--led-cyan-glow)"}`,
          }}>{learning ? "LRN" : "M"}</div>
        )}
      </div>
      {label && <div className="knob-label">{label}</div>}
      <div className="knob-value">
        {display ? display(value) : `${fmt(value, step < 0.1 ? 2 : step < 1 ? 1 : 0)}${unit}`}
      </div>
    </div>
  );
}

/* ---------- Fader (vertical) ---------- */
function Fader({ value, onChange, min = 0, max = 1, label, height = 110 }) {
  const ref = useRef(null);
  const startRef = useRef({ y: 0, v: value });
  const norm = clamp((value - min) / (max - min), 0, 1);
  const fillPct = `${norm * 100}%`;

  const onMove = useCallback((e) => {
    e.preventDefault?.();
    const rect = ref.current.getBoundingClientRect();
    const y = e.touches ? e.touches[0].clientY : e.clientY;
    const n = clamp(1 - (y - rect.top) / rect.height, 0, 1);
    onChange(min + n * (max - min));
  }, [min, max, onChange]);
  const onEnd = useCallback(() => {
    window.removeEventListener("mousemove", onMove);
    window.removeEventListener("touchmove", onMove);
    window.removeEventListener("mouseup", onEnd);
    window.removeEventListener("touchend", onEnd);
  }, [onMove]);
  const onStart = (e) => {
    onMove(e);
    window.addEventListener("mousemove", onMove);
    window.addEventListener("touchmove", onMove, { passive: false });
    window.addEventListener("mouseup", onEnd);
    window.addEventListener("touchend", onEnd);
  };

  return (
    <div className="fader-wrap">
      <div className="fader-track" ref={ref}
        onMouseDown={onStart} onTouchStart={onStart}
        style={{ "--fader-h": `${height}px` }}>
        <div className="fader-fill" style={{ "--fill": fillPct }} />
        <div className="fader-cap" style={{ "--fill": fillPct }} />
      </div>
      {label && <div className="knob-label">{label}</div>}
    </div>
  );
}

/* ---------- HSlider (horizontal slider) ---------- */
function HSlider({ value, onChange, min = 0, max = 1, label }) {
  const ref = useRef(null);
  const norm = clamp((value - min) / (max - min), 0, 1);
  const fill = `${norm * 100}%`;

  const onMove = useCallback((e) => {
    e.preventDefault?.();
    const rect = ref.current.getBoundingClientRect();
    const x = e.touches ? e.touches[0].clientX : e.clientX;
    const n = clamp((x - rect.left) / rect.width, 0, 1);
    onChange(min + n * (max - min));
  }, [min, max, onChange]);
  const onEnd = useCallback(() => {
    window.removeEventListener("mousemove", onMove);
    window.removeEventListener("touchmove", onMove);
    window.removeEventListener("mouseup", onEnd);
    window.removeEventListener("touchend", onEnd);
  }, [onMove]);
  const onStart = (e) => {
    onMove(e);
    window.addEventListener("mousemove", onMove);
    window.addEventListener("touchmove", onMove, { passive: false });
    window.addEventListener("mouseup", onEnd);
    window.addEventListener("touchend", onEnd);
  };

  return (
    <div className="hslider" ref={ref}
      onMouseDown={onStart} onTouchStart={onStart}>
      <div className="hslider-track">
        <div className="hslider-fill" style={{ "--fill": fill }} />
        <div className="hslider-thumb" style={{ "--fill": fill }} />
      </div>
    </div>
  );
}

/* ---------- LED dot ---------- */
function Led({ on, color = "green", size = 8 }) {
  return (
    <div className={`led led-${color} ${on ? "on" : ""}`}
      style={{ width: size, height: size }} />
  );
}

/* ---------- VU meter ladder ---------- */
function VU({ level, segments = 14 }) {
  const lit = Math.round(clamp(level, 0, 1) * segments);
  const segs = [];
  for (let i = 0; i < segments; i++) {
    const k = i / (segments - 1);
    const klass = k > 0.85 ? "r" : k > 0.6 ? "a" : "g";
    segs.push(<div key={i} className={`vu-seg ${klass} ${i < lit ? "on" : ""}`} />);
  }
  return <div className="vu">{segs}</div>;
}

/* ---------- Flip switch ---------- */
function Flip({ value, onChange, label }) {
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
      <div className={`flip ${value ? "on" : ""}`} onClick={() => onChange(!value)}>
        <div className="flip-lever" />
      </div>
      {label && <div className="cap lit">{label}</div>}
    </div>
  );
}

/* ---------- Chips (segmented selector) ---------- */
function Chips({ value, onChange, options }) {
  return (
    <div className="chips">
      {options.map(o => {
        const v = typeof o === "string" ? o : o.value;
        const label = typeof o === "string" ? o : o.label;
        return (
          <button key={v}
            className={`chip ${v === value ? "active" : ""}`}
            onClick={() => onChange(v)}>{label}</button>
        );
      })}
    </div>
  );
}

/* ---------- LCD readout ---------- */
function Lcd({ children, tone = "amber", style }) {
  return <div className={`lcd ${tone}`} style={style}>{children}</div>;
}

/* ---------- Screws (corners of a panel) ---------- */
function Screws() {
  return (
    <>
      <div className="screw tl" /><div className="screw tr" />
      <div className="screw bl" /><div className="screw br" />
    </>
  );
}

Object.assign(window, {
  Knob, Fader, HSlider, Led, VU, Flip, Chips, Lcd, Screws,
  clamp, fmt,
});
