/* ========================================================================
   LUMINA — Main app (workspaces + tabs + audio sync)
   ======================================================================== */
const { useState: useStateA, useEffect: useEffectA, useCallback: useCallbackA } = React;

const DEFAULT_PARAMS = {
  intensity: 1.0, smoothness: 0.78,
  motion: 1.0, density: 0.6, lineWidth: 2.0,
  hue: 18, glow: 1.0, bgAlpha: 0.18,
  mirror: false, kaleidoSegs: 8,
  macro1: 0.3, macro2: 0.5, macro3: 0.7, macro4: 0.2,
  text: "LUMINA",
};

const WORKSPACES = [
  { id: "stage",       label: "Stage",      icon: "▶" },
  { id: "composition", label: "Live Loops", icon: "▦" },
  { id: "slideshow",   label: "Slideshow",  icon: "▣" },
  { id: "scene3d",     label: "3D Scene",   icon: "◆" },
  { id: "text",        label: "Boards", icon: "Aa" },
  { id: "mapping",     label: "Output Map", icon: "▥" },
  { id: "lighting",    label: "Lighting", icon: "✸" },
  { id: "nodes",       label: "Nodes", icon: "⊣" },
  { id: "generative",  label: "Generative", icon: "✦" },
];

function WorkspaceTabs({ value, onChange }) {
  const P = window.LuminaPopout;
  return (
    <div className="panel" style={{
      padding: 4, borderRadius: 14,
      display: "flex", gap: 2,
      marginBottom: 6,
      overflowX: "auto",
      flexShrink: 0,
      maxWidth: "100%",
      alignItems: "center",
    }}>
      {WORKSPACES.map(w => {
        const popped = P && P.isOpen(w.id);
        return (
        <button key={w.id}
          onClick={() => onChange(w.id)}
          className="nbtn compact"
          style={{
            padding: "7px 14px",
            background: value === w.id
              ? "radial-gradient(circle at 50% 0%, #ffe39a 0%, #ffb02a 40%, #c87016 100%)"
              : "transparent",
            color: value === w.id ? "#2a1606" : popped ? "var(--led-cyan)" : "var(--ink-dim)",
            boxShadow: value === w.id
              ? "inset 0 1px 0 rgba(255,255,255,0.4), 0 0 14px rgba(255,122,26,0.6)"
              : "none",
            fontWeight: 700,
            textShadow: value === w.id ? "0 1px 0 rgba(255,255,255,0.3)" : "none",
          }}>
          <span style={{ fontSize: 13, marginRight: 6 }}>{w.icon}</span>
          {w.label}{popped ? " ⇱" : ""}
        </button>
      );})}
      <div style={{ flex: 1 }} />
      {P && value !== "stage" && (
        <button className="nbtn compact"
          title="Open this workspace in its own window — drag it to any screen, the main window keeps working"
          onClick={() => P.open(value)}
          style={{ padding: "6px 10px", color: "var(--led-cyan)", whiteSpace: "nowrap" }}>
          ⇱ Pop-out
        </button>
      )}
    </div>
  );
}

function PoppedPlaceholder({ kind, label }) {
  const P = window.LuminaPopout;
  return (
    <div className="panel" style={{ flex: 1, display: "grid", placeItems: "center", color: "var(--ink-dim)" }}>
      <Screws />
      <div style={{ textAlign: "center" }}>
        <div style={{ fontSize: 30, marginBottom: 10 }}>⇱</div>
        <div style={{ fontFamily: "var(--font-display)", fontWeight: 700,
          fontSize: 14, letterSpacing: "0.2em", textTransform: "uppercase" }}>
          {label} · external window
        </div>
        <div style={{ fontSize: 11, color: "var(--ink-faint)", marginTop: 8, marginBottom: 14 }}>
          This workspace is live in its own window — everything stays in sync.
        </div>
        <div style={{ display: "flex", gap: 8, justifyContent: "center" }}>
          <button className="nbtn compact glow pressed" onClick={() => P && P.open(kind)}>Focus window</button>
          <button className="nbtn compact" onClick={() => P && P.close(kind)}>⏏ Bring back here</button>
        </div>
      </div>
    </div>
  );
}

