/* ========================================================================
   LUMINA — Custom FX Studio (floating)
   Write JS effects, save to library, export/import, push to FX chain.
   ======================================================================== */
(function () {
  const { useState, useEffect, useRef } = React;

  const STARTERS = {
    "Tint": "// (ctx, w, h, fx, audio) =>\nconst hue = (audio.bass || 0) * 360;\nctx.fillStyle = `hsla(${hue}, 80%, 50%, 0.18)`;\nctx.fillRect(0, 0, w, h);",
    "Bass Vignette": "// pulses a vignette with bass\nconst v = (audio.bass || 0) * 0.7;\nconst g = ctx.createRadialGradient(w/2, h/2, Math.min(w,h)*0.3, w/2, h/2, Math.max(w,h)*0.7);\ng.addColorStop(0, 'rgba(0,0,0,0)');\ng.addColorStop(1, `rgba(0,0,0,${v})`);\nctx.fillStyle = g;\nctx.fillRect(0, 0, w, h);",
    "Scanlines": "// CRT scanlines\nctx.save();\nctx.globalAlpha = 0.25;\nctx.fillStyle = '#000';\nfor (let y = 0; y < h; y += 3) ctx.fillRect(0, y, w, 1);\nctx.restore();",
    "Beat Flash": "// flash on beat\nif ((audio.beat || 0) > 0.5) {\n  ctx.save();\n  ctx.fillStyle = 'rgba(255,255,255,' + (audio.beat * 0.4) + ')';\n  ctx.fillRect(0, 0, w, h);\n  ctx.restore();\n}",
    "Border Glow": "// audio-reactive border\nconst t = 4 + (audio.energy || 0) * 24;\nctx.save();\nctx.strokeStyle = `hsl(${(audio.high || 0) * 360}, 100%, 60%)`;\nctx.lineWidth = t;\nctx.shadowColor = ctx.strokeStyle;\nctx.shadowBlur = 24;\nctx.strokeRect(t/2, t/2, w - t, h - t);\nctx.restore();",
    "Time Lines": "// scrolling diagonal lines\nconst t = performance.now() / 1000;\nctx.save();\nctx.globalAlpha = 0.18;\nctx.strokeStyle = '#fff';\nctx.lineWidth = 1;\nfor (let x = -h; x < w + h; x += 20) {\n  const o = (x + t * 80) % (w + h);\n  ctx.beginPath();\n  ctx.moveTo(o, 0); ctx.lineTo(o - h, h);\n  ctx.stroke();\n}\nctx.restore();",
  };

  const STORE = "lumina_custom_fx_library_v1";

  function loadLib() {
    try { return JSON.parse(localStorage.getItem(STORE) || "[]"); }
    catch (e) { return []; }
  }
  function saveLib(lib) {
    localStorage.setItem(STORE, JSON.stringify(lib));
  }

  function CustomFXStudio() {
    const [open, setOpen] = useState(false);
    const [pos, setPos] = useState({ x: 380, y: 90 });
    const [size, setSize] = useState({ w: 720, h: 480 });
    const [code, setCode] = useState(STARTERS["Tint"]);
    const [name, setName] = useState("My FX");
    const [error, setError] = useState(null);
    const [library, setLibrary] = useState(loadLib());
    const [, tick] = useState(0);
    const previewRef = useRef(null);
    const stageRef = useRef(null);
    const fileInput = useRef(null);

    useEffect(() => { saveLib(library); }, [library]);

    // Preview loop
    useEffect(() => {
      if (!open || !previewRef.current) return;
      const cnv = previewRef.current;
      const ctx = cnv.getContext("2d");
      let fn = null, fnErr = null;
      try { fn = new Function("ctx", "w", "h", "fx", "audio", code); }
      catch (e) { fnErr = e.message; }
      setError(fnErr);

      let raf;
      const tickFn = () => {
        const W = cnv.width = cnv.clientWidth * 2;
        const H = cnv.height = cnv.clientHeight * 2;
        // demo background — drifting gradient
        const t = performance.now() / 1000;
        const g = ctx.createLinearGradient(0, 0, W, H);
        g.addColorStop(0, `hsl(${(t * 20) % 360}, 70%, 30%)`);
        g.addColorStop(1, `hsl(${(t * 20 + 180) % 360}, 70%, 30%)`);
        ctx.fillStyle = g; ctx.fillRect(0, 0, W, H);
        // shapes
        ctx.fillStyle = "rgba(255,255,255,0.2)";
        for (let i = 0; i < 8; i++) {
          ctx.beginPath();
          ctx.arc(W/2 + Math.cos(t + i) * W * 0.3, H/2 + Math.sin(t * 0.7 + i) * H * 0.3, 60, 0, Math.PI * 2);
          ctx.fill();
        }
        // run user FX
        const audio = window.AudioEngine || { bass: 0, mid: 0, high: 0, energy: 0, beat: 0 };
        if (fn) {
          try { fn(ctx, W, H, { code }, audio); setError(null); }
          catch (e) { setError(e.message); }
        }
        raf = requestAnimationFrame(tickFn);
      };
      raf = requestAnimationFrame(tickFn);
      return () => cancelAnimationFrame(raf);
    }, [code, open]);

    const startDrag = (e) => {
      if (["BUTTON","INPUT","SELECT","TEXTAREA"].includes(e.target.tagName)) return;
      const start = { x: e.clientX, y: e.clientY };
      const sp = { ...pos };
      const onMove = (m) => setPos({ x: sp.x + (m.clientX - start.x), y: sp.y + (m.clientY - start.y) });
      const onUp = () => { window.removeEventListener("mousemove", onMove); window.removeEventListener("mouseup", onUp); };
      window.addEventListener("mousemove", onMove);
      window.addEventListener("mouseup", onUp);
    };

    const saveToLib = () => {
      const id = "cfx-" + Date.now();
      setLibrary(arr => [...arr, { id, name, code, created: Date.now() }]);
    };

    const loadFromLib = (item) => {
      setName(item.name);
      setCode(item.code);
    };

    const deleteLib = (id) => {
      setLibrary(arr => arr.filter(it => it.id !== id));
    };

    const addToFxChain = () => {
      window.Prog?.addFX("customScript");
      const fx = window.Prog.fxChain[window.Prog.fxChain.length - 1];
      if (fx) {
        fx.code = code;
        fx._fn = null;
        fx._customName = name;
        window.Prog.emit();
      }
    };

    const exportLib = () => {
      const blob = new Blob([JSON.stringify(library, null, 2)], { type: "application/json" });
      const a = document.createElement("a");
      a.href = URL.createObjectURL(blob);
      a.download = `lumina-custom-fx.${Date.now()}.json`;
      a.click();
    };

    const importLib = (e) => {
      const f = e.target.files[0];
      if (!f) return;
      const r = new FileReader();
      r.onload = () => {
        try {
          const data = JSON.parse(r.result);
          if (Array.isArray(data)) {
            setLibrary(arr => [...arr, ...data.map(d => ({ ...d, id: "cfx-" + Date.now() + Math.random() }))]);
          }
        } catch (err) { alert("Invalid JSON: " + err.message); }
      };
      r.readAsText(f);
      e.target.value = "";
    };

    if (!open) {
      return (
        <button
          onClick={() => setOpen(true)}
          style={{
            position: "fixed", right: 18, bottom: 170, zIndex: 597,
            padding: "10px 14px", border: "none", borderRadius: 8,
            background: "linear-gradient(180deg, var(--panel-hi), var(--panel))",
            color: "var(--led-violet)", boxShadow: "var(--nshadow-out-sm), 0 0 12px rgba(183,133,255,0.3)",
            fontFamily: "var(--font-display)", fontWeight: 800, fontSize: 11,
            letterSpacing: "0.18em", textTransform: "uppercase",
            cursor: "pointer", display: "flex", alignItems: "center", gap: 8,
          }}>
          <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
            <path d="M14 2 L14 8 L20 8 Z M4 4 L12 4 L12 10 L20 10 L20 20 L4 20 Z" stroke="currentColor" strokeWidth="1.4" fill="none"/>
            <text x="7" y="17" fontSize="6" fill="currentColor" fontFamily="monospace">JS</text>
          </svg>
          Custom FX {library.length ? `· ${library.length}` : ""}
        </button>
      );
    }

    return (
      <div style={{
        position: "fixed", left: pos.x, top: pos.y,
        width: size.w, height: size.h, zIndex: 597,
        background: "linear-gradient(180deg, var(--panel-hi), var(--panel-lo))",
        borderRadius: 10,
        boxShadow: "0 30px 60px rgba(0,0,0,0.8), 0 0 0 2px rgba(183,133,255,0.4)",
        display: "flex", flexDirection: "column", overflow: "hidden",
      }}>
        {/* Header */}
        <div onMouseDown={startDrag} style={{
          padding: "10px 14px",
          display: "flex", alignItems: "center", gap: 10,
          cursor: "move", userSelect: "none",
          borderBottom: "1px solid rgba(255,255,255,0.06)",
          background: "linear-gradient(180deg, rgba(183,133,255,0.12), transparent)",
        }}>
          <span style={{
            fontFamily: "var(--font-display)", fontWeight: 800, fontSize: 13,
            letterSpacing: "0.18em", textTransform: "uppercase", color: "var(--led-violet)",
            textShadow: "0 0 6px var(--led-violet)",
          }}>✦ Custom FX Studio</span>
          <input type="text" value={name} onChange={e => setName(e.target.value)}
            style={{
              padding: "4px 8px", borderRadius: 4, border: "none",
              background: "linear-gradient(180deg,#0a0a0c,#14141a)",
              color: "var(--accent)", fontFamily: "var(--font-mono)", fontSize: 11,
              boxShadow: "var(--nshadow-in-sm)", outline: "none",
              textShadow: "0 0 3px var(--accent-glow)",
            }} />
          <div style={{ flex: 1 }} />
          <button data-lum-close="1" className="nbtn compact" onClick={() => setOpen(false)} style={{ padding: "3px 8px" }}>×</button>
        </div>

        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 200px", gap: 8, padding: 10, flex: 1, minHeight: 0 }}>
          {/* Code editor */}
          <div style={{ display: "flex", flexDirection: "column", gap: 6, minHeight: 0 }}>
            <div className="cap">Code · (ctx, w, h, fx, audio)</div>
            <textarea value={code} onChange={e => setCode(e.target.value)}
              style={{
                flex: 1, padding: "10px 12px",
                background: "linear-gradient(180deg,#06080a,#0a0d10)",
                border: "1px solid #050708", borderRadius: 6,
                color: "var(--led-cyan)", textShadow: "0 0 3px var(--led-cyan-glow)",
                fontFamily: "var(--font-mono)", fontSize: 11, lineHeight: 1.5,
                outline: "none", resize: "none",
                boxShadow: "var(--nshadow-in-sm)",
              }} />
            {error && (
              <div style={{
                padding: "6px 8px", fontSize: 10,
                background: "rgba(255,59,59,0.12)",
                border: "1px solid rgba(255,59,59,0.4)", borderRadius: 4,
                color: "var(--led-red)", fontFamily: "var(--font-mono)",
              }}>{error}</div>
            )}
            <div className="cap" style={{ marginTop: 4 }}>Starters</div>
            <div style={{ display: "flex", flexWrap: "wrap", gap: 4 }}>
              {Object.entries(STARTERS).map(([n, c]) => (
                <button key={n} className="nbtn compact" onClick={() => { setCode(c); setName(n); }}
                  style={{ padding: "3px 8px", fontSize: 9 }}>{n}</button>
              ))}
            </div>
          </div>

          {/* Preview */}
          <div style={{ display: "flex", flexDirection: "column", gap: 6, minHeight: 0 }}>
            <div className="cap">Live Preview</div>
            <div style={{
              flex: 1, position: "relative",
              background: "#000", borderRadius: 6, overflow: "hidden",
              boxShadow: "var(--nshadow-in-sm)",
            }}>
              <canvas ref={previewRef} style={{ display: "block", width: "100%", height: "100%" }} />
              <div style={{
                position: "absolute", top: 6, left: 8,
                fontFamily: "var(--font-mono)", fontSize: 8, color: "rgba(255,255,255,0.6)",
              }}>● PREVIEW · synthetic scene</div>
            </div>
            <button className="nbtn glow pressed" onClick={addToFxChain}
              style={{ padding: "8px 12px", fontWeight: 700 }}>
              ↗ Add to FX Chain
            </button>
            <button className="nbtn compact" onClick={saveToLib}>
              💾 Save to Library
            </button>
          </div>

          {/* Library */}
          <div style={{
            display: "flex", flexDirection: "column", gap: 6,
            background: "linear-gradient(180deg, #0a0a0c, #14141a)",
            borderRadius: 6, padding: 8,
            boxShadow: "var(--nshadow-in-sm)",
            minHeight: 0, overflow: "hidden",
          }}>
            <div className="cap" style={{ display: "flex", justifyContent: "space-between" }}>
              <span>Library</span>
              <span style={{ color: "var(--accent)" }}>{library.length}</span>
            </div>
            <div style={{ flex: 1, overflowY: "auto", display: "flex", flexDirection: "column", gap: 3 }}>
              {library.length === 0 && (
                <div style={{
                  padding: 14, textAlign: "center", color: "var(--ink-faint)", fontSize: 10,
                  border: "1.5px dashed rgba(255,255,255,0.08)", borderRadius: 6,
                }}>Empty.<br/>Save FX to build a library.</div>
              )}
              {library.map(it => (
                <div key={it.id}
                  onClick={() => loadFromLib(it)}
                  style={{
                    display: "grid", gridTemplateColumns: "1fr auto",
                    padding: "5px 7px", borderRadius: 4, cursor: "pointer",
                    background: "linear-gradient(180deg, var(--panel-hi), var(--panel))",
                    boxShadow: "var(--nshadow-out-sm)",
                    fontFamily: "var(--font-mono)", fontSize: 10, color: "var(--ink)",
                  }}>
                  <span style={{ whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
                    ✦ {it.name}
                  </span>
                  <button onClick={(e) => { e.stopPropagation(); deleteLib(it.id); }}
                    style={{
                      border: "none", background: "transparent", cursor: "pointer",
                      color: "var(--led-red)", fontSize: 10, padding: 0, marginLeft: 4,
                    }}>×</button>
                </div>
              ))}
            </div>
            <input ref={fileInput} type="file" accept=".json" hidden onChange={importLib} />
            <button className="nbtn compact" onClick={() => fileInput.current.click()}>⬆ Import JSON</button>
            <button className="nbtn compact" onClick={exportLib} disabled={!library.length}>⬇ Export JSON</button>
          </div>
        </div>

        {/* Footer */}
        <div style={{
          padding: 8, borderTop: "1px solid rgba(255,255,255,0.06)",
          fontSize: 9, fontFamily: "var(--font-mono)", color: "var(--ink-faint)",
          textAlign: "center",
          background: "linear-gradient(180deg, var(--panel-lo), #0a0a0c)",
        }}>
          Vars: <b style={{ color: "var(--led-cyan)" }}>ctx</b> (CanvasRenderingContext2D) ·
          <b style={{ color: "var(--led-cyan)" }}> w, h</b> (dimensions) ·
          <b style={{ color: "var(--led-cyan)" }}> fx</b> (this effect) ·
          <b style={{ color: "var(--led-cyan)" }}> audio</b> ({"{bass, mid, high, energy, beat}"}) ·
          Code runs every frame on the rendered canvas.
        </div>
      </div>
    );
  }

  function mount() {
    if (!window.React || !window.ReactDOM) { setTimeout(mount, 100); return; }
    let host = document.getElementById("__customfx_panel");
    if (!host) {
      host = document.createElement("div");
      host.id = "__customfx_panel";
      document.body.appendChild(host);
    }
    ReactDOM.createRoot(host).render(<CustomFXStudio />);
  }
  if (document.readyState === "complete" || document.readyState === "interactive") setTimeout(mount, 800);
  else document.addEventListener("DOMContentLoaded", () => setTimeout(mount, 800));
})();
