(function (Scratch) {
    'use strict';

    if (!Scratch.extensions.unsandboxed) {
        throw new Error('SceneManager must run unsandboxed!');
    }

    let rootDirectoryHandle = null;
    let currentFileHandle = null;

    const styleEl = document.createElement('style');
    styleEl.textContent = `
    /* ── Overlay & Box ───────────────────────────────────────────── */
    .sm-ov {
      position: fixed; inset: 0;
      background: rgba(0,0,0,.6);
      display: flex; align-items: center; justify-content: center;
      z-index: 99999;
      animation: smFade .14s ease;
    }
    @keyframes smFade { from { opacity:0 } to { opacity:1 } }

    .sm-box {
      background: #1e1e2e;
      border: 1px solid #383860;
      border-radius: 14px;
      padding: 26px 28px 22px;
      max-width: 540px;
      width: calc(100% - 40px);
      color: #cdd6f4;
      box-shadow: 0 28px 70px rgba(0,0,0,.55);
      animation: smSlide .18s ease;
      max-height: 90vh;
      overflow-y: auto;
    }
    @keyframes smSlide { from { transform:translateY(-10px);opacity:0 } to { transform:none;opacity:1 } }

    /* ── Typography ──────────────────────────────────────────────── */
    .sm-box h2 {
      margin: 0 0 14px;
      font: 600 17px/1.3 system-ui, sans-serif;
      color: #89b4fa;
      display: flex; align-items: center; gap: 8px;
    }
    .sm-box p {
      margin: 0 0 12px;
      font: 400 13.5px/1.65 system-ui, sans-serif;
      color: #bac2de;
    }
    .sm-box p:last-child { margin-bottom: 0; }
    .sm-box code {
      background: #313244;
      border-radius: 4px;
      padding: 1px 5px;
      font-size: 12px;
      font-family: monospace;
      color: #a6e3a1;
    }

    /* ── Note / Info block ───────────────────────────────────────── */
    .sm-note {
      background: #252538;
      border-left: 3px solid #89b4fa;
      border-radius: 0 8px 8px 0;
      padding: 10px 14px;
      font: 400 13px/1.6 system-ui, sans-serif;
      color: #bac2de;
      margin: 12px 0;
    }
    .sm-note strong { color: #cdd6f4; font-weight: 600; }

    /* ── Path display ────────────────────────────────────────────── */
    .sm-path {
      background: #181825;
      border: 1px solid #45475a;
      border-radius: 7px;
      padding: 9px 13px;
      font: 400 12.5px/1.5 monospace;
      color: #a6e3a1;
      margin: 12px 0;
      word-break: break-all;
      white-space: pre-wrap;
    }

    /* ── Buttons ─────────────────────────────────────────────────── */
    .sm-row {
      display: flex; gap: 10px; justify-content: flex-end;
      margin-top: 22px;
      flex-wrap: wrap;
    }
    .sm-btn {
      padding: 8px 20px;
      border-radius: 7px;
      border: none;
      cursor: pointer;
      font: 600 13px system-ui, sans-serif;
      transition: filter .12s, transform .1s;
      white-space: nowrap;
    }
    .sm-btn:hover   { filter: brightness(1.13); }
    .sm-btn:active  { transform: scale(.97); }
    .sm-btn-primary { background: #89b4fa; color: #1e1e2e; }
    .sm-btn-ghost   { background: #313244; color: #cdd6f4; }
    .sm-btn-warn    { background: #f38ba8; color: #1e1e2e; }
    .sm-btn:disabled { opacity: .4; cursor: default; filter: none; }

    /* ── Project tree ────────────────────────────────────────────── */
    .sm-tree {
      background: #181825;
      border: 1px solid #45475a;
      border-radius: 8px;
      max-height: 300px;
      overflow-y: auto;
      margin: 12px 0;
    }
    .sm-tree-empty {
      padding: 28px 16px;
      text-align: center;
      color: #585b70;
      font: italic 13px system-ui, sans-serif;
    }
    .sm-folder-row {
      padding: 7px 14px;
      background: #1e1e2e;
      border-bottom: 1px solid #2a2a42;
      font: 600 12.5px system-ui, sans-serif;
      color: #fab387;
      display: flex; align-items: center; gap: 7px;
      user-select: none;
    }
    .sm-file-row {
      padding: 8px 14px 8px 30px;
      border-bottom: 1px solid #252538;
      font: 400 13px system-ui, sans-serif;
      color: #cdd6f4;
      cursor: pointer;
      display: flex; align-items: center; gap: 8px;
      transition: background .1s;
    }
    .sm-file-row:last-child { border-bottom: none; }
    .sm-file-row:hover   { background: #313244; }
    .sm-file-row.sm-sel  { background: #3a3a5c; color: #89b4fa; }

    /* ── Toast ───────────────────────────────────────────────────── */
    .sm-toast {
      position: fixed; bottom: 24px; right: 24px;
      background: #313244;
      border: 1px solid #45475a;
      border-radius: 8px;
      padding: 10px 16px;
      font: 400 13px system-ui, sans-serif;
      color: #cdd6f4;
      z-index: 100000;
      opacity: 0;
      transform: translateY(8px);
      transition: opacity .22s ease, transform .22s ease;
      max-width: 320px;
      box-shadow: 0 8px 24px rgba(0,0,0,.4);
    }
    .sm-toast.sm-toast-in { opacity: 1; transform: none; }
    .sm-toast.sm-ok  { border-left: 3px solid #a6e3a1; }
    .sm-toast.sm-err { border-left: 3px solid #f38ba8; }

    /* ── Divider ─────────────────────────────────────────────────── */
    .sm-sep {
      border: none; border-top: 1px solid #313244;
      margin: 14px 0;
    }

    /* ── Loading spinner ─────────────────────────────────────────── */
    .sm-spin {
      display: inline-block; width: 14px; height: 14px;
      border: 2px solid #45475a;
      border-top-color: #89b4fa;
      border-radius: 50%;
      animation: smSpin .7s linear infinite;
    }
    @keyframes smSpin { to { transform: rotate(360deg); } }
  `;
    document.head.appendChild(styleEl);

    /** Opens a modal. `buildFn(box, close)` populates .sm-box and calls close(value) to resolve. */
    function openModal(buildFn) {
        return new Promise(resolve => {
            const overlay = document.createElement('div');
            overlay.className = 'sm-ov';
            const box = document.createElement('div');
            box.className = 'sm-box';
            overlay.appendChild(box);
            document.body.appendChild(overlay);

            let closed = false;
            const close = val => {
                if (closed) return;
                closed = true;
                document.removeEventListener('keydown', onKey);
                overlay.remove();
                resolve(val);
            };
            const onKey = e => { if (e.key === 'Escape') close(null); };
            document.addEventListener('keydown', onKey);
            overlay.addEventListener('click', e => { if (e.target === overlay) close(null); });

            buildFn(box, close);
        });
    }

    function showToast(msg, type = 'ok') {
        const t = document.createElement('div');
        t.className = `sm-toast sm-${type}`;
        t.textContent = msg;
        document.body.appendChild(t);
        requestAnimationFrame(() => requestAnimationFrame(() => t.classList.add('sm-toast-in')));
        setTimeout(() => {
            t.classList.remove('sm-toast-in');
            t.addEventListener('transitionend', () => t.remove(), { once: true });
        }, 2800);
    }

    class SceneManager {

        getInfo() {
            return {
                id: 'sceneManager',
                name: 'Scene Manager',
                color1: '#4A90E2',
                color2: '#357ABD',
                color3: '#285A8F',
                blocks: [
                    // ── Folder management ──
                    {
                        blockType: Scratch.BlockType.LABEL,
                        text: 'Root Folder'
                    },
                    {
                        opcode: 'selectFolder',
                        blockType: Scratch.BlockType.BUTTON,
                        text: 'Set Root Folder',
                        func: 'selectFolder'
                    },
                    {
                        opcode: 'browseProjects',
                        blockType: Scratch.BlockType.BUTTON,
                        text: 'Browse & Switch Project',
                        func: 'browseProjects'
                    },
                    {
                        opcode: 'showHelp',
                        blockType: Scratch.BlockType.BUTTON,
                        text: 'How does this work?',
                        func: 'showHelp'
                    },
                    // ── Scene blocks ──
                    {
                        blockType: Scratch.BlockType.LABEL,
                        text: 'Scene Blocks'
                    },
                    {
                        opcode: 'openSB3',
                        blockType: Scratch.BlockType.COMMAND,
                        text: 'open [arg0].sb3',
                        arguments: {
                            arg0: { type: Scratch.ArgumentType.STRING, defaultValue: 'MyGame/Level2' }
                        }
                    },
                    {
                        opcode: 'openSB3withData',
                        blockType: Scratch.BlockType.COMMAND,
                        text: 'open [arg0].sb3 with data [arg1]',
                        arguments: {
                            arg0: { type: Scratch.ArgumentType.STRING, defaultValue: 'MyGame/Level2' },
                            arg1: { type: Scratch.ArgumentType.STRING, defaultValue: 'score=100' }
                        }
                    },
                    {
                        opcode: 'receivedData',
                        blockType: Scratch.BlockType.REPORTER,
                        text: 'received data'
                    }, {
                        blockType: Scratch.BlockType.LABEL,
                        text: 'SE Support: Coming in v1.0'
                    }

                ]
            };
        }

        async selectFolder() {
            const handle = await this._pickDirectory();
            if (!handle) return;
            rootDirectoryHandle = handle;
            currentFileHandle = null;
            this._showFolderSetModal(handle.name);
        }

        async browseProjects() {
            if (!rootDirectoryHandle) {
                const ok = await this._requireFolder('Open the project browser');
                if (!ok) return;
            }

            const files = await this._scanSb3(rootDirectoryHandle);

            await openModal((box, close) => {
                let selectedPath = null;

                // Build tree HTML
                const grouped = {};
                for (const f of files) {
                    const parts = f.path.split('/');
                    const dir = parts.length > 1 ? parts.slice(0, -1).join('/') : '';
                    (grouped[dir] = grouped[dir] || []).push(f);
                }

                let treeHtml = '';
                if (files.length === 0) {
                    treeHtml = '<div class="sm-tree-empty">No .sb3 files found in this folder.</div>';
                } else {
                    for (const [dir, items] of Object.entries(grouped)) {
                        if (dir) treeHtml += `<div class="sm-folder-row">📁 ${this._esc(dir)}/</div>`;
                        for (const item of items) {
                            const name = item.path.split('/').pop().replace(/\.sb3$/i, '');
                            treeHtml += `<div class="sm-file-row" data-path="${this._esc(item.path)}">📄 ${this._esc(name)}.sb3</div>`;
                        }
                    }
                }

                box.innerHTML = `
          <h2>Browse Projects</h2>
          <p>Root folder: <code>${this._esc(rootDirectoryHandle.name)}/</code></p>
          <div class="sm-tree" id="sm-tree">${treeHtml}</div>
          <p id="sm-sel-label" style="font-size:12.5px;color:#585b70;min-height:18px;margin-bottom:0"></p>
          <div class="sm-row">
            <button class="sm-btn sm-btn-ghost" id="sm-cancel-btn">Cancel</button>
            <button class="sm-btn sm-btn-primary" id="sm-switch-btn" disabled>Switch →</button>
          </div>
        `;

                const tree = box.querySelector('#sm-tree');
                const switchBtn = box.querySelector('#sm-switch-btn');
                const selLabel = box.querySelector('#sm-sel-label');

                tree.addEventListener('click', e => {
                    const row = e.target.closest('.sm-file-row');
                    if (!row) return;
                    tree.querySelectorAll('.sm-sel').forEach(el => el.classList.remove('sm-sel'));
                    row.classList.add('sm-sel');
                    selectedPath = row.dataset.path;
                    selLabel.textContent = `Selected: ${selectedPath}`;
                    selLabel.style.color = '#89b4fa';
                    switchBtn.disabled = false;
                });

                box.querySelector('#sm-cancel-btn').onclick = () => close(null);

                switchBtn.onclick = () => {
                    if (!selectedPath) return;
                    close(selectedPath);
                };

            }).then(async path => {
                if (!path) return;
                await this._loadScene(path, '', /* saveFirst */ true);
            });
        }

        async showHelp() {
            await openModal((box, close) => {
                box.innerHTML = `
          <h2>How the Scene Manager works</h2>

          <p>
            <strong style="color:#cdd6f4">Scene Manager</strong> lets you split your Scratch project
            into multiple <code>.sb3</code> files and seamlessly switch between them at runtime —
            just like loading a new level or screen.
          </p>

          <div class="sm-note">
            <strong>Scratch Everywhere</strong><br>
            All <code>.sb3</code> files directly inside the scratch-everywhere folder appear in the SE! menu.<br><br>
            <strong>Set Root Folder</strong> tells Scene Manager which folder acts as that root folder while
            you're developing in Scratch/Turbowarp/ScratchBox/Mistwarp or any other SE! compatible editor.
          </div>

          <hr class="sm-sep">
          <p><strong style="color:#cdd6f4">Recommended project structure</strong></p>
          <div class="sm-path">📁 scratch-everywhere/
  MyGame.sb3        ← launcher (appears in menu)
  📁 MyGameName-Scenes/
      Level1.sb3
      Level2.sb3
      BossLevel.sb3</div>

          <p>
            Only <code>MyGame.sb3</code> is visible in the game-selection menu.
            It uses Scene Manager blocks to load the actual levels from the
            <code>scenes/</code> subfolder, those files stay private inside the SE! menu.
          </p>

          <div class="sm-note">
            Use <strong>subfolder-prefixed paths</strong> in the scene blocks:<br>
            <code>open scenes/Level1.sb3</code><br>
            Scene Manager navigates into the subfolder automatically.
          </div>

          <div class="sm-row">
            <button class="sm-btn sm-btn-primary" id="sm-ok-btn">Got it!</button>
          </div>
        `;
                box.querySelector('#sm-ok-btn').onclick = () => close(null);
            });
        }

        async openSB3(args) {
            await this._loadScene(args.arg0, '');
        }

        async openSB3withData(args) {
            await this._loadScene(args.arg0, args.arg1);
        }

        receivedData() {
            return sessionStorage.getItem('scenemanager_data') || '';
        }

        /** Opens a directory picker. Returns null if cancelled. */
        async _pickDirectory() {
            try {
                return await window.showDirectoryPicker({ id: 'scenemanager-root', mode: 'readwrite', startIn: 'documents' });
            } catch (e) {
                if (e.name !== 'AbortError') console.error('[SceneManager] Directory picker failed:', e);
                return null;
            }
        }

        /** Shows the "folder has been set" confirmation modal. */
        _showFolderSetModal(name) {
            openModal((box, close) => {
                box.innerHTML = `
          <h2>Root folder set</h2>
          <p>Scene Manager will now load projects from:</p>
          <div class="sm-path">📁 ${this._esc(name)}/</div>
          <div class="sm-note">
            In <strong>Scratch Everywhere</strong> this corresponds to the
            <code>scratch-everywhere/</code> folder, where all your .sb3 files are located.
            Put your launcher project in the root and keep scene files in a subfolder.
            You theoretically don't have to put all your scenes inside a subfolder, but it is recommended.
          </div>
          <div class="sm-row">
            <button class="sm-btn sm-btn-primary" id="sm-ok-btn">Got it!</button>
          </div>
        `;
                box.querySelector('#sm-ok-btn').onclick = () => close(null);
            });
        }

        /**
         * If no root folder is set, prompts the user to select one.
         * Returns true if a folder is now available, false if cancelled.
         */
        async _requireFolder(action) {
            return openModal((box, close) => {
                box.innerHTML = `
          <h2>Root folder required</h2>
          <p>
            <strong style="color:#cdd6f4">${this._esc(action)}</strong> needs to know
            where your <code>.sb3</code> files are stored.
          </p>
          <div class="sm-note">
            Select your <code>scratch-everywhere/</code> folder (or any folder containing your projects).
            After selecting, the action will continue automatically.
          </div>
          <div class="sm-row">
            <button class="sm-btn sm-btn-ghost"  id="sm-cancel-btn">Cancel</button>
            <button class="sm-btn sm-btn-primary" id="sm-select-btn">Select folder…</button>
          </div>
        `;
                box.querySelector('#sm-cancel-btn').onclick = () => close(false);
                box.querySelector('#sm-select-btn').onclick = async () => {
                    const handle = await this._pickDirectory();
                    if (handle) { rootDirectoryHandle = handle; currentFileHandle = null; close(true); }
                    else close(false);
                };
            });
        }

        /** Recursively scans a directory for .sb3 files. Returns [{path, handle}]. */
        async _scanSb3(dirHandle, prefix = '') {
            const results = [];
            for await (const [name, handle] of dirHandle.entries()) {
                const rel = prefix ? `${prefix}/${name}` : name;
                if (handle.kind === 'file' && name.toLowerCase().endsWith('.sb3')) {
                    results.push({ path: rel, handle });
                } else if (handle.kind === 'directory') {
                    results.push(...await this._scanSb3(handle, rel));
                }
            }
            return results.sort((a, b) => a.path.localeCompare(b.path));
        }

        /**
         * Resolves a relative path (e.g. "scenes/Level2" or "scenes/Level2.sb3")
         * to a FileSystemFileHandle inside rootDirectoryHandle.
         */
        async _getFileHandle(relativePath) {
            if (!rootDirectoryHandle) {
                const ok = await this._requireFolder('A scene block tried to load a file');
                if (!ok) return null;
            }
            // Normalise: strip trailing .sb3, then re-add
            const clean = relativePath.replace(/\.sb3$/i, '');
            const parts = clean.split('/').filter(Boolean);
            const fileName = parts[parts.length - 1] + '.sb3';
            let handle = rootDirectoryHandle;
            try {
                for (let i = 0; i < parts.length - 1; i++) {
                    handle = await handle.getDirectoryHandle(parts[i]);
                }
                return await handle.getFileHandle(fileName);
            } catch {
                await this._showError(`File not found: ${clean}.sb3`);
                return null;
            }
        }

        /** Core loading routine used by both the scene blocks and the project browser. */
        async _loadScene(filePath, dataToPass, fromBrowser = false) {
            const display = filePath.replace(/\.sb3$/i, '') + '.sb3';

            // Confirm
            const confirmed = await openModal((box, close) => {
                const saveRow = currentFileHandle
                    ? `<button class="sm-btn sm-btn-primary" id="sm-save-btn">Save current & switch</button>`
                    : `<button class="sm-btn sm-btn-primary" id="sm-switch-btn">Switch</button>`;

                box.innerHTML = `
          <h2>Switch scene</h2>
          <p>Load <code>${this._esc(display)}</code>?</p>
          <div class="sm-note">
            Unsaved changes in the current project will be lost.
            ${currentFileHandle ? 'You can save first using the button below.' : ''}
          </div>
          <div class="sm-row">
            <button class="sm-btn sm-btn-ghost" id="sm-cancel-btn">Cancel</button>
            ${saveRow}
            ${currentFileHandle ? `<button class="sm-btn sm-btn-warn" id="sm-switch-btn">Switch without saving</button>` : ''}
          </div>
        `;

                box.querySelector('#sm-cancel-btn').onclick = () => close('cancel');

                const switchBtn = box.querySelector('#sm-switch-btn');
                if (switchBtn) switchBtn.onclick = () => close('switch');

                const saveBtn = box.querySelector('#sm-save-btn');
                if (saveBtn) saveBtn.onclick = () => close('save-and-switch');
            });

            if (!confirmed || confirmed === 'cancel') return;

            // Optionally save current project back to its file
            if (confirmed === 'save-and-switch' && currentFileHandle) {
                try {
                    const blob = await Scratch.vm.saveProjectSb3();
                    const writable = await currentFileHandle.createWritable();
                    await writable.write(blob);
                    await writable.close();
                    showToast(`Saved: ${currentFileHandle.name}`, 'ok');
                } catch (e) {
                    console.error('[SceneManager] Save failed:', e);
                    showToast('Save failed — switching anyway.', 'err');
                }
            }

            // Resolve file
            const fh = await this._getFileHandle(filePath);
            if (!fh) return;

            // Load
            try {
                const file = await fh.getFile();
                const buf = await file.arrayBuffer();
                sessionStorage.setItem('scenemanager_data', dataToPass);
                currentFileHandle = fh;
                await Scratch.vm.loadProject(buf);
                Scratch.vm.greenFlag();
            } catch (e) {
                console.error('[SceneManager] Load failed:', e);
                await this._showError('Could not load the project. Is it a valid .sb3 file?');
            }
        }

        async _showError(msg) {
            await openModal((box, close) => {
                box.innerHTML = `
          <h2>Error</h2>
          <p>${this._esc(msg)}</p>
          <div class="sm-row">
            <button class="sm-btn sm-btn-primary" id="sm-ok-btn">OK</button>
          </div>
        `;
                box.querySelector('#sm-ok-btn').onclick = () => close(null);
            });
        }

        _esc(str) {
            return String(str)
                .replace(/&/g, '&amp;')
                .replace(/</g, '&lt;')
                .replace(/>/g, '&gt;')
                .replace(/"/g, '&quot;');
        }
    }

    Scratch.extensions.register(new SceneManager());

})(Scratch);