(function () {
    'use strict';

    document.body.classList.add('project-open');

    const navbar = document.getElementById('projectNavbar');
    const shellBaseUrl = (navbar?.dataset.baseUrl || '').replace(/\/$/, '');
    const shellProjectSlug = navbar?.dataset.projectSlug || '';
    const projectRoot = `${shellBaseUrl}/project/${encodeURIComponent(shellProjectSlug)}`;

    document.addEventListener('click', event => {
        const dismiss = event.target.closest('[data-flash-dismiss]');
        if (dismiss) dismiss.parentElement?.remove();
    });

    function setupSidebarToggle() {
        const toggleBtn = document.getElementById('sidebarToggle');
        const sidebar = document.getElementById('sidebarDesktop');
        const main = document.getElementById('mainArea') || document.getElementById('mainContent');
        if (!toggleBtn || !sidebar || !main) return;

        const icon = toggleBtn.querySelector('i');
        const setCollapsed = (collapsed) => {
            sidebar.classList.toggle('collapsed', collapsed);
            main.classList.toggle('expanded', collapsed);
            if (icon) {
                icon.classList.toggle('bi-layout-sidebar', collapsed);
                icon.classList.toggle('bi-layout-sidebar-inset', !collapsed);
            }
            localStorage.setItem('sidebarCollapsed', collapsed ? 'true' : 'false');
        };

        setCollapsed(localStorage.getItem('sidebarCollapsed') === 'true');
        toggleBtn.addEventListener('click', () => setCollapsed(!sidebar.classList.contains('collapsed')));
    }

    const spaPages = (() => {
        const items = Array.from(document.querySelectorAll('#spaPageMenu .pp-spa-item[data-page]'));
        return items.length
            ? items.map(item => ({ label: item.dataset.label || item.textContent.trim(), uri: item.dataset.page, icon: item.dataset.icon }))
            : [
                { label: 'Workspace', uri: '/workspace' },
                { label: 'Tables', uri: '/tables' },
                { label: 'Query', uri: '/query' },
                { label: 'Pages', uri: '/pages' },
                { label: 'Fields', uri: '/fields' },
                { label: 'Misc', uri: '/misc' },
                { label: 'Security', uri: '/security' },
                { label: 'Designer', uri: '/designer' },
                { label: 'Style', uri: '/style' },
                { label: 'Events', uri: '/events' },
                { label: 'Custom Files', uri: '/custom-files' },
                { label: 'Output', uri: '/output' },
                { label: 'Finished', uri: '/finished' },
            ];
    })();

    function currentProjectUri() {
        const decodedRoot = decodeURI(projectRoot);
        const decodedPath = decodeURI(window.location.pathname);
        const relative = decodedPath.startsWith(decodedRoot) ? decodedPath.slice(decodedRoot.length) : '';
        return relative === '' || relative === '/' ? '/workspace' : relative.replace(/\/$/, '');
    }

    const currentUri = currentProjectUri();
    const currentPage = spaPages.find(page => page.uri === currentUri) || spaPages[0];
    const currentIndex = Math.max(0, spaPages.findIndex(page => page.uri === currentPage.uri));

    const currentLabel = document.getElementById('currentPageLabel');
    if (currentLabel) currentLabel.textContent = currentPage.label;
    const currentIcon = document.getElementById('currentPageIcon');
    if (currentIcon && currentPage.icon) currentIcon.className = 'bi bi-' + currentPage.icon + ' pp-page-btn-ico';
    document.querySelectorAll('#spaPageMenu .pp-spa-item[data-page]').forEach(item => {
        item.classList.toggle('active', item.dataset.page === currentPage.uri);
    });

    function moduleName() {
        return currentPage.uri.replace(/^\//, '') || 'workspace';
    }

    function collectSavePayload() {
        const detail = { module: moduleName(), payload: {} };
        document.dispatchEvent(new CustomEvent('projectSave', { detail }));
        return detail;
    }

    async function persistCurrentModule(keepalive = false) {
        if (!shellProjectSlug) return { success: true, message: 'Nothing to save.' };
        const detail = collectSavePayload();
        const response = await fetch(`${projectRoot}/save`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ module: detail.module, data: detail.payload }),
            keepalive,
        });
        const data = await response.json().catch(() => ({ success: false, message: 'The server returned an invalid response.' }));
        if (!response.ok || !data.success) {
            throw new Error(data.message || 'The project could not be saved.');
        }
        return data;
    }

    function targetUrl(uri) {
        const normalized = uri === '' || uri === '/' ? '/workspace' : uri;
        return `${projectRoot}${normalized}`;
    }

    async function saveCurrentModuleAndNavigate(uri) {
        const destination = targetUrl(uri);
        if (destination === window.location.href || destination === window.location.pathname) return;

        try {
            const detail = collectSavePayload();
            fetch(`${projectRoot}/save`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ module: detail.module, data: detail.payload }),
                keepalive: true,
            }).catch(error => console.warn('Auto-save failed', error));
        } finally {
            window.location.href = destination;
        }
    }

    window.navigateToPage = saveCurrentModuleAndNavigate;

    document.querySelectorAll('#spaPageMenu .pp-spa-item[data-page], .mobile-nav-item[data-page]').forEach(item => {
        item.addEventListener('click', event => {
            event.preventDefault();
            saveCurrentModuleAndNavigate(item.dataset.page);
        });
    });

    // ── Screen switcher: search, arrow-nav, and global keyboard shortcuts ──
    const spaMenu = document.getElementById('spaPageMenu');
    const spaToggle = document.getElementById('spaPageDropdown');
    const spaSearch = document.getElementById('spaPageSearch');

    // Map shortcut letter -> uri (non-locked screens only), from the rendered rows.
    const keymap = {};
    if (spaMenu) {
        spaMenu.querySelectorAll('.pp-spa-item[data-page][data-key]').forEach(item => {
            keymap[(item.dataset.key || '').toUpperCase()] = item.dataset.page;
        });
    }

    // Show the right modifier label (⌘ on mac, Ctrl elsewhere).
    const isMac = /Mac|iPhone|iPad|iPod/.test(navigator.platform || navigator.userAgent || '');
    if (!isMac) document.querySelectorAll('[data-mod-key]').forEach(el => { el.textContent = 'Ctrl '; });

    function openSwitcher() {
        if (!spaToggle) return;
        if (window.bootstrap && window.bootstrap.Dropdown) {
            window.bootstrap.Dropdown.getOrCreateInstance(spaToggle).show();
        } else {
            spaToggle.click();
        }
    }

    if (spaSearch && spaMenu) {
        const rows = Array.from(spaMenu.querySelectorAll('.pp-spa-item'));
        const groups = Array.from(spaMenu.querySelectorAll('.pp-spa-group'));
        const empty = spaMenu.querySelector('.pp-spa-empty');
        let hl = -1;

        const visible = () => rows.filter(row => !row.hidden && row.dataset.page);
        const clearHl = () => { rows.forEach(row => row.classList.remove('is-hl')); hl = -1; };
        const moveHl = step => {
            const vis = visible();
            if (!vis.length) return;
            rows.forEach(row => row.classList.remove('is-hl'));
            hl = (hl + step + vis.length) % vis.length;
            vis[hl].classList.add('is-hl');
            vis[hl].scrollIntoView({ block: 'nearest' });
        };

        const applyFilter = () => {
            const q = spaSearch.value.trim().toLowerCase();
            let shown = 0;
            rows.forEach(row => {
                const hit = !q || (row.dataset.label || '').toLowerCase().includes(q);
                row.hidden = !hit;
                if (hit) shown++;
            });
            groups.forEach(group => {
                let sib = group.nextElementSibling, any = false;
                while (sib && !sib.classList.contains('pp-spa-group')) {
                    if (sib.classList.contains('pp-spa-item') && !sib.hidden) { any = true; break; }
                    sib = sib.nextElementSibling;
                }
                group.hidden = !any;
            });
            if (empty) empty.hidden = shown > 0;
            clearHl();
        };

        spaSearch.addEventListener('input', applyFilter);
        spaSearch.addEventListener('keydown', event => {
            if (event.key === 'Escape') { spaSearch.value = ''; applyFilter(); }
            else if (event.key === 'ArrowDown') { event.preventDefault(); moveHl(1); }
            else if (event.key === 'ArrowUp') { event.preventDefault(); moveHl(-1); }
            else if (event.key === 'Enter') {
                const vis = visible();
                const target = (hl >= 0 && vis[hl]) ? vis[hl] : vis[0];
                if (target) { event.preventDefault(); saveCurrentModuleAndNavigate(target.dataset.page); }
            }
        });
        spaToggle?.addEventListener('shown.bs.dropdown', () => {
            spaSearch.value = '';
            applyFilter();
            setTimeout(() => spaSearch.focus(), 30);
        });
    }

    // Global shortcuts: ⌘K/Ctrl+K opens the switcher; Ctrl+Alt+<letter> jumps to a screen.
    // Map by physical key code (KeyT…) so Mac's Option-key diacritics don't break it.
    const codeMap = {};
    Object.keys(keymap).forEach(k => { codeMap['Key' + k] = keymap[k]; });
    document.querySelectorAll('[data-mod-combo]').forEach(el => { el.textContent = isMac ? '⌃⌥' : 'Ctrl+Alt'; });

    document.addEventListener('keydown', event => {
        // ⌘K / Ctrl+K opens the palette.
        if ((event.metaKey || event.ctrlKey) && !event.altKey && !event.shiftKey && (event.key === 'k' || event.key === 'K')) {
            event.preventDefault();
            openSwitcher();
            return;
        }
        // Ctrl+Alt+<letter> (Ctrl+Option on Mac) — jump straight to a screen.
        if (event.ctrlKey && event.altKey && !event.metaKey) {
            const dest = codeMap[event.code];
            if (dest) { event.preventDefault(); saveCurrentModuleAndNavigate(dest); }
        }
    });

    document.getElementById('btnPrevPage')?.addEventListener('click', () => {
        const index = (currentIndex - 1 + spaPages.length) % spaPages.length;
        saveCurrentModuleAndNavigate(spaPages[index].uri);
    });

    document.getElementById('btnNextPage')?.addEventListener('click', () => {
        const index = (currentIndex + 1) % spaPages.length;
        saveCurrentModuleAndNavigate(spaPages[index].uri);
    });

    document.addEventListener('click', event => {
        const link = event.target.closest('#sidebarDesktop a, #sidebarOffcanvas a');
        if (!link) return;
        const href = link.getAttribute('href');
        if (!href || !href.includes(`/project/${shellProjectSlug}`)) return;

        const url = new URL(href, window.location.href);
        const rootPath = new URL(projectRoot, window.location.origin).pathname;
        if (!url.pathname.startsWith(rootPath)) return;
        event.preventDefault();
        const relative = url.pathname.slice(rootPath.length) || '/workspace';
        saveCurrentModuleAndNavigate(relative);
    });

    document.getElementById('mobileBtnSave')?.addEventListener('click', event => {
        event.preventDefault();
        document.getElementById('btnSaveProject')?.click();
    });
    document.getElementById('mobileBtnBuild')?.addEventListener('click', event => {
        event.preventDefault();
        document.getElementById('btnBuildProject')?.click();
    });

    // Close project: save the current module (best-effort), then return to the
    // My Projects list. The anchor's href is a no-JS fallback.
    function closeProject() {
        const destination = `${shellBaseUrl}/projects`;
        try {
            const detail = collectSavePayload();
            fetch(`${projectRoot}/save`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ module: detail.module, data: detail.payload }),
                keepalive: true,
            }).catch(() => {});
        } finally {
            window.location.href = destination;
        }
    }
    document.getElementById('btnCloseProject')?.addEventListener('click', event => {
        event.preventDefault();
        closeProject();
    });
    document.getElementById('mobileBtnClose')?.addEventListener('click', event => {
        event.preventDefault();
        closeProject();
    });

    document.getElementById('btnSaveProject')?.addEventListener('click', async () => {
        const button = document.getElementById('btnSaveProject');
        const originalHtml = button.innerHTML;
        button.disabled = true;
        button.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span> Saving';
        try {
            const data = await persistCurrentModule(false);
            const savedMessage = document.getElementById('lastSavedMsg');
            if (savedMessage) savedMessage.textContent = 'Saved ' + new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
            await Swal.fire({ title: 'Saved', text: data.message || 'Project saved.', icon: 'success', timer: 1300, showConfirmButton: false });
        } catch (error) {
            await Swal.fire('Save failed', error.message || 'The project could not be saved.', 'error');
        } finally {
            button.disabled = false;
            button.innerHTML = originalHtml;
        }
    });

    document.getElementById('btnBuildProject')?.addEventListener('click', async () => {
        const confirmation = await Swal.fire({
            title: 'Build project?',
            text: 'Current changes will be saved before the application is generated.',
            icon: 'question',
            showCancelButton: true,
            confirmButtonText: '<i class="bi bi-hammer me-1"></i> Build now',
            confirmButtonColor: '#2563eb',
        });
        if (!confirmation.isConfirmed) return;

        try {
            await persistCurrentModule(false);
        } catch (error) {
            await Swal.fire('Build stopped', error.message || 'Save the current changes before building.', 'error');
            return;
        }

        // Same step-based build overlay the Finished module uses, so a build looks
        // identical from every SPA page (falls back to a simple dialog if the shared
        // component is somehow absent).
        const BP = window.PPBuildProgress || null;
        if (BP) BP.show();
        else Swal.fire({
            title: 'Building…',
            html: '<div class="d-flex flex-column align-items-center gap-2"><div class="spinner-border text-primary"></div><p class="small text-muted mb-0">Generating pages, assets, and configuration…</p></div>',
            allowOutsideClick: false,
            showConfirmButton: false,
        });

        try {
            const response = await fetch(`${projectRoot}/finished/action/build`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
            });
            // Read as text first so a non-JSON body (e.g. a server error page or an
            // empty 500) becomes a readable message instead of a cryptic parse error.
            const raw = await response.text();
            let data;
            try {
                data = raw ? JSON.parse(raw) : {};
            } catch (parseErr) {
                data = {
                    success: false,
                    message: raw
                        ? ('The build returned an unexpected response (HTTP ' + response.status + '): ' + raw.replace(/<[^>]*>/g, ' ').trim().slice(0, 300))
                        : ('The build endpoint returned an empty response (HTTP ' + response.status + '). Check the server log.'),
                };
            }

            if (response.ok && data.success) {
                if (BP) BP.hide(); else Swal.close();
                const esc = (s) => String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;')
                    .replace(/>/g, '&gt;').replace(/"/g, '&quot;');
                const parts = [`<div style="margin-bottom:.2rem"><strong>${data.files || 0}</strong> files generated in <strong>${data.duration || 0}s</strong>.</div>`];
                if (data.outputUrl) {
                    // A public URL was resolved from the live request — offer a direct,
                    // new-tab link to the running app plus the plain URL underneath.
                    parts.push(
                        `<a href="${esc(data.outputUrl)}" target="_blank" rel="noopener" ` +
                        `style="display:inline-flex;align-items:center;gap:.45rem;margin-top:.55rem;padding:.5rem .95rem;` +
                        `background:#4f46e5;color:#fff;border-radius:.55rem;font-weight:600;text-decoration:none;">` +
                        `<i class="bi bi-box-arrow-up-right"></i> View Generated App</a>`,
                        `<div style="margin-top:.5rem;font-size:.78rem;opacity:.7;word-break:break-all">${esc(data.outputUrl)}</div>`
                    );
                } else if (data.outputRelative) {
                    // No public URL (e.g. the build sits above the web root) — show the
                    // path so the author still knows where the app was written.
                    parts.push(`<div class="text-muted" style="margin-top:.4rem">Your app is at <code>${esc(data.outputRelative)}</code></div>`);
                }
                const result = await Swal.fire({
                    title: 'Build successful',
                    html: `<div class="text-center">${parts.join('')}</div>`,
                    icon: 'success',
                    confirmButtonText: 'View details',
                    showCancelButton: true,
                    cancelButtonText: 'Close',
                });
                if (result.isConfirmed) saveCurrentModuleAndNavigate('/finished');
            } else {
                // Same "Build stopped" step screen as the Finished module.
                if (BP) BP.done(false, data.message || 'The application could not be generated.');
                else await Swal.fire('Build failed', data.message || 'The application could not be generated.', 'error');
            }
        } catch (error) {
            if (BP) BP.done(false, error.message || 'The build request failed.');
            else { Swal.close(); await Swal.fire('Build error', error.message || 'The build request failed.', 'error'); }
        }
    });

    // ── Resume where you left off ──────────────────────────────────────
    // Record the current screen (per user) so the next project open can offer
    // to jump back to it.
    function recordResume() {
        if (!shellProjectSlug) return;
        try {
            fetch(`${projectRoot}/resume`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ uri: currentUri }),
                keepalive: true,
            }).catch(() => {});
        } catch (error) { /* non-critical */ }
    }

    // On a fresh project open the server sets window.PP_RESUME to the last
    // screen; offer to take the user there.
    function offerResume() {
        const r = window.PP_RESUME;
        if (!r || !r.url || !r.label || typeof Swal === 'undefined') return;
        Swal.fire({
            title: 'Welcome back',
            html: `Pick up where you left off in <strong>${String(r.label).replace(/[&<>"]/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]))}</strong>?`,
            icon: 'question',
            showCancelButton: true,
            confirmButtonText: 'Take me there',
            cancelButtonText: 'Stay on Workspace',
            reverseButtons: true,
            focusConfirm: true,
        }).then((res) => {
            if (res.isConfirmed) window.location.href = r.url;
        });
    }

    // Informative tooltips for the top-toolbar controls (works on every page,
    // regardless of which module renders — idempotent via getOrCreateInstance).
    function initNavTooltips() {
        if (!window.bootstrap || !bootstrap.Tooltip) return;
        var nav = document.getElementById('projectNavbar');
        if (!nav) return;
        nav.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(function (el) {
            bootstrap.Tooltip.getOrCreateInstance(el, { delay: { show: 400, hide: 0 }, trigger: 'hover' });
        });
    }

    function shellInit() {
        setupSidebarToggle();
        recordResume();
        offerResume();
        initNavTooltips();
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', shellInit, { once: true });
    } else {
        shellInit();
    }
})();