/* Live-show armor: a crash inside one workspace must NEVER take down the
   whole console. The boundary catches it and offers a one-click reload. */
class WorkspaceBoundary extends React.Component {
  constructor(p) { super(p); this.state = { err: null }; }
  static getDerivedStateFromError(e) { return { err: String(e && e.message || e) }; }
  componentDidCatch(e) { console.warn("Workspace crash:", e); }
  render() {
    if (this.state.err) {
      return (
        <div className="panel" style={{ flex: 1, display: "grid", placeItems: "center", color: "var(--ink-dim)" }}>
          <div style={{ textAlign: "center", maxWidth: 420 }}>
            <div style={{ fontSize: 26, marginBottom: 10 }}>⚠</div>
            <div style={{ fontFamily: "var(--font-display)", fontWeight: 700, fontSize: 13,
              letterSpacing: "0.16em", textTransform: "uppercase", color: "var(--led-red)" }}>
              Modulo in errore — il resto della console è vivo
            </div>
            <div style={{ fontFamily: "var(--font-mono)", fontSize: 9, color: "var(--ink-faint)",
              margin: "10px 0 14px", wordBreak: "break-all" }}>{this.state.err}</div>
            <button className="nbtn compact glow pressed"
              onClick={() => this.setState({ err: null })}>↺ Ricarica modulo</button>
          </div>
        </div>
      );
    }
    return this.props.children;
  }
}

