/* ========================================================================
   LUMINA — Stage panel (canvas + overlays + cast/fullscreen)
   ======================================================================== */
const { useEffect: useEffectS, useRef: useRefS, useState: useStateS } = React;

function Stage({ vizId, params, onParam, cast, setCast, recording, setRecording,
                 exportFormat, exportRes, exportOrient, audio, fileName, projectName,
                 liveMode, requestGoLive, panicKill }) {
  const canvasRef = useRefS(null);
  const fsCanvasRef = useRefS(null);
  const offRef = useRefS(null);
  const rafRef = useRefS(0);
  const tStartRef = useRefS(performance.now());
  const [tab, setTab] = useStateS("preview"); // preview | master | output

  // animation loop
  useEffectS(() => {
    const cnv = canvasRef.current;
    const fcnv = fsCanvasRef.current;
    if (!cnv) return;
    const ctx = cnv.getContext("2d");
    const A = window.AudioEngine;

    function resize() {
      const parent = cnv.parentElement;
      if (!parent) return;
      const r = parent.getBoundingClientRect();
      if (r.width < 4 || r.height < 4) {
        requestAnimationFrame(resize);
        return;
      }
      const dpr = Math.min(2, window.devicePixelRatio || 1);
      cnv.width  = Math.max(2, Math.floor(r.width * dpr));
      cnv.height = Math.max(2, Math.floor(r.height * dpr));
      // Match CSS size to layout box — using px not %
      cnv.style.width = r.width + "px";
      cnv.style.height = r.height + "px";
      cnv.style.display = "block";
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
      if (fcnv && cast) {
        fcnv.width  = window.innerWidth;
        fcnv.height = window.innerHeight;
      }
    }
    // Multi-pass resize to catch post-layout reflows
    resize();
    requestAnimationFrame(resize);
    setTimeout(resize, 100);
    setTimeout(resize, 400);
    const ro = new ResizeObserver(resize);
    ro.observe(cnv.parentElement);
    window.addEventListener("resize", resize);

    const tick = () => {
      const tStart = performance.now();
      const t = (tStart - tStartRef.current) / 1000;
      // Per-frame resync — catches stale buffer/CSS mismatches reliably.
      const parent = cnv.parentElement;
      const dpr = Math.min(2, window.devicePixelRatio || 1);
      if (parent) {
        const cw = parent.clientWidth, chh = parent.clientHeight;
        if (cw > 4 && chh > 4) {
          const wantW = Math.floor(cw * dpr);
          const wantH = Math.floor(chh * dpr);
          if (cnv.width !== wantW || cnv.height !== wantH) {
            cnv.width = wantW; cnv.height = wantH;
            cnv.style.width = cw + "px";
            cnv.style.height = chh + "px";
            ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
          }
        }
      }
      const w = cnv.width / dpr;
      const h = cnv.height / dpr;
      if (w < 4 || h < 4) {
        rafRef.current = requestAnimationFrame(tick);
        return;
      }
      const viz = window.VISUALIZERS.find(v => v.id === vizId) || window.VISUALIZERS[0];
      const audioState = {
        freq: A.freq, wave: A.wave,
        bass: A.bass, mid: A.mid, high: A.high,
        energy: A.energy, beat: A.beat
      };
      const computedParams = window.Prog ? window.Prog.computeParams(params, audioState, t) : params;

      // ── Layered compositor (Resolume-style) ──
      // Renders every enabled layer with its own source + opacity + blend mode.
      // Falls back to single viz when stack unavailable or only Main is active.
      const composite = (tctx, tw, th, tdpr) => {
        const stack = window.LuminaStack;
        const VIS = window.VISUALIZERS;
        if (!stack || !stack.layers || !stack.layers.length) {
          viz.draw(tctx, tw, th, audioState, computedParams, t);
          return;
        }
        const anySolo = stack.layers.some(l => l.solo);
        const active = stack.layers.filter(l => anySolo ? l.solo : l.enabled);
        if (!active.length) { tctx.clearRect(0, 0, tw, th); return; }
        // Single opaque normal layer → draw direct (fast path, identical to legacy)
        if (active.length === 1 && active[0].opacity >= 0.999 && active[0].blend === "normal") {
          const vid = (active[0].source && active[0].source !== "stage") ? active[0].source : vizId;
          const v = VIS.find(x => x.id === vid) || viz;
          v.draw(tctx, tw, th, audioState, computedParams, t);
          return;
        }
        // Multi-layer: render each to offscreen, blend onto target
        let off = offRef.current;
        if (!off) { off = offRef.current = document.createElement("canvas"); }
        if (off.width !== tw * tdpr || off.height !== th * tdpr) {
          off.width = tw * tdpr; off.height = th * tdpr;
        }
        const octx = off.getContext("2d");
        tctx.clearRect(0, 0, tw, th);
        active.forEach((layer, idx) => {
          const vid = (layer.source && layer.source !== "stage") ? layer.source : vizId;
          const v = VIS.find(x => x.id === vid) || viz;
          octx.setTransform(tdpr, 0, 0, tdpr, 0, 0);
          octx.globalAlpha = 1; octx.globalCompositeOperation = "source-over";
          octx.clearRect(0, 0, tw, th);
          v.draw(octx, tw, th, audioState, computedParams, t);
          tctx.globalAlpha = Math.max(0, Math.min(1, layer.opacity));
          tctx.globalCompositeOperation = (idx === 0 || layer.blend === "normal") ? "source-over" : layer.blend;
          tctx.drawImage(off, 0, 0, tw, th);
        });
        tctx.globalAlpha = 1; tctx.globalCompositeOperation = "source-over";
      };

      composite(ctx, w, h, dpr);
      // Apply FX chain (Bloom, Pixelate, Chromatic, Echo, Kaleido, Dither, etc.)
      if (window.Prog && window.Prog.fxChain.length) {
        window.Prog.applyFXChain(ctx, w, h, audioState);
      }
      // Visual stingers (one-shot overlays from Live Loops triggers)
      if (window.Stingers) window.Stingers.tick(ctx, w, h);
      // Strobo overlay
      if (window.Strobo) { window.Strobo.tick(audioState, t); window.Strobo.render(ctx, w, h); }
      // mirror to fullscreen canvas if casting AND we're allowed to (liveMode)
      if (cast && fcnv && liveMode) {
        const fctx = fcnv.getContext("2d");
        const fw = fcnv.width, fh = fcnv.height;
        composite(fctx, fw, fh, 1);
        if (window.Prog && window.Prog.fxChain.length) window.Prog.applyFXChain(fctx, fw, fh, audioState);
        if (window.Stingers) window.Stingers.tick(fctx, fw, fh);
        if (window.Strobo) window.Strobo.render(fctx, fw, fh);
      }
      // Report frame time to Perf monitor
      if (window.Perf) window.Perf.report(performance.now() - tStart);
      rafRef.current = requestAnimationFrame(tick);
    };
    rafRef.current = requestAnimationFrame(tick);
    return () => {
      cancelAnimationFrame(rafRef.current);
      ro.disconnect();
      window.removeEventListener("resize", resize);
    };
  }, [vizId, params, cast, liveMode]);

  // export resolution display
  const [rw, rh] = exportRes.split("x").map(Number);
  const oriented = exportOrient === "portrait" ? [rh, rw] : [rw, rh];

  return (
    <div className="panel stage-bezel" style={{ padding: 0,
      boxShadow: liveMode
        ? "var(--shadow-out), 0 0 0 2px var(--led-red), 0 0 30px rgba(255,59,59,0.4)"
        : undefined,
    }}>
      <Screws />
      <div className="stage-toolbar">
        <div className="stage-tabs">
          {["preview", "master", "output"].map(t => (
            <div key={t} className={`stage-tab ${tab === t ? "active" : ""}`}
              onClick={() => setTab(t)}>{t}</div>
          ))}
        </div>
        <div style={{ flex: 1 }} />
        {/* PREVIEW / ON AIR indicator */}
        <div style={{
          padding: "4px 10px",
          background: liveMode
            ? "radial-gradient(circle at 50% 0%, #ffc4c4 0%, #ff3b3b 60%, #8a1818 100%)"
            : "linear-gradient(180deg, var(--panel-hi), var(--panel))",
          color: liveMode ? "#fff" : "var(--ink-dim)",
          borderRadius: 6,
          boxShadow: liveMode
            ? "inset 0 1px 0 rgba(255,255,255,0.4), 0 0 14px rgba(255,59,59,0.7)"
            : "var(--nshadow-in-sm)",
          fontFamily: "var(--font-display)", fontSize: 10, fontWeight: 800,
          letterSpacing: "0.2em", textTransform: "uppercase",
          display: "flex", alignItems: "center", gap: 6,
          textShadow: liveMode ? "0 0 6px rgba(255,255,255,0.5)" : "none",
        }}>
          <span style={{
            width: 7, height: 7, borderRadius: "50%",
            background: liveMode ? "#fff" : "var(--ink-faint)",
            boxShadow: liveMode ? "0 0 8px #fff" : "none",
            animation: liveMode ? "pulse 1.2s infinite" : "none",
          }} />
          {liveMode ? "On Air" : "Preview"}
        </div>
        <Lcd tone="cyan" style={{ fontSize: 10 }}>
          {oriented[0]}×{oriented[1]} · {exportFormat.toUpperCase()}
        </Lcd>
        <button className="nbtn compact" title={liveMode ? "Mirror Live to second display" : "Cast disabled in Preview mode"}
          disabled={!liveMode}
          style={{ opacity: liveMode ? 1 : 0.4, cursor: liveMode ? "pointer" : "not-allowed" }}
          onClick={() => liveMode && setCast(true)}>
          <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
            <rect x="2" y="4" width="20" height="14" rx="2"/>
            <path d="M2 18 L12 8 L22 18" />
          </svg>
          Cast
        </button>
        <button className={`nbtn compact ${recording ? "danger pressed" : ""}`}
          disabled={!liveMode}
          style={{ opacity: liveMode ? 1 : 0.4, cursor: liveMode ? "pointer" : "not-allowed" }}
          onClick={() => liveMode && setRecording(r => !r)}>
          <span style={{
            display: "inline-block", width: 9, height: 9, borderRadius: "50%",
            background: recording ? "var(--led-red)" : "var(--ink-faint)",
            boxShadow: recording ? "0 0 8px var(--led-red-glow)" : "none",
          }} />
          {recording ? "REC" : "RECORD"}
        </button>
      </div>

      <div className="screen-frame">
        <canvas ref={canvasRef} />
        <div className="screen-overlay" />
        <div className="screen-corner" style={{ top: 10, left: 14 }}>
          {liveMode ? "● ON AIR" : "○ PREVIEW (not transmitting)"} · {window.VISUALIZERS.find(v => v.id === vizId)?.name || "—"}
        </div>
        <div className="screen-corner" style={{ top: 10, right: 14, textAlign: "right" }}>
          {projectName} · {(audio?.energy * 100 || 0).toFixed(0)} dB
        </div>
        <div className="screen-corner" style={{ bottom: 10, left: 14 }}>
          ♫ {fileName || "no signal"}
        </div>
        <div className="screen-corner" style={{ bottom: 10, right: 14 }}>
          {oriented[0]}×{oriented[1]} @ 60fps
        </div>
        {recording && (
          <div style={{
            position: "absolute", top: 10, left: "50%", transform: "translateX(-50%)",
            background: "rgba(255,0,30,0.18)", padding: "4px 10px", borderRadius: 999,
            border: "1px solid var(--led-red)", fontFamily: "var(--font-mono)",
            fontSize: 10, color: "var(--led-red)",
            textShadow: "0 0 6px var(--led-red-glow)",
            display: "flex", alignItems: "center", gap: 6
          }}>
            <span style={{
              width: 8, height: 8, borderRadius: "50%",
              background: "var(--led-red)",
              boxShadow: "0 0 8px var(--led-red-glow)",
              animation: "pulse 1s infinite"
            }} />
            REC · {exportFormat.toUpperCase()} · {oriented[0]}×{oriented[1]}
          </div>
        )}
      </div>

      <div className="stage-strip" style={{ gap: 16 }}>
        <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
          <span className="cap">FX</span>
          <Flip value={params.glow > 0.5} onChange={v => onParam("glow", v ? 1 : 0.2)} />
          <span className="cap lit">Glow</span>
        </div>
        <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
          <Flip value={!!params.mirror} onChange={v => onParam("mirror", v)} />
          <span className="cap lit">Mirror</span>
        </div>
        <div style={{ flex: 1 }} />
        {/* GO LIVE / PANIC */}
        {!liveMode ? (
          <button onClick={requestGoLive}
            style={{
              padding: "8px 16px", border: "none", borderRadius: 8,
              background: "radial-gradient(circle at 50% 0%, #ffc4c4 0%, #ff3b3b 40%, #8a1818 100%)",
              color: "#fff", fontFamily: "var(--font-display)",
              fontSize: 11, fontWeight: 800, letterSpacing: "0.16em",
              textTransform: "uppercase", cursor: "pointer",
              boxShadow: "inset 0 1px 0 rgba(255,255,255,0.3), 0 0 14px rgba(255,59,59,0.5)",
              textShadow: "0 1px 0 rgba(0,0,0,0.4)",
              display: "flex", alignItems: "center", gap: 6,
            }}>
            <span style={{ width: 8, height: 8, borderRadius: "50%", background: "#fff" }} />
            Go Live
          </button>
        ) : (
          <button onClick={panicKill}
            style={{
              padding: "8px 16px", border: "2px solid #ff3b3b", borderRadius: 8,
              background: "linear-gradient(180deg, #2a0a0a, #1a0606)",
              color: "var(--led-red)", fontFamily: "var(--font-display)",
              fontSize: 11, fontWeight: 800, letterSpacing: "0.16em",
              textTransform: "uppercase", cursor: "pointer",
              textShadow: "0 0 6px var(--led-red-glow)",
              animation: "pulse 1.5s infinite",
            }}>
            ■ Kill Output
          </button>
        )}
        <div style={{ display: "flex", gap: 4, alignItems: "center" }}>
          <Led on={audio?.beat > 0.4} color="red" />
          <span className="cap lit" style={{ marginRight: 4 }}>Beat</span>
          <Led on={audio?.bass > 0.3} color="amber" />
          <span className="cap lit" style={{ marginRight: 4 }}>Bass</span>
          <Led on={audio?.mid > 0.3} color="green" />
          <span className="cap lit" style={{ marginRight: 4 }}>Mid</span>
          <Led on={audio?.high > 0.3} color="cyan" />
          <span className="cap lit">Hi</span>
        </div>
      </div>

      {cast && (
        <div className="fullscreen-mirror">
          <canvas ref={fsCanvasRef} />
          <div className="fullscreen-exit">
            <button className="nbtn" onClick={() => setCast(false)}>
              ◀ Exit Presentation
            </button>
          </div>
          <div style={{
            position: "fixed", bottom: 18, left: 18,
            display: "flex", gap: 8, alignItems: "center",
            background: "rgba(0,0,0,0.5)", padding: "8px 12px",
            borderRadius: 999, fontFamily: "var(--font-mono)",
            fontSize: 11, color: "var(--led-amber)",
            textShadow: "0 0 5px var(--led-amber-glow)",
            zIndex: 210
          }}>
            <span style={{
              width: 8, height: 8, borderRadius: "50%",
              background: "var(--led-amber)",
              boxShadow: "0 0 8px var(--led-amber-glow)"
            }} />
            PRESENTATION · {window.VISUALIZERS.find(v => v.id === vizId)?.name}
          </div>
        </div>
      )}
    </div>
  );
}

window.Stage = Stage;
