/* ============================================================
   Lumino Design System — Fullscreen Shell
   ------------------------------------------------------------
   <FullscreenShell id="…" title="…" onSave={(format)=>{…}} saveFormats={[{id,label,ext,mime}]}>
     <YourStudio />
   </FullscreenShell>

   Behavior
   --------
   • Embedded mode: renders children in-place with a small "Expand" button (top-right).
   • Expanded mode: renders children inside a full-viewport overlay with a toolbar
     (title · Save · Exit). Escape exits. URL hash persists the expanded id.
   • Save UX (graceful per browser):
       – Chromium (File System Access API present): user can "Pick save folder" once per
         session; subsequent saves write silently to <folder>/<filename>.
       – All browsers: "Download" always available via <a download>.
   • The shell stays out of the way visually: only a subtle top-right expand button
     while embedded; a minimal floating toolbar while expanded.

   Globals exported
   ----------------
   window.FullscreenShell
   window.useFullscreenSave    // hook that returns {save, canPickFolder, folderLabel, pickFolder, clearFolder}
   ============================================================ */

(function () {
  const { useState, useEffect, useRef, useCallback, useMemo } = React;

  /* ───────── hash helpers ───────── */
  function readHash() {
    const m = /[#&]expanded=([^&]+)/.exec(window.location.hash || "");
    return m ? decodeURIComponent(m[1]) : null;
  }
  function writeHash(id) {
    const hash = (window.location.hash || "").replace(/(^|[#&])expanded=[^&]*/g, "");
    const base = hash.replace(/^#&?/, "");
    const next = id ? (base ? "#" + base + "&expanded=" + encodeURIComponent(id) : "#expanded=" + encodeURIComponent(id))
                    : (base ? "#" + base : "");
    history.replaceState(null, "", window.location.pathname + window.location.search + next);
  }

  /* ───────── File System Access support ───────── */
  const FSA_SUPPORTED = typeof window !== "undefined" && "showDirectoryPicker" in window;

  /* Persist a picked dir handle across reloads? No — FS Access handles are not
     serializable to localStorage reliably. We keep it in-memory per session, and
     remember the chosen folder *name* so the UI can show "Saving to: ~/Projects/x". */
  const FS_MEM = { handle: null, label: null };

  async function verifyPermission(handle, mode = "readwrite") {
    const opts = { mode };
    if ((await handle.queryPermission(opts)) === "granted") return true;
    if ((await handle.requestPermission(opts)) === "granted") return true;
    return false;
  }

  /* ───────── useFullscreenSave hook ───────── */
  function useFullscreenSave() {
    const [folderLabel, setFolderLabel] = useState(FS_MEM.label);

    const pickFolder = useCallback(async () => {
      if (!FSA_SUPPORTED) return false;
      try {
        const handle = await window.showDirectoryPicker({ mode: "readwrite" });
        if (!(await verifyPermission(handle))) return false;
        FS_MEM.handle = handle;
        FS_MEM.label = handle.name;
        setFolderLabel(handle.name);
        return true;
      } catch (e) {
        // user cancelled picker, no error UI needed
        return false;
      }
    }, []);

    const clearFolder = useCallback(() => {
      FS_MEM.handle = null;
      FS_MEM.label = null;
      setFolderLabel(null);
    }, []);

    /* save({filename, data, mime})
       - If a folder is picked and FSA is supported → writes to folder, returns {target:"folder", path}.
       - Otherwise → browser download, returns {target:"download"}.
    */
    const save = useCallback(async ({ filename, data, mime = "text/plain" }) => {
      const blob = data instanceof Blob ? data : new Blob([data], { type: mime });
      if (FSA_SUPPORTED && FS_MEM.handle) {
        try {
          if (!(await verifyPermission(FS_MEM.handle))) throw new Error("permission denied");
          const fileHandle = await FS_MEM.handle.getFileHandle(filename, { create: true });
          const writable = await fileHandle.createWritable();
          await writable.write(blob);
          await writable.close();
          return { target: "folder", path: FS_MEM.label + "/" + filename };
        } catch (err) {
          // fall through to download on any write error
        }
      }
      // Download fallback
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url; a.download = filename;
      document.body.appendChild(a); a.click(); a.remove();
      setTimeout(() => URL.revokeObjectURL(url), 1000);
      return { target: "download" };
    }, []);

    return { save, canPickFolder: FSA_SUPPORTED, folderLabel, pickFolder, clearFolder };
  }

  /* ───────── Save menu dropdown ───────── */
  function SaveMenu({ onSave, formats }) {
    const { save, canPickFolder, folderLabel, pickFolder, clearFolder } = useFullscreenSave();
    const [open, setOpen] = useState(false);
    const [toast, setToast] = useState(null);
    const ref = useRef(null);

    useEffect(() => {
      if (!open) return;
      const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
      document.addEventListener("mousedown", onDoc);
      return () => document.removeEventListener("mousedown", onDoc);
    }, [open]);

    const handleSave = async (fmt) => {
      const payload = await onSave(fmt.id);
      if (!payload) return;
      const res = await save({ filename: payload.filename || `lumino.${fmt.ext}`, data: payload.data, mime: fmt.mime });
      setToast(res.target === "folder" ? `Saved · ${res.path}` : `Downloaded · ${payload.filename || 'lumino.' + fmt.ext}`);
      setOpen(false);
      setTimeout(() => setToast(null), 2400);
    };

    return (
      <div ref={ref} style={{ position: "relative" }}>
        <button className="fs-btn fs-btn-primary" onClick={() => setOpen(o => !o)} aria-expanded={open}>
          Save <span style={{ opacity: 0.6, marginLeft: 4 }}>▾</span>
        </button>
        {open && (
          <div className="fs-menu" role="menu">
            {canPickFolder && (
              <div className="fs-menu-section">
                <div className="fs-menu-label">Destination</div>
                {folderLabel ? (
                  <div className="fs-menu-folder">
                    <span className="fs-menu-folder-name" title={folderLabel}>📁 {folderLabel}</span>
                    <button className="fs-menu-link" onClick={clearFolder}>Change</button>
                  </div>
                ) : (
                  <button className="fs-menu-item" onClick={async () => { const ok = await pickFolder(); if (!ok) return; }}>
                    <span>📁 Pick save folder…</span>
                    <span className="fs-menu-sub">Silent saves after first pick</span>
                  </button>
                )}
              </div>
            )}
            {!canPickFolder && (
              <div className="fs-menu-hint">
                Chrome/Edge/Arc support direct-to-folder saves. Here, files download via your browser.
              </div>
            )}
            <div className="fs-menu-section">
              <div className="fs-menu-label">Format</div>
              {formats.map((fmt) => (
                <button key={fmt.id} className="fs-menu-item" onClick={() => handleSave(fmt)}>
                  <span>{fmt.label}</span>
                  <span className="fs-menu-sub">.{fmt.ext}</span>
                </button>
              ))}
            </div>
          </div>
        )}
        {toast && <div className="fs-toast">{toast}</div>}
      </div>
    );
  }

  /* ───────── FullscreenShell ───────── */
  function FullscreenShell({
    id,                // required, used for URL hash
    title,             // shown in expanded toolbar
    subtitle,          // optional small caption
    onSave,            // async (formatId) => ({filename, data}) | null
    saveFormats = [],  // [{id, label, ext, mime}]
    extraToolbar,      // optional JSX rendered in the toolbar (left of Save)
    minHeightEmbedded = 520,
    children,
  }) {
    const [expanded, setExpanded] = useState(() => readHash() === id);

    // Sync with URL (handles browser back/forward)
    useEffect(() => {
      const onHash = () => setExpanded(readHash() === id);
      window.addEventListener("hashchange", onHash);
      return () => window.removeEventListener("hashchange", onHash);
    }, [id]);

    // Push hash when entering/leaving
    useEffect(() => { writeHash(expanded ? id : null); }, [expanded, id]);

    // Esc to exit
    useEffect(() => {
      if (!expanded) return;
      const onKey = (e) => { if (e.key === "Escape") setExpanded(false); };
      window.addEventListener("keydown", onKey);
      return () => window.removeEventListener("keydown", onKey);
    }, [expanded]);

    // Lock body scroll while expanded
    useEffect(() => {
      if (!expanded) return;
      const prev = document.body.style.overflow;
      document.body.style.overflow = "hidden";
      return () => { document.body.style.overflow = prev; };
    }, [expanded]);

    const saveable = !!onSave && saveFormats.length > 0;

    // IMPORTANT: Keep the children in a SINGLE, stable wrapper across embedded/expanded
    // modes so React never unmounts/remounts the child tree — which would wipe state
    // (strokes on a canvas, form input, selections, etc). We toggle classes/styles
    // instead, and render the chrome (expand button or overlay toolbar) as siblings.
    return (
      <>
        {expanded && (
          <div className="fs-overlay-chrome" role="dialog" aria-modal="true" aria-label={title || "Fullscreen editor"}>
            <div className="fs-toolbar">
              <div className="fs-toolbar-left">
                <div className="fs-toolbar-title">{title}</div>
                {subtitle && <div className="fs-toolbar-sub">{subtitle}</div>}
              </div>
              <div className="fs-toolbar-right">
                {extraToolbar}
                {saveable && <SaveMenu onSave={onSave} formats={saveFormats} />}
                <button className="fs-btn" onClick={() => setExpanded(false)} title="Exit fullscreen (Esc)">
                  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                    <polyline points="4 14 10 14 10 20" />
                    <polyline points="20 10 14 10 14 4" />
                    <line x1="14" y1="10" x2="21" y2="3" />
                    <line x1="3" y1="21" x2="10" y2="14" />
                  </svg>
                  <span>Exit</span>
                  <kbd className="fs-kbd">Esc</kbd>
                </button>
              </div>
            </div>
          </div>
        )}
        <div
          className={expanded ? "fs-stage-mount expanded" : "fs-stage-mount embedded"}
          style={!expanded ? { position: "relative", minHeight: minHeightEmbedded } : undefined}
        >
          {!expanded && (
            <button
              className="fs-expand-btn"
              onClick={() => setExpanded(true)}
              title="Expand to fullscreen"
              aria-label="Expand to fullscreen"
            >
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                <polyline points="15 3 21 3 21 9" />
                <polyline points="9 21 3 21 3 15" />
                <line x1="21" y1="3" x2="14" y2="10" />
                <line x1="3" y1="21" x2="10" y2="14" />
              </svg>
              <span>Expand</span>
            </button>
          )}
          {children}
        </div>
      </>
    );
  }

  window.FullscreenShell = FullscreenShell;
  window.useFullscreenSave = useFullscreenSave;
})();