function App() {
  const [vizId, setVizId] = useStateA("radial");
  const [params, setParams] = useStateA(DEFAULT_PARAMS);
  const [source, setSource] = useStateA("off");
  const [fileName, setFileName] = useStateA("");
  const [projectName, setProjectName] = useStateA("Festival Set 2026");
  const [audioState, setAudioState] = useStateA({ bass: 0, mid: 0, high: 0, energy: 0, beat: 0 });
  const [workspace, setWorkspace] = useStateA("stage");

  // export config
  const [exportOpen, setExportOpen] = useStateA(false);
  const [format, setFormat] = useStateA("MP4 H.264");
  const [res, setRes] = useStateA("1920x1080");
  const [orient, setOrient] = useStateA("landscape");
  const [fps, setFps] = useStateA(60);
  const [duration, setDuration] = useStateA(30);
  const [codec, setCodec] = useStateA("3");
  const [quality, setQuality] = useStateA(85);

  const [cast, setCast] = useStateA(false);
  const [recording, setRecording] = useStateA(false);
  const [, setPopTick] = useStateA(0);   // re-render when popout windows open/close

  useEffectA(() => {
    if (!window.LuminaPopout) return;
    return window.LuminaPopout.on(() => setPopTick(t => t + 1));
  }, []);

  /* ── LIVE vs PREVIEW (blind) editing ──────────────────────────────
     LIVE    = every tweak hits the projectors instantly.
     PREVIEW = tweaks edit a staged copy (visible in the ◐ Preview
               monitor) — nothing reaches the output until TAKE. */
  const [blind, setBlind] = useStateA(false);
  const [stagedParams, setStagedParams] = useStateA(null);
  const [stagedVizId, setStagedVizId] = useStateA(null);
  const armBlind = () => { setStagedParams({ ...params }); setStagedVizId(vizId); setBlind(true); };
  const disarmBlind = () => { setBlind(false); setStagedParams(null); setStagedVizId(null); };
  const takeBlind = () => {
    if (!stagedParams) { if (stagedVizId) setVizId(stagedVizId); return; }
    // smooth TAKE: crossfade numeric params over 0.7s instead of jump-cut
    const from = { ...params };
    const to = { ...stagedParams };
    const t0 = performance.now(), DUR = 700;
    const step = () => {
      const k = Math.min(1, (performance.now() - t0) / DUR);
      const e = k * k * (3 - 2 * k);   // smoothstep
      const p = {};
      Object.keys(to).forEach(key => {
        const a = from[key], b = to[key];
        p[key] = (typeof a === "number" && typeof b === "number") ? a + (b - a) * e : b;
      });
      setParams(p);
      if (k < 1) requestAnimationFrame(step);
      else if (stagedVizId) setVizId(stagedVizId);
    };
    requestAnimationFrame(step);
    // stay armed on the freshly-launched state so you can keep pre-editing
    setStagedParams(p => p ? { ...p } : p);
  };
  useEffectA(() => {
    window.__blind = blind
      ? { armed: true, params: stagedParams || params, vizId: stagedVizId || vizId }
      : { armed: false };
  }, [blind, stagedParams, stagedVizId, params, vizId]);

  const [volume, setVolumeS] = useStateA(0.85);
  const [muted, setMutedS] = useStateA(false);

  // === SYNC source from AudioEngine.mode (so STOP button works)
  useEffectA(() => {
    // Expose snapshot/apply hooks for project I/O + quick launcher
    window.__paramsSnapshot = () => params;
    window.__paramsApply = (p) => setParams(p);
    window.__vizIdSnapshot = () => vizId;
    window.__vizIdApply = (id) => setVizId(id);
    window.__workspaceSnapshot = () => workspace;
    window.__workspaceApply = (w) => setWorkspace(w);
    window.__projectNameSnapshot = () => projectName;
    window.__projectNameApply = (n) => setProjectName(n);
    window.__setVizId = setVizId;
    window.__setWorkspace = setWorkspace;
    window.__resetParams = () => setParams(DEFAULT_PARAMS);
  }, [params, vizId, workspace, projectName]);

  // === SYNC source from AudioEngine.mode (so STOP button works)
  useEffectA(() => {
    const sync = () => {
      setSource(window.AudioEngine.mode);
      setFileName(window.AudioEngine.fileName || "");
    };
    const unsub = window.AudioEngine.onChange(sync);
    sync();
    return unsub;
  }, []);

  // === Audio state polling for meters
  useEffectA(() => {
    let raf;
    const tick = () => {
      const A = window.AudioEngine;
      setAudioState({ bass: A.bass, mid: A.mid, high: A.high, energy: A.energy, beat: A.beat });
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, []);

  const ensure = () => {
    if (!window.AudioEngine.ctx) window.AudioEngine.init();
    if (window.AudioEngine.ctx.state === "suspended") window.AudioEngine.ctx.resume();
  };

  const blindRef = window.__blindRef = window.__blindRef || {};
  blindRef.armed = blind;
  const onParam = useCallbackA((k, v) => {
    if (blindRef.armed) {
      // PREVIEW: edit the staged copy only — output untouched until TAKE
      setStagedParams(p => ({ ...(p || {}), [k]: v }));
      return;
    }
    setParams(p => {
      const next = { ...p, [k]: v };
      if (k === "smoothness") window.AudioEngine.setSmoothing(v);
      return next;
    });
  }, []);
  const routedSetVizId = useCallbackA((id) => {
    if (blindRef.armed) setStagedVizId(id);
    else setVizId(id);
  }, []);

  const onLoadFile = async (file) => { ensure(); await window.AudioEngine.useFile(file); };
  const onMic = async () => { ensure(); await window.AudioEngine.useMic(); };
  const onDemo = () => { ensure(); window.AudioEngine.useDemo(); };
  const setVolume = (v) => { setVolumeS(v); window.AudioEngine.setVolume(v); };
  const setMuted = (m) => { setMutedS(m); window.AudioEngine.setMuted(m); };

  useEffectA(() => {
    const onKey = (e) => {
      if (e.key === "Escape" && cast) setCast(false);
      if (e.key === " " && document.activeElement?.tagName !== "INPUT" && document.activeElement?.tagName !== "TEXTAREA") {
        e.preventDefault(); window.AudioEngine.toggle();
      }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [cast]);

  const onRender = () => {
    setRecording(true);
    setTimeout(() => setRecording(false), 4000);
  };

  // Workspace router
  let workspaceEl = null;
  const _popped = window.LuminaPopout && workspace !== "stage" && window.LuminaPopout.isOpen(workspace);
  if (_popped) {
    workspaceEl = <PoppedPlaceholder kind={workspace}
      label={(WORKSPACES.find(w => w.id === workspace) || {}).label || workspace} />;
  } else if (workspace === "stage") {
    workspaceEl = (
      <Stage
        vizId={vizId} params={params} onParam={onParam}
        cast={cast} setCast={setCast}
        recording={recording} setRecording={setRecording}
        exportFormat={format} exportRes={res} exportOrient={orient}
        audio={audioState} fileName={fileName} projectName={projectName}
      />
    );
  } else if (workspace === "composition") {
    workspaceEl = <CompositionDeck vizId={vizId} setVizId={setVizId} params={params} onParam={onParam} audio={audioState} />;
  } else if (workspace === "scene3d") {
    workspaceEl = window.Scene3D ? <Scene3D params={params} audio={audioState} /> : <PlaceholderPanel name="3D Scene" />;
  } else if (workspace === "text") {
    workspaceEl = window.TextEditor ? <TextEditor audio={audioState} params={params} /> : <PlaceholderPanel name="Boards" />;
  } else if (workspace === "generative") {
    workspaceEl = window.GenerativePanel ? <GenerativePanel /> : <PlaceholderPanel name="Generative AI" />;
  } else if (workspace === "slideshow") {
    workspaceEl = window.Slideshow ? <Slideshow audio={audioState} params={params} /> : <PlaceholderPanel name="Slideshow" />;
  } else if (workspace === "mapping") {
    workspaceEl = window.MappingWorkspace ? <MappingWorkspace audio={audioState} params={params} vizId={vizId} /> : <PlaceholderPanel name="Output Mapping" />;
  } else if (workspace === "nodes") {
    workspaceEl = window.NodesWorkspace ? <NodesWorkspace /> : <PlaceholderPanel name="Nodes" />;
  } else if (workspace === "lighting") {
    workspaceEl = window.LightingWorkspace ? <LightingWorkspace audio={audioState} /> : <PlaceholderPanel name="Lighting" />;
  }

  return (
    <div className="app">
      <div className="shell">
        <div className="topbar">
          <TopBar
            projectName={projectName} setProjectName={setProjectName}
            audio={audioState} fileName={fileName}
            onLoadFile={onLoadFile} onMic={onMic} onDemo={onDemo}
            openExport={() => setExportOpen(true)}
            source={source}
          />
        </div>

        <div className="rack">
          {window.ChannelsRack
            ? <ChannelsRack vizId={blind ? (stagedVizId || vizId) : vizId} setVizId={routedSetVizId} />
            : <PresetRack vizId={blind ? (stagedVizId || vizId) : vizId} setVizId={routedSetVizId} />}
        </div>

        <div className="stage" style={{ display: "flex", flexDirection: "column", gap: 0 }}>
          <WorkspaceTabs value={workspace} onChange={setWorkspace} />
          <div style={{ flex: 1, minHeight: 0, display: "flex", flexDirection: "column" }}>
            <WorkspaceBoundary key={workspace}>{workspaceEl}</WorkspaceBoundary>
          </div>
        </div>

        <div className="params">
          <ParamDock params={blind ? (stagedParams || params) : params} onParam={onParam}
            vizId={blind ? (stagedVizId || vizId) : vizId} />
        </div>

        <div className="transport">
          <Transport audio={audioState} source={source}
            volume={volume} setVolume={setVolume}
            muted={muted} setMuted={setMuted} />
        </div>
      </div>

      <ExportModal
        open={exportOpen} onClose={() => setExportOpen(false)}
        format={format} setFormat={setFormat}
        res={res} setRes={setRes}
        orient={orient} setOrient={setOrient}
        fps={fps} setFps={setFps}
        duration={duration} setDuration={setDuration}
        codec={codec} setCodec={setCodec}
        quality={quality} setQuality={setQuality}
        onRender={onRender}
      />

      {/* ── EDIT MODE banner: impossible to miss ── */}
      <div style={{
        position: "fixed", bottom: 104, left: "50%", transform: "translateX(-50%)",
        zIndex: 60, display: "flex", gap: 8, alignItems: "center",
        padding: "5px 8px 5px 12px",
        background: blind
          ? "linear-gradient(180deg, rgba(80,52,4,0.96), rgba(46,30,3,0.96))"
          : "linear-gradient(180deg, rgba(58,8,8,0.92), rgba(30,4,4,0.92))",
        borderRadius: 999,
        border: blind ? "1px solid rgba(255,174,58,0.7)" : "1px solid rgba(255,59,59,0.55)",
        boxShadow: blind
          ? "0 4px 18px rgba(0,0,0,0.55), 0 0 16px rgba(255,174,58,0.35)"
          : "0 4px 18px rgba(0,0,0,0.55), 0 0 14px rgba(255,59,59,0.3)",
      }}>
        <span style={{
          width: 9, height: 9, borderRadius: "50%",
          background: blind ? "var(--led-amber)" : "var(--led-red)",
          boxShadow: blind ? "0 0 8px var(--led-amber)" : "0 0 8px var(--led-red)",
          animation: blind ? "none" : "pulse 1.1s infinite",
        }} />
        <span style={{
          fontFamily: "var(--font-display)", fontWeight: 800, fontSize: 10,
          letterSpacing: "0.18em", textTransform: "uppercase",
          color: blind ? "var(--led-amber)" : "var(--led-red)",
        }}>
          {blind ? "EDIT: PREVIEW — knobs stay blind" : "EDIT: LIVE — knobs hit the output"}
        </span>
        {blind ? (
          <>
            <button className="nbtn compact glow pressed" onClick={takeBlind}
              title="Launch the staged look to the live output"
              style={{ padding: "4px 14px", fontWeight: 800, color: "var(--led-red)" }}>
              ▶ TAKE
            </button>
            <button className="nbtn compact" onClick={disarmBlind}
              title="Back to LIVE editing (discard staged changes)"
              style={{ padding: "4px 8px", fontSize: 9 }}>
              ✕
            </button>
          </>
        ) : (
          <button className="nbtn compact" onClick={armBlind}
            title="Switch to PREVIEW: edit knobs/visualizer blind, then TAKE to launch — open the ◐ Preview monitor to watch the staged look"
            style={{ padding: "4px 10px", fontSize: 9, color: "var(--led-amber)" }}>
            ◐ Edit in preview
          </button>
        )}
      </div>

      {source === "off" && workspace === "stage" && (
        <div style={{
          position: "fixed", top: 88, left: "50%", transform: "translateX(-50%)",
          zIndex: 50,
          padding: "10px 16px",
          background: "linear-gradient(180deg, var(--panel-hi), var(--panel))",
          borderRadius: "var(--r-md)",
          boxShadow: "var(--nshadow-out)",
          display: "flex", gap: 10, alignItems: "center",
          fontSize: 12, color: "var(--ink-dim)",
        }}>
          <Led on color="amber" />
          <span>Pick a source to begin</span>
          <button className="nbtn compact glow pressed" onClick={onDemo}>▶ Start Demo</button>
          <button className="nbtn compact" onClick={onMic}>Use Mic</button>
          <span style={{ fontSize: 10, color: "var(--ink-faint)" }}>· Stop via top-bar STOP button</span>
        </div>
      )}
    </div>
  );
}

function PlaceholderPanel({ name }) {
  return (
    <div className="panel" style={{ flex: 1, display: "grid", placeItems: "center", color: "var(--ink-dim)" }}>
      <Screws />
      <div style={{ textAlign: "center" }}>
        <div className="brand-mark" style={{ margin: "0 auto 14px", width: 56, height: 56, fontSize: 22 }}>L</div>
        <div style={{ fontFamily: "var(--font-display)", fontWeight: 700,
          fontSize: 14, letterSpacing: "0.2em", textTransform: "uppercase" }}>
          {name}
        </div>
        <div style={{ fontFamily: "var(--font-mono)", fontSize: 10, color: "var(--ink-faint)", marginTop: 6 }}>
          loading module…
        </div>
      </div>
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
