// app.js — bootstrap. Runs after every other module's IIFE has
// registered its functions on window.app.modules.
(function () {
    'use strict';

    var state = window.app.state;
    var loader = window.app.modules.loader;
    var tree = window.app.modules.tree;
    var events = window.app.modules.events;

    // Walk a `?file=` path segment-by-segment from the current root.
    // Each non-leaf segment is matched against the parent's children
    // by name; if found and it's a folder, expand+load it (so its
    // children populate state.nodes) and recurse into them. The leaf
    // segment becomes the selected/previewed entry. Silently no-ops
    // when any segment doesn't resolve — deep links aren't a hard
    // contract, just an affordance.
    async function openDeepLink(path) {
        var segs = path.split('/').filter(Boolean);
        if (segs.length === 0) return;
        var tree = window.app.modules.tree;
        var prev = window.app.modules.preview;

        // Lookup helper: find a node by name within a given parent's
        // immediate children. Top-level walk uses state.rootIds.
        function findChild(parentIds, name) {
            for (var i = 0; i < parentIds.length; i++) {
                var n = window.app.state.nodes.get(parentIds[i]);
                if (n && n.name === name) return n;
            }
            return null;
        }

        var ids = window.app.state.rootIds;
        for (var i = 0; i < segs.length; i++) {
            var node = findChild(ids, segs[i]);
            if (!node) return; // segment not present in this listing
            if (i === segs.length - 1) {
                // Leaf — select + preview.
                window.app.state.selectedId = node.id;
                window.app.state.lastPreviewedNodeId = node.id;
                tree.render();
                if (prev && !node.isDir) prev.showFilePreview(node);
                return;
            }
            // Intermediate — must be a folder we can expand into.
            if (!(node.isDir || node.isZip)) return;
            if (!node.loaded) {
                await tree.toggleFolder(node.id); // loads + sets expanded
            } else if (!node.expanded) {
                node.expanded = true;
            }
            ids = node.childIds;
        }
    }

    async function bootstrap() {
        events.init();

        // Honor ?file=<path> deep links: external clients (the profile
        // page's "edit your .zddc files" list, future bookmarks, etc.)
        // can link directly to "open browse at <dir>, with this entry
        // selected and previewed". Single-segment names (?file=foo.md)
        // match in the current directory; multi-segment paths
        // (?file=a/b/foo.md) walk into a/ then b/ then open foo.md,
        // loading intermediate directories on the way.
        //
        // When the LEAF (or any intermediate segment) is hidden
        // (.zddc, .form.yaml, …), flip showHidden ON BEFORE the
        // initial listing fetch so dotfiles appear in the tree.
        var qs = new URLSearchParams(location.search);
        var deepFile = qs.get('file');
        // Explicit ?hidden=1 in the URL: restore the show-hidden toggle
        // on reload (the URL is the persistence layer for this flag —
        // see events.js syncURLToSelection).
        if (qs.get('hidden') === '1') state.showHidden = true;
        if (deepFile) {
            var segs = deepFile.split('/').filter(Boolean);
            for (var si = 0; si < segs.length; si++) {
                var c = segs[si].charAt(0);
                if (c === '.' || c === '_') { state.showHidden = true; break; }
            }
        }

        // Try server auto-detect. If this page is served by zddc-server
        // (or any server with a Caddy-shaped JSON listing), load the
        // current directory automatically. Otherwise show the empty
        // state with the "Select Directory" button.
        var detected = await loader.autoDetectServerMode();
        if (detected) {
            tree.setRoot(detected.entries);
            events.showBrowseRoot();
            tree.render();
            if (events.prefetchScopeAccess) events.prefetchScopeAccess();
            events.statusInfo('Loaded ' + detected.entries.length + ' item'
                + (detected.entries.length === 1 ? '' : 's')
                + ' from ' + detected.path);
            // The initial events.init() applied view mode before the
            // cascade headers were available (no fetch yet). Now that
            // state.scopeDefaultTool is set from the detection
            // response, re-resolve so an /incoming URL auto-activates
            // grid mode.
            if (events.applyResolvedViewMode) events.applyResolvedViewMode();

            // Final step of the deep link: walk the path segment by
            // segment, expanding + loading intermediate directories
            // before opening the leaf. Single-segment names use the
            // same code path with one iteration.
            if (deepFile) {
                await openDeepLink(deepFile);
            }
        }
        // Else: empty state stays visible; user can click Select Directory.

        // Browser back / forward: client-side rescope when the URL
        // changes via popstate. We can't tell server-vs-fs mode from
        // popstate alone, so only honor it in server mode.
        window.addEventListener('popstate', async function () {
            if (window.app.state.source !== 'server') return;
            var path = location.pathname;
            if (!path.endsWith('/')) path += '/';
            var popQS = new URLSearchParams(location.search);
            if (popQS.get('hidden') === '1') window.app.state.showHidden = true;
            else window.app.state.showHidden = false;
            // Join the shared nav token: rapid back/forward (or back/forward
            // while an in-tool rescope is mid-flight) must not apply a stale
            // listing on top of a newer one.
            var seq = events.beginNav ? events.beginNav() : 0;
            try {
                var es = await loader.fetchServerChildren(path);
                if (events.isCurrentNav && !events.isCurrentNav(seq)) return;
                window.app.state.currentPath = path;
                window.app.state.selectedId = null;
                window.app.state.lastPreviewedNodeId = null;
                tree.setRoot(es);
                tree.render();
                if (events.prefetchScopeAccess) events.prefetchScopeAccess();
                // Route through clearPreview so a live editor is disposed
                // (not leaked) when back/forward swaps scope.
                var pmod = window.app.modules.preview;
                if (pmod && pmod.clearPreview) pmod.clearPreview();
                else {
                    var previewBody = document.getElementById('previewBody');
                    if (previewBody) previewBody.innerHTML = '';
                }
                var previewTitle = document.getElementById('previewTitle');
                if (previewTitle) previewTitle.textContent = 'No file selected';
                // Reapply view mode for the new URL (incoming/ → grid, etc).
                if (events.applyResolvedViewMode) events.applyResolvedViewMode();
                // Re-walk ?file= so back/forward restores selection +
                // expansion, not just scope.
                var popFile = popQS.get('file');
                if (popFile) await openDeepLink(popFile);
            } catch (_e) { /* swallow — leave the tree as-is */ }
        });
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', bootstrap);
    } else {
        bootstrap();
    }
})();
