import { comfyConfirm, comfyPrompt } from "../../app/dialogs.js";
import { comfyToast } from "../../app/toast.js";
import { EVENTS } from "../../app/events.js";
import { t } from "../../app/i18n.js";
import { startTimer, endTimer, trackGridRender, mark } from "../../app/metrics.js";
import { setupStatusPolling, triggerScan, updateStatus } from "../status/StatusDot.js";
import {
    hydrateGridFromSnapshot,
    loadAssets,
    loadAssetsFromList,
    prepareGridForScopeSwitch,
    disposeGrid,
    refreshGrid,
    captureAnchor,
    restoreAnchor,
    bindGridScanListeners,
    disposeGridScanListeners,
} from "../grid/gridApi.js";
import { get, post, getCollectionAssets, getDuplicateAlerts } from "../../api/client.js";
import { ENDPOINTS } from "../../api/endpoints.js";
import { showAssetInSidebar, closeSidebar } from "../../components/sidebar/SidebarView.js";
import { createRatingBadge, createTagsBadge } from "../../components/Badges.js";
import { loadMajoorSettings } from "../../app/settings.js";
import { createPopoverManager } from "./views/popoverManager.js";

import { createPanelStateBridge } from "../../stores/panelStateBridge.js";
import { getOptionalPanelStore } from "../../stores/getOptionalPanelStore.js";
import { bindMessagePopoverController } from "./messages/messagePopoverController.js";

import { normalizeQuery } from "./controllers/query.js";
import { createGridController } from "./controllers/gridController.js";
import { createBrowserNavigationController } from "./controllers/browserNavigationController.js";
import { createScopeController } from "./controllers/scopeController.js";
import { createContextController } from "./controllers/contextController.js";
import { createCustomRootsController } from "./controllers/customRootsController.js";
import { createSortController } from "./controllers/sortController.js";
import { bindSidebarOpen } from "./controllers/sidebarController.js";
import { createPanelHotkeysController } from "./controllers/panelHotkeysController.js";
import { createRatingHotkeysController } from "./controllers/ratingHotkeysController.js";
import { getHotkeysState, setHotkeysScope } from "./controllers/hotkeysState.js";
import { bindGridContextMenu } from "../contextmenu/GridContextMenu.js";
import { bindGridSelectionState } from "../../vue/composables/useGridSelection.js";
import { createAssetsQueryController } from "../../vue/composables/useAssetsQuery.js";
import { clearActiveGridContainer, setActiveGridContainer } from "./panelRuntimeRefs.js";

import { bootstrapPanelContainer } from "./panelBootstrap.js";
import { createSettingsSync } from "./panelSettingsSync.js";
import { bindGridEvents } from "./panelGridEventBindings.js";
import { bindSimilarSearch } from "./panelSimilarSearch.js";
import { bindPinnedFolders } from "./panelPinnedFolders.js";
import { setupFiltersInit } from "./panelFiltersInit.js";
import { buildContextMenuExtraActions } from "./panelContextMenuExtraActions.js";
import { consumePendingGeneratedAssets } from "../runtime/pendingGeneratedAssets.js";
import { MAJOOR_SETTINGS_DIALOG_EVENT } from "../../app/settings/MajoorSettingsDialog.js";

/**
 * Wires controller/service orchestration against Vue-owned panel surfaces.
 *
 * `external` allows Vue components to provide pre-created DOM elements.
 * Required external keys: statusSection, statusDot, statusText,
 * capabilitiesSection, headerSection, summaryBar, updateSummaryBar,
 * folderBreadcrumb, browseSection, gridWrapper, gridContainer, sidebar.
 */
export async function mountAssetsManagerPanelRuntime(
    container: HTMLElement,
    { useComfyThemeUI = true, external = {} }: { useComfyThemeUI?: boolean; external?: Record<string, any> } = {},
): Promise<unknown> {
    return _mountPanelRuntimeImpl(container, { useComfyThemeUI, external });
}

async function _mountPanelRuntimeImpl(container: any, { useComfyThemeUI = true, external = {} }: { useComfyThemeUI?: boolean; external?: Record<string, any> } = {}) {
    startTimer("panelRender");
    mark("panelRender");

    const panelLifecycleAC = typeof AbortController !== "undefined" ? new AbortController() : null;

    // -- 1. BOOTSTRAP ------------------------------------------------------
    const { popovers, hostWrapper, hostWrapperPrevStyle } = bootstrapPanelContainer(container, {
        useComfyThemeUI,
    });

    // -- 2. VALIDATE STATE + EXTERNAL ELEMENTS -----------------------------
    const state = getOptionalPanelStore();
    if (!state) {
        throw new Error(
            "[Majoor] mountAssetsManagerPanelRuntime requires an active Pinia panel store",
        );
    }
    for (const key of [
        "statusSection",
        "statusDot",
        "statusText",
        "capabilitiesSection",
        "headerSection",
        "summaryBar",
        "updateSummaryBar",
        "folderBreadcrumb",
        "browseSection",
        "gridWrapper",
        "gridContainer",
        "sidebar",
    ]) {
        if (!external?.[key]) {
            throw new Error(`[Majoor] mountAssetsManagerPanelRuntime now requires external.${key}`);
        }
    }

    // -- 3. STATE BRIDGE + READ/WRITE HELPERS ------------------------------
    const panelStateBridge = createPanelStateBridge(null, [
        "searchQuery",
        "scrollTop",
        "scope",
        "customRootId",
        "customRootLabel",
        "currentFolderRelativePath",
        "sort",
        "lastGridCount",
        "lastGridTotal",
        "activeAssetId",
        "selectedAssetIds",
        "sidebarOpen",
        "collectionId",
        "collectionName",
        "viewScope",
        "similarResults",
        "similarTitle",
        "similarSourceAssetId",
    ]);
    state.viewScope =
        String(state.viewScope || "")
            .trim()
            .toLowerCase() === "similar"
            ? "similar"
            : "";
    state.similarResults = Array.isArray(state.similarResults) ? state.similarResults : [];
    state.similarTitle = String(state.similarTitle || "");
    state.similarSourceAssetId = String(state.similarSourceAssetId || "");

    const readPanelValue = (key: string, fallback: any = "") =>
        panelStateBridge.read(key, (state as any)?.[key] ?? fallback);
    const writePanelValue = (key: any, value: any) => panelStateBridge.write(key, value);
    const readSelectedAssetIds = () => {
        const selected = readPanelValue("selectedAssetIds", []);
        return Array.isArray(selected) ? selected.map(String).filter(Boolean) : [];
    };
    const readActiveAssetId = () => String(readPanelValue("activeAssetId", "") || "").trim();
    const isSidebarOpen = () => !!readPanelValue("sidebarOpen", false);

    // -- 4. DESTRUCTURE EXTERNAL ELEMENTS ----------------------------------
    const {
        header,
        tabButtons,
        customMenuBtn,
        filterBtn,
        sortBtn,
        collectionsBtn,
        pinnedFoldersBtn,
        messageBtn,
        settingsBtn,
        customPopover,
        customSelect,
        customAddBtn,
        customRemoveBtn,
        filterPopover,
        kindSelect,
        wfCheckbox,
        workflowTypeSelect,
        workflowIdInput,
        workflowModelInput,
        workflowRunsOnSelect,
        ratingSelect,
        minSizeInput,
        maxSizeInput,
        resolutionPresetSelect,
        minWidthInput,
        minHeightInput,
        maxWidthInput,
        maxHeightInput,
        dateRangeSelect,
        dateExactInput,
        agendaContainer,
        sortPopover,
        sortMenu,
        collectionsPopover,
        pinnedFoldersPopover,
        pinnedFoldersController,
        pinnedFoldersMenu,
        messagePopoverTitle,
        messagePopover,
        messageTabBtn,
        messageTabBadge,
        historyTabBtn,
        historyTabBadge,
        historyTabCount,
        historyPanel,
        shortcutsTabBtn,
        messageList,
        shortcutsPanel,
        markReadBtn,
        searchSection,
        searchInputEl,
        similarBtn,
        similarPopover,
        similarFindBtn,
        similarDuplicatesBtn,
        similarSameNodeBtn,
        similarSameWorkflowBtn,
        setSemanticEnabled,
        _headerDispose,
    } = external.headerSection;
    const hasVueHeaderSection = !!external?.headerSection?.isVueHeader;

    // Restore persistent search query.
    if (panelStateBridge.read("searchQuery", "")) {
        searchInputEl.value = panelStateBridge.read("searchQuery", "");
    }
    searchInputEl.addEventListener(
        "input",
        (e: any) => {
            panelStateBridge.write("searchQuery", e.target.value);
        },
        { signal: panelLifecycleAC?.signal },
    );

    const summaryBar = external.summaryBar;
    const updateSummaryBar = external.updateSummaryBar;
    const folderBreadcrumb = external.folderBreadcrumb;
    const folderBreadcrumbController = external.folderBreadcrumbController || null;
    const statusSection = external.statusSection;
    const statusDot = external.statusDot;
    const statusText = external.statusText;
    const capabilitiesSection = external.capabilitiesSection;
    const browseSection = external.browseSection;
    const gridWrapper = external.gridWrapper;
    const sidebar = external.sidebar;
    const settings = loadMajoorSettings();

    const content = document.createElement("div");
    content.classList.add("mjr-am-content");
    let gridContainer: any = null;
    let sidebarController: any = null;

    container.tabIndex = -1;

    // -- 5. SCROLL PERSISTENCE + GRID CONTAINER SETUP ----------------------
    let _scrollTimer: any = null;
    let _lastUserInteractionAt = 0;
    const markUserInteraction = () => {
        try {
            _lastUserInteractionAt = Date.now();
        } catch {
            _lastUserInteractionAt = 0;
        }
    };
    const hasVueGridHostState =
        typeof external?.bindGridHostState === "function" &&
        typeof external?.restoreGridUiState === "function";
    const shouldPersistScrollFromPanel = !hasVueGridHostState;
    gridWrapper.addEventListener(
        "scroll",
        () => {
            markUserInteraction();
            if (!shouldPersistScrollFromPanel) return;
            if (_scrollTimer) clearTimeout(_scrollTimer);
            _scrollTimer = setTimeout(() => {
                try {
                    panelStateBridge.write("scrollTop", gridWrapper.scrollTop);
                } catch (e) {
                    console.debug?.(e);
                }
            }, 100);
        },
        { passive: true, signal: panelLifecycleAC?.signal },
    );

    gridContainer = external.gridContainer;
    if (gridContainer.parentElement !== gridWrapper) {
        gridWrapper.appendChild(gridContainer);
    }
    setActiveGridContainer(gridContainer);
    try {
        gridContainer.dataset.mjrScope = String(
            readPanelValue("scope", state.scope || "output") || "output",
        );
    } catch (e) {
        console.debug?.(e);
    }

    const summaryBarDisposers: any[] = [];
    const registerSummaryDispose = (fn: any) => {
        if (typeof fn === "function") summaryBarDisposers.push(fn);
    };
    gridContainer._mjrSummaryBarDispose = () => {
        for (const fn of summaryBarDisposers.splice(0)) {
            try {
                fn();
            } catch (e) {
                console.debug?.(e);
            }
        }
    };
    try {
        const handler = () => {
            try {
                updateSummaryBar?.({ state, gridContainer });
            } catch (e) {
                console.debug?.(e);
            }
        };
        gridContainer.addEventListener("mjr:grid-stats", handler, {
            signal: panelLifecycleAC?.signal,
        });
        registerSummaryDispose(() => {
            try {
                gridContainer.removeEventListener("mjr:grid-stats", handler);
            } catch (e) {
                console.debug?.(e);
            }
        });
    } catch (e) {
        console.debug?.(e);
    }
    try {
        bindGridScanListeners();
    } catch (e) {
        console.debug?.(e);
    }

    // -- 6. GRID API FUNCTION RESOLUTION -----------------------------------
    const loadAssetsFn = external.loadAssets ?? loadAssets;
    const loadAssetsFromListFn = external.loadAssetsFromList ?? loadAssetsFromList;
    const prepareGridForScopeSwitchFn =
        external.prepareGridForScopeSwitch ?? prepareGridForScopeSwitch;
    const disposeGridFn = external.disposeGrid ?? disposeGrid;
    const refreshGridFn = external.refreshGrid ?? refreshGrid;
    const captureAnchorFn = external.captureAnchor ?? captureAnchor;
    const restoreAnchorFn = external.restoreAnchor ?? restoreAnchor;
    const hydrateGridFromSnapshotFn = external.hydrateGridFromSnapshot ?? hydrateGridFromSnapshot;

    // -- 7. GRID CONTEXT MENU ----------------------------------------------
    if (typeof external?.onGridContainerReady === "function") {
        try {
            external.onGridContainerReady(gridContainer, { getState: () => state });
        } catch (e) {
            console.debug?.(e);
        }
    } else {
        container._mjrGridContextMenuUnbind = bindGridContextMenu({
            gridContainer,
            getState: () => state as Record<string, any>,
        });
    }

    // -- 8. SETTINGS SYNC --------------------------------------------------
    const similarEnabledTitle = t("search.findSimilar", "Find Similar");
    const similarDisabledTitle = t(
        "search.similarDisabled",
        "AI features are disabled in settings",
    );
    const { applyWatcherForScope, isAiEnabled } = createSettingsSync({
        container,
        state,
        sidebar,
        browseSection,
        gridWrapper,
        similarBtn,
        setSemanticEnabled,
        similarEnabledTitle,
        similarDisabledTitle,
        refreshGridFn,
        isSidebarOpen,
        readActiveAssetId,
        getSidebarController: () => sidebarController,
        panelLifecycleAC,
    });

    // -- 9. DOM ASSEMBLY ---------------------------------------------------
    content.appendChild(statusSection);
    content.appendChild(searchSection);
    content.appendChild(folderBreadcrumb);
    content.appendChild(summaryBar);
    content.appendChild(browseSection);
    container.appendChild(header);
    try {
        container._mjrVersionUpdateCleanup?.();
    } catch (e) {
        console.debug?.(e);
    }
    container._mjrVersionUpdateCleanup = _headerDispose || header._mjrVersionUpdateCleanup;
    container.appendChild(content);

    // -- 10. GRID CONTROLLER -----------------------------------------------
    const getQuery = () => normalizeQuery(searchInputEl);
    const gridController = createGridController({
        gridContainer,
        loadAssets: loadAssetsFn,
        loadAssetsFromList: loadAssetsFromListFn,
        getCollectionAssets,
        disposeGrid: disposeGridFn,
        getQuery,
        searchInputEl,
        state,
    });

    // -- 11. SHARED CONTROLLER VARIABLES ----------------------------------
    let scopeController: any = null;
    let contextController: any = null;
    let _duplicatesAlert: any = null;
    let _dupPollTimer: any = null;
    let _autoLoadTimer: any = null;
    let browserNav: any = null;
    let _unbindBrowserFolderNav: any = null;
    let selectionState: any = null;
    let assetsQueryController: any = null;
    let settingsDialogOpen = false;

    const isPopoverOpen = (popover: any) =>
        !!popover && String(popover?.style?.display || "").toLowerCase() === "block";

    const syncOpenPopoverButtonStates = () => {
        const pairs = [
            [filterBtn, filterPopover],
            [sortBtn, sortPopover],
            [collectionsBtn, collectionsPopover],
            [pinnedFoldersBtn, pinnedFoldersPopover],
            [messageBtn, messagePopover],
            [similarBtn, similarPopover],
        ];
        for (const [button, popover] of pairs) {
            try {
                button?.classList?.toggle?.("mjr-popover-open", isPopoverOpen(popover));
                button?.setAttribute?.("aria-expanded", isPopoverOpen(popover) ? "true" : "false");
            } catch (e) {
                console.debug?.(e);
            }
        }
        try {
            settingsBtn?.classList?.toggle?.("mjr-popover-open", settingsDialogOpen);
            settingsBtn?.setAttribute?.("aria-expanded", settingsDialogOpen ? "true" : "false");
        } catch (e) {
            console.debug?.(e);
        }
    };

    const notifyContextChanged = () => {
        try {
            contextController?.update?.();
        } catch (e) {
            console.debug?.(e);
        }
        try {
            browserNav?.renderBreadcrumb?.();
        } catch (e) {
            console.debug?.(e);
        }
        syncOpenPopoverButtonStates();
    };

    // -- 12. SELECTION STATE + BROWSER NAV --------------------------------
    if (!hasVueGridHostState) {
        selectionState = bindGridSelectionState({
            gridContainer,
            readSelectedAssetIds,
            readActiveAssetId: () => readActiveAssetId(),
            writeSelectedAssetIds: (ids: any) => writePanelValue("selectedAssetIds", ids),
            writeActiveAssetId: (id: any) => writePanelValue("activeAssetId", id),
            onSelectionChanged: () => {
                markUserInteraction();
                notifyContextChanged();
            },
            lifecycleSignal: panelLifecycleAC?.signal || null,
        });
    }
    browserNav = createBrowserNavigationController({
        state,
        gridContainer,
        folderBreadcrumb,
        folderBreadcrumbController,
        customSelect,
        reloadGrid: () => gridController.reloadGrid(),
        clearSelection: () => {
            try {
                gridContainer?._mjrSetSelection?.([], "");
            } catch (e) {
                console.debug?.(e);
            }
        },
        onContextChanged: () => {
            try {
                contextController?.update?.();
            } catch (e) {
                console.debug?.(e);
            }
        },
        lifecycleSignal: panelLifecycleAC?.signal || null,
    });
    try {
        _unbindBrowserFolderNav = browserNav.bindGridFolderNavigation();
    } catch (e) {
        console.debug?.(e);
    }

    // -- 13. DUPLICATE ALERTS ---------------------------------------------
    const refreshDuplicateAlerts = async () => {
        const scope = String(
            readPanelValue("scope", state.scope || "output") || "output",
        ).toLowerCase();
        const customRootId = String(
            readPanelValue("customRootId", state.customRootId || "") || "",
        ).trim();
        if (scope === "custom" && !customRootId) {
            _duplicatesAlert = null;
            notifyContextChanged();
            return;
        }
        try {
            const result = await getDuplicateAlerts(
                { scope, customRootId, maxGroups: 4, maxPairs: 4 },
                { signal: panelLifecycleAC?.signal },
            );
            if (!result?.ok) return;
            const data = result.data || {};
            _duplicatesAlert = {
                exactCount: Array.isArray(data.exact_groups) ? data.exact_groups.length : 0,
                similarCount: Array.isArray(data.similar_pairs) ? data.similar_pairs.length : 0,
                firstGroup: Array.isArray(data.exact_groups) ? data.exact_groups[0] : null,
                firstPair: Array.isArray(data.similar_pairs) ? data.similar_pairs[0] : null,
            };
            notifyContextChanged();
        } catch (e) {
            console.debug?.(e);
        }
    };

    // -- 14. QUEUED RELOAD -------------------------------------------------
    const REQUEST_QUEUED_RELOAD_DEBOUNCE_MS = 120;
    let _queuedReloadTimer: any = null;
    const requestQueuedReload = () => {
        try {
            if (_queuedReloadTimer) clearTimeout(_queuedReloadTimer);
        } catch (e) {
            console.debug?.(e);
        }
        _queuedReloadTimer = setTimeout(() => {
            _queuedReloadTimer = null;
            queuedReload().catch((err) => {
                try {
                    console.warn("[Majoor] queuedReload failed", err);
                } catch (e) {
                    console.debug?.(e);
                }
            });
        }, REQUEST_QUEUED_RELOAD_DEBOUNCE_MS);
    };

    // -- 15. GRID EVENT BINDINGS -------------------------------------------
    bindGridEvents({
        gridContainer,
        panelLifecycleAC,
        requestQueuedReload,
        notifyContextChanged,
        markUserInteraction,
        writePanelValue,
        popovers,
        collectionsPopover,
        gridController,
        registerSummaryDispose,
    });

    // -- 16. CUSTOM ROOTS CONTROLLER ---------------------------------------
    const customRootsController = createCustomRootsController({
        state,
        customSelect,
        customRemoveBtn,
        comfyConfirm,
        comfyPrompt,
        comfyToast,
        get,
        post,
        ENDPOINTS,
        reloadGrid: gridController.reloadGrid,
        onRootChanged: async () => {
            try {
                browserNav?.resetHistory?.();
            } catch (e) {
                console.debug?.(e);
            }
            try {
                await applyWatcherForScope(state.scope);
            } catch (e) {
                console.debug?.(e);
            }
            try {
                await refreshDuplicateAlerts();
            } catch (e) {
                console.debug?.(e);
            }
        },
    });
    const disposeCustomRootsBindings = customRootsController.bind({
        customAddBtn,
        customRemoveBtn,
    });
    try {
        window.addEventListener(
            "mjr:custom-roots-changed",
            async (e: Event) => {
                const preferredId = String((e as CustomEvent)?.detail?.preferredId || "").trim() || null;
                await customRootsController.refreshCustomRoots(preferredId);
            },
            { signal: panelLifecycleAC?.signal },
        );
    } catch (e) {
        console.debug?.(e);
    }

    // -- 17. RECONCILE VISIBLE SELECTION ----------------------------------
    const reconcileVisibleSelection = () => {
        try {
            const getAssets = gridContainer?._mjrGetAssets;
            const setSelection = gridContainer?._mjrSetSelection;
            if (typeof getAssets !== "function" || typeof setSelection !== "function") return;
            const visibleIds = (Array.isArray(getAssets()) ? getAssets() : [])
                .map((asset: any) => String(asset?.id ?? "").trim())
                .filter(Boolean);
            const selectedIds = readSelectedAssetIds();
            const next = selectedIds.filter((id) => visibleIds.includes(id));
            const pruned = selectedIds.length - next.length;
            if (pruned <= 0) return;
            const currentActiveId = readActiveAssetId();
            const nextActiveId = next.includes(currentActiveId) ? currentActiveId : next[0] || "";
            setSelection(next, nextActiveId);
            comfyToast(
                t(
                    "toast.selectionPruned",
                    "{count} items deselected - not visible in current view",
                    { count: pruned },
                ),
                "info",
                2600,
                { noHistory: true },
            );
        } catch (e) {
            console.debug?.(e);
        }
    };

    // -- 18. SCOPE CONTROLLER ---------------------------------------------
    scopeController = createScopeController({
        state,
        tabButtons,
        customMenuBtn,
        customPopover,
        popovers,
        refreshCustomRoots: customRootsController.refreshCustomRoots,
        reloadGrid: gridController.reloadGrid,
        reconcileSelection: reconcileVisibleSelection,
        onChanged: () => {
            try {
                if (gridContainer) {
                    gridContainer.dataset.mjrScope = String(
                        readPanelValue("scope", state.scope || "output") || "output",
                    );
                }
            } catch (e) {
                console.debug?.(e);
            }
            notifyContextChanged();
        },
        onScopeChanged: async () => {
            try {
                await applyWatcherForScope(
                    String(readPanelValue("scope", state.scope || "output") || "output"),
                );
            } catch (e) {
                console.debug?.(e);
            }
            try {
                await refreshDuplicateAlerts();
            } catch (e) {
                console.debug?.(e);
            }
        },
        onBeforeReload: async () => {
            // Sync key dataset fields *before* prepareGridForScopeSwitch so that
            // snapshot-cache hydration uses the correct (new-scope) key.
            try {
                gridContainer.dataset.mjrScope = readPanelValue("scope", "output");
                gridContainer.dataset.mjrCustomRootId = readPanelValue("customRootId", "") || "";
                gridContainer.dataset.mjrSubfolder =
                    readPanelValue("currentFolderRelativePath", "") || "";
                gridContainer.dataset.mjrCollectionId = readPanelValue("collectionId", "") || "";
                gridContainer.dataset.mjrViewScope = readPanelValue("viewScope", "") || "";
            } catch (e) {
                console.debug?.(e);
            }
            try {
                prepareGridForScopeSwitchFn(gridContainer);
            } catch (e) {
                console.debug?.(e);
            }
            try {
                await updateStatus(
                    statusDot,
                    statusText,
                    capabilitiesSection,
                    {
                        scope: readPanelValue("scope", state.scope || "output"),
                        customRootId: readPanelValue("customRootId", state.customRootId || ""),
                    },
                    null,
                    { signal: panelLifecycleAC?.signal || null },
                );
            } catch (e) {
                console.debug?.(e);
            }
        },
    });

    const exitSimilarViewIfActive = async ({ reload = false } = {}) => {
        try {
            if (
                String(readPanelValue("viewScope", state.viewScope || "") || "")
                    .trim()
                    .toLowerCase() !== "similar"
            )
                return;
            await scopeController?.clearSimilarScope?.({ reload });
        } catch (e) {
            console.debug?.(e);
        }
    };

    Object.values(tabButtons as Record<string, any>).forEach((btn) => {
        btn.addEventListener("click", () => scopeController.setScope(btn.dataset.scope), {
            signal: panelLifecycleAC?.signal,
        });
    });

    // -- 19. MESSAGE POPOVER -----------------------------------------------
    bindMessagePopoverController({
        messageBtn,
        messagePopover,
        title: messagePopoverTitle,
        messageList,
        historyTabBtn,
        historyTabBadge,
        historyTabCount,
        historyPanel,
        shortcutsPanel,
        messageTabBtn,
        messageTabBadge,
        shortcutsTabBtn,
        markReadBtn,
        popovers,
        signal: panelLifecycleAC?.signal,
        onBeforeToggle: () => {
            popovers.close(customPopover);
            popovers.close(filterPopover);
            popovers.close(sortPopover);
            popovers.close(collectionsPopover);
            popovers.close(pinnedFoldersPopover);
        },
    });

    // -- 20. POPOVER BUTTONS -----------------------------------------------
    popovers.setDismissWhitelist([
        customPopover,
        filterPopover,
        sortPopover,
        collectionsPopover,
        pinnedFoldersPopover,
        messagePopover,
        similarPopover,
        customMenuBtn,
        filterBtn,
        sortBtn,
        collectionsBtn,
        pinnedFoldersBtn,
        messageBtn,
        similarBtn,
    ]);
    popovers.setOnVisibilityChanged?.(() => syncOpenPopoverButtonStates());
    try {
        window.addEventListener(
            MAJOOR_SETTINGS_DIALOG_EVENT,
            (event: any) => {
                settingsDialogOpen = Boolean(event?.detail?.open);
                syncOpenPopoverButtonStates();
            },
            { signal: panelLifecycleAC?.signal },
        );
    } catch (e) {
        console.debug?.(e);
    }
    syncOpenPopoverButtonStates();

    customMenuBtn.addEventListener(
        "click",
        (e: any) => {
            e.stopPropagation();
            popovers.close(filterPopover);
            popovers.close(sortPopover);
            popovers.close(collectionsPopover);
            popovers.close(messagePopover);
            popovers.close(similarPopover);
            popovers.toggle(customPopover, customMenuBtn);
        },
        { signal: panelLifecycleAC?.signal },
    );
    filterBtn.addEventListener(
        "click",
        (e: any) => {
            e.stopPropagation();
            popovers.close(customPopover);
            popovers.close(sortPopover);
            popovers.close(collectionsPopover);
            popovers.close(messagePopover);
            popovers.close(similarPopover);
            popovers.toggle(filterPopover, filterBtn);
        },
        { signal: panelLifecycleAC?.signal },
    );
    collectionsBtn.addEventListener(
        "click",
        (e: any) => {
            e.stopPropagation();
            popovers.close(customPopover);
            popovers.close(filterPopover);
            popovers.close(sortPopover);
            popovers.close(messagePopover);
            popovers.close(similarPopover);
            popovers.toggle(collectionsPopover, collectionsBtn);
        },
        { signal: panelLifecycleAC?.signal },
    );

    const sortController = hasVueHeaderSection
        ? null
        : createSortController({
              state,
              sortBtn,
              sortMenu,
              sortPopover,
              popovers,
              reloadGrid: gridController.reloadGrid,
              onChanged: () => notifyContextChanged(),
          });
    if (sortController) {
        sortController.bind({
            onBeforeToggle: () => {
                popovers.close(customPopover);
                popovers.close(filterPopover);
                popovers.close(collectionsPopover);
                popovers.close(messagePopover);
                popovers.close(similarPopover);
            },
        });
    } else {
        sortBtn.addEventListener(
            "click",
            (e: any) => {
                e.stopPropagation();
                popovers.close(customPopover);
                popovers.close(filterPopover);
                popovers.close(collectionsPopover);
                popovers.close(messagePopover);
                popovers.close(similarPopover);
                popovers.toggle(sortPopover, sortBtn);
            },
            { signal: panelLifecycleAC?.signal },
        );
    }

    // -- 21. SIMILAR SEARCH ------------------------------------------------
    bindSimilarSearch({
        similarBtn,
        similarPopover,
        similarFindBtn,
        similarDuplicatesBtn,
        similarSameNodeBtn,
        similarSameWorkflowBtn,
        gridContainer,
        state,
        panelLifecycleAC,
        isAiEnabled,
        similarDisabledTitle,
        readActiveAssetId,
        readSelectedAssetIds,
        readPanelValue,
        writePanelValue,
        scopeController,
        closePopovers: () => {
            popovers.close(customPopover);
            popovers.close(filterPopover);
            popovers.close(sortPopover);
            popovers.close(collectionsPopover);
            popovers.close(messagePopover);
            popovers.close(similarPopover);
        },
        closePeerPopovers: () => {
            popovers.close(customPopover);
            popovers.close(filterPopover);
            popovers.close(sortPopover);
            popovers.close(collectionsPopover);
            popovers.close(messagePopover);
            popovers.close(pinnedFoldersPopover);
        },
        popovers,
        workflowIdInput,
        reloadGrid: () => gridController.reloadGrid(),
        getDuplicatesAlert: () => _duplicatesAlert,
    });

    // -- 22. PINNED FOLDERS ------------------------------------------------
    bindPinnedFolders({
        pinnedFoldersBtn,
        pinnedFoldersController,
        pinnedFoldersMenu,
        pinnedFoldersPopover,
        popovers,
        closeOtherPopovers: () => {
            popovers.close(customPopover);
            popovers.close(filterPopover);
            popovers.close(sortPopover);
            popovers.close(collectionsPopover);
            popovers.close(messagePopover);
            popovers.close(similarPopover);
        },
        state,
        gridContainer,
        customSelect,
        scopeController,
        customRootsController,
        gridController,
        applyWatcherForScope,
        refreshDuplicateAlerts,
        notifyContextChanged,
        panelLifecycleAC,
    });

    // -- 23. FILTER INIT ---------------------------------------------------
    const { agendaCalendar, disposeFilters } = setupFiltersInit({
        state,
        hasVueHeaderSection,
        kindSelect,
        wfCheckbox,
        workflowTypeSelect,
        workflowIdInput,
        workflowModelInput,
        workflowRunsOnSelect,
        ratingSelect,
        minSizeInput,
        maxSizeInput,
        resolutionPresetSelect,
        minWidthInput,
        minHeightInput,
        maxWidthInput,
        maxHeightInput,
        dateRangeSelect,
        dateExactInput,
        agendaContainer,
        gridController,
        reconcileVisibleSelection,
        exitSimilarViewIfActive,
        notifyContextChanged,
        panelLifecycleAC,
        popovers,
        filterPopover,
    });

    // -- 24. CONTEXT CONTROLLER --------------------------------------------
    const extraActions = buildContextMenuExtraActions({
        state,
        scopeController,
        browserNav,
        gridController,
        getDuplicatesAlert: () => _duplicatesAlert,
        refreshDuplicateAlerts,
    });
    try {
        contextController = createContextController({
            state,
            gridContainer,
            filterBtn,
            sortBtn,
            collectionsBtn,
            searchInputEl,
            kindSelect,
            wfCheckbox,
            workflowTypeSelect,
            workflowIdInput,
            workflowModelInput,
            workflowRunsOnSelect,
            ratingSelect,
            minSizeInput,
            maxSizeInput,
            minWidthInput,
            minHeightInput,
            maxWidthInput,
            maxHeightInput,
            resolutionPresetSelect,
            dateRangeSelect,
            dateExactInput,
            scopeController,
            sortController,
            updateSummaryBar,
            reloadGrid: () => gridController.reloadGrid(),
            getExtraContext: () => ({ duplicatesAlert: _duplicatesAlert }),
            extraActions,
        });
    } catch (e) {
        console.debug?.(e);
    }

    // -- 25. SUMMARY BAR UPDATES -------------------------------------------
    try {
        notifyContextChanged();
        if (hasVueGridHostState) {
            const cleanup = external.bindGridHostState({
                onContextChanged: notifyContextChanged,
                markUserInteraction,
            });
            registerSummaryDispose(cleanup);
        } else {
            const onStats = () => notifyContextChanged();
            gridContainer.addEventListener("mjr:grid-stats", onStats, {
                signal: panelLifecycleAC?.signal,
            });
            window.addEventListener?.("mjr-settings-changed", onStats, {
                signal: panelLifecycleAC?.signal,
            });
            let raf: any = null;
            let debounceTimer: any = null;
            const schedule = () => {
                // Debounce MutationObserver callbacks during pagination bursts
                // (many cards inserted rapidly). Coalesce to a single update.
                if (debounceTimer) clearTimeout(debounceTimer);
                debounceTimer = setTimeout(() => {
                    debounceTimer = null;
                    if (raf) cancelAnimationFrame(raf);
                    raf = requestAnimationFrame(() => {
                        raf = null;
                        notifyContextChanged();
                    });
                }, 250);
            };
            const mo = new MutationObserver(() => schedule());
            mo.observe(gridContainer, { childList: true });
            gridContainer._mjrSummaryBarObserver = mo;
            registerSummaryDispose(() => {
                try {
                    gridContainer.removeEventListener("mjr:grid-stats", onStats);
                } catch (e) {
                    console.debug?.(e);
                }
                try {
                    window.removeEventListener?.("mjr-settings-changed", onStats);
                } catch (e) {
                    console.debug?.(e);
                }
                try {
                    if (debounceTimer) clearTimeout(debounceTimer);
                } catch (e) {
                    console.debug?.(e);
                }
                try {
                    mo.disconnect();
                } catch (e) {
                    console.debug?.(e);
                }
            });
        }
    } catch (e) {
        console.debug?.(e);
    }

    // -- 26. SIDEBAR CONTROLLER --------------------------------------------
    sidebarController = bindSidebarOpen({
        gridContainer,
        sidebar,
        createRatingBadge,
        createTagsBadge,
        showAssetInSidebar,
        closeSidebar,
        state,
    });

    // -- 27. ASSETS QUERY CONTROLLER ---------------------------------------
    const assetsQueryOptions = {
        gridController,
        captureAnchor: captureAnchorFn,
        restoreAnchor: restoreAnchorFn,
        restoreGridUiState: hasVueGridHostState
            ? (initialLoadPromise: any) =>
                  external.restoreGridUiState(initialLoadPromise, {
                      onRestoreSidebar: () => {
                          try {
                              sidebarController?.toggleDetails?.();
                          } catch (e) {
                              console.debug?.(e);
                          }
                      },
                  })
            : null,
        readScrollTop: () => panelStateBridge.read("scrollTop", (state as any).scrollTop || 0),
        restoreSelectionState: ({ scrollTop }: { scrollTop?: number } = {}) => {
            selectionState?.restoreSelectionState?.({ scrollTop });
        },
        readActiveAssetId,
        isSidebarOpen,
        toggleSidebarDetails: () => {
            sidebarController?.toggleDetails?.();
        },
        getQuery,
        getScope: () => panelStateBridge.read("scope", (state as any).scope),
        loadAssets: loadAssetsFn,
        lifecycleSignal: panelLifecycleAC?.signal || null,
    };
    if (typeof external?.initAssetsQueryController === "function") {
        assetsQueryController = external.initAssetsQueryController(assetsQueryOptions);
    } else {
        assetsQueryController = createAssetsQueryController({
            gridContainer,
            gridWrapper,
            ...assetsQueryOptions,
        });
    }

    // -- 28. HOTKEYS -------------------------------------------------------
    const hotkeys = createPanelHotkeysController({
        onTriggerScan: (ctx: any) => {
            triggerScan(statusDot, statusText, capabilitiesSection, ctx || {});
        },
        onToggleDetails: () => {
            sidebarController?.toggleDetails?.();
        },
        onToggleFloatingViewer: () => {
            try {
                window.dispatchEvent(new Event(EVENTS.MFV_TOGGLE));
            } catch (e) {
                console.debug?.(e);
            }
        },
        onFocusSearch: () => {
            try {
                searchInputEl?.focus?.();
                const len = searchInputEl?.value?.length || 0;
                searchInputEl?.setSelectionRange?.(len, len);
            } catch (e) {
                console.debug?.(e);
            }
        },
        onClearSearch: () => {
            try {
                if (!searchInputEl) return;
                writePanelValue("searchQuery", "");
                searchInputEl.value = "";
                searchInputEl.dispatchEvent?.(new Event("input", { bubbles: true }));
            } catch (e) {
                console.debug?.(e);
            }
        },
        getScanContext: () => ({
            scope: panelStateBridge.read("scope", state.scope),
            customRootId: panelStateBridge.read("customRootId", state.customRootId),
        }),
    });
    const ratingHotkeys = createRatingHotkeysController({ gridContainer, createRatingBadge });

    let disposeSearchBinding: any = null;
    if (container._eventCleanup) {
        try {
            container._eventCleanup();
        } catch (e) {
            console.debug?.(e);
        }
    }
    hotkeys.bind(content);
    ratingHotkeys.bind();
    setHotkeysScope("grid");

    try {
        container._mjrHotkeys = hotkeys;
        container._mjrRatingHotkeys = ratingHotkeys;
        container._mjrSidebarController = sidebarController;
    } catch (e) {
        console.debug?.(e);
    }

    // -- 29. CLEANUP CLOSURE -----------------------------------------------
    container._eventCleanup = () => {
        try {
            panelLifecycleAC?.abort?.();
        } catch (e) {
            console.debug?.(e);
        }
        try {
            statusSection?._mjrStatusPollDispose?.();
        } catch (e) {
            console.debug?.(e);
        }
        try {
            container._mjrVersionUpdateCleanup?.();
        } catch (e) {
            console.debug?.(e);
        }
        container._mjrVersionUpdateCleanup = null;
        try {
            if (_scrollTimer) clearTimeout(_scrollTimer);
            _scrollTimer = null;
        } catch (e) {
            console.debug?.(e);
        }
        try {
            hotkeys.dispose();
        } catch (e) {
            console.debug?.(e);
        }
        try {
            ratingHotkeys.dispose();
        } catch (e) {
            console.debug?.(e);
        }
        if (getHotkeysState().scope === "grid") setHotkeysScope(null);
        try {
            agendaCalendar?.dispose?.();
        } catch (e) {
            console.debug?.(e);
        }
        try {
            disposeFilters?.();
        } catch (e) {
            console.debug?.(e);
        }
        try {
            disposeSearchBinding?.();
        } catch (e) {
            console.debug?.(e);
        }
        disposeSearchBinding = null;
        try {
            sidebar?.dispose?.();
        } catch (e) {
            console.debug?.(e);
        }
        try {
            sidebarController?.dispose?.();
        } catch (e) {
            console.debug?.(e);
        }
        try {
            if (_dupPollTimer) clearInterval(_dupPollTimer);
            _dupPollTimer = null;
        } catch (e) {
            console.debug?.(e);
        }
        try {
            if (_autoLoadTimer) clearTimeout(_autoLoadTimer);
            _autoLoadTimer = null;
        } catch (e) {
            console.debug?.(e);
        }
        try {
            assetsQueryController?.dispose?.();
        } catch (e) {
            console.debug?.(e);
        }
        assetsQueryController = null;
        try {
            selectionState?.dispose?.();
        } catch (e) {
            console.debug?.(e);
        }
        selectionState = null;
        try {
            gridContainer?._mjrSummaryBarDispose?.();
        } catch (e) {
            console.debug?.(e);
        }
        try {
            gridContainer?._mjrSummaryBarObserver?.disconnect?.();
        } catch (e) {
            console.debug?.(e);
        }
        try {
            gridContainer?._mjrGridContextMenuUnbind?.();
        } catch (e) {
            console.debug?.(e);
        }
        try {
            disposeCustomRootsBindings?.();
        } catch (e) {
            console.debug?.(e);
        }
        try {
            _unbindBrowserFolderNav?.();
        } catch (e) {
            console.debug?.(e);
        }
        try {
            disposeGridScanListeners();
        } catch (e) {
            console.debug?.(e);
        }
        try {
            if (gridContainer) disposeGridFn(gridContainer);
        } catch (e) {
            console.debug?.(e);
        }
        clearActiveGridContainer(gridContainer);
        gridContainer = null;
        try {
            popovers.dispose();
        } catch (e) {
            console.debug?.(e);
        }
        try {
            if (container._mjrPopoverManager === popovers) container._mjrPopoverManager = null;
        } catch (e) {
            console.debug?.(e);
        }
        try {
            if (hostWrapper && hostWrapperPrevStyle) {
                hostWrapper.style.height = hostWrapperPrevStyle.height;
                hostWrapper.style.minHeight = hostWrapperPrevStyle.minHeight;
                hostWrapper.style.width = hostWrapperPrevStyle.width;
            }
        } catch (e) {
            console.debug?.(e);
        }
    };

    // -- 30. INITIAL LOAD SEQUENCE -----------------------------------------
    const queuedReload = async () => {
        await assetsQueryController?.queuedReload?.();
    };
    const handleCountersUpdate =
        assetsQueryController?.createCountersUpdateHandler?.({
            state,
            getStableQuery: () => String(gridContainer?.dataset?.mjrQuery || "*").trim() || "*",
            getRecentUserInteractionAt: () => Number(_lastUserInteractionAt || 0),
        }) ??
        (async () => {
            await queuedReload();
        });

    setupStatusPolling(
        statusDot,
        statusText,
        statusSection,
        capabilitiesSection,
        handleCountersUpdate,
        () => ({
            scope: panelStateBridge.read("scope", state.scope),
            customRootId: panelStateBridge.read("customRootId", state.customRootId),
        }),
    );

    scopeController.setActiveTabStyles();
    browserNav?.renderBreadcrumb?.();
    customRootsController.refreshCustomRoots().catch(() => {});

    disposeSearchBinding = assetsQueryController?.bindSearchInput?.({
        searchInputEl,
        lifecycleSignal: panelLifecycleAC?.signal || null,
        debounceMs: 120,
        onQueryChanged: () => {
            void exitSimilarViewIfActive({ reload: false });
            notifyContextChanged();
        },
        onBeforeReload: () => {
            void exitSimilarViewIfActive({ reload: false });
            notifyContextChanged();
        },
        startSearchTimer: () => {
            startTimer("searchQuery");
        },
        reloadGrid: () => {
            gridController.reloadGrid();
        },
    });

    let didHydrateFromSnapshot = false;
    try {
        didHydrateFromSnapshot = await hydrateGridFromSnapshotFn(
            gridContainer,
            {
                scope: readPanelValue("scope", state.scope || "output") || "output",
                query: String(
                    normalizeQuery(
                        panelStateBridge.read("searchQuery", state.searchQuery || "*") || "*",
                    ) || "*",
                ),
                customRootId: readPanelValue("customRootId", state.customRootId || "") || "",
                subfolder:
                    readPanelValue(
                        "currentFolderRelativePath",
                        state.currentFolderRelativePath || "",
                    ) || "",
                collectionId: readPanelValue("collectionId", state.collectionId || "") || "",
                viewScope: readPanelValue("viewScope", state.viewScope || "") || "",
                kind: readPanelValue("kindFilter", state.kindFilter || "") || "",
                workflowOnly: !!readPanelValue("workflowOnly", state.workflowOnly),
                minRating: readPanelValue("minRating", state.minRating || "") || "",
                minSizeMB: readPanelValue("minSizeMB", state.minSizeMB || "") || "",
                maxSizeMB: readPanelValue("maxSizeMB", state.maxSizeMB || "") || "",
                resolutionCompare:
                    readPanelValue("resolutionCompare", state.resolutionCompare || "") || "",
                minWidth: readPanelValue("minWidth", state.minWidth || "") || "",
                minHeight: readPanelValue("minHeight", state.minHeight || "") || "",
                maxWidth: readPanelValue("maxWidth", state.maxWidth || "") || "",
                maxHeight: readPanelValue("maxHeight", state.maxHeight || "") || "",
                workflowType: readPanelValue("workflowType", state.workflowType || "") || "",
                workflowId: readPanelValue("workflowId", state.workflowId || "") || "",
                workflowModel: readPanelValue(
                    "workflowModelFilter",
                    state.workflowModelFilter || "",
                ) || "",
                workflowRunsOn: readPanelValue(
                    "workflowRunsOnFilter",
                    state.workflowRunsOnFilter || "",
                ) || "",
                dateRange: readPanelValue("dateRangeFilter", state.dateRangeFilter || "") || "",
                dateExact: readPanelValue("dateExactFilter", state.dateExactFilter || "") || "",
                sort: readPanelValue("sort", state.sort || "mtime_desc") || "mtime_desc",
            },
            {
                title:
                    readPanelValue("collectionName", state.collectionName || "") ||
                    panelStateBridge.read("searchQuery", state.searchQuery || "") ||
                    "Cached",
            },
        );
    } catch (e) {
        console.debug?.(e);
    }

    let hasExistingGridCards = false;
    try {
        hasExistingGridCards = !!gridContainer?.querySelector?.(".mjr-asset-card");
    } catch (e) {
        console.debug?.(e);
    }
    const pendingGeneratedAssetCount = consumePendingGeneratedAssets();

    const initialLoadPromise =
        !pendingGeneratedAssetCount && (didHydrateFromSnapshot || hasExistingGridCards)
            ? Promise.resolve({
                  ok: true,
                  hydrated: !!didHydrateFromSnapshot,
                  cached: !!hasExistingGridCards,
              })
            : gridController.reloadGrid({
                  reason: pendingGeneratedAssetCount
                      ? "pending-generated-assets"
                      : "initial-load",
                  preserveVisibleUntilReady: !pendingGeneratedAssetCount,
              });

    // When we showed cached snapshot data on first launch, schedule a silent
    // background refresh so the user always sees an up-to-date grid without
    // waiting for the API on cold open. The refresh reuses preserveVisibleUntilReady
    // so the cached cards remain visible until the fresh page arrives.
    if (didHydrateFromSnapshot) {
        try {
            setTimeout(() => {
                try {
                    if (panelLifecycleAC?.signal?.aborted) return;
                } catch (e) {
                    console.debug?.(e);
                }
                gridController.reloadGrid().catch(() => {});
            }, 200);
        } catch (e) {
            console.debug?.(e);
        }
    }

    // Fire-and-forget: do not block the panel mount on the duplicate alerts
    // request. It contended with the initial /list call on the single-threaded
    // backend and added perceptible latency before the first paint.
    refreshDuplicateAlerts().catch(() => {});
    try {
        _dupPollTimer = setInterval(() => {
            try {
                if (panelLifecycleAC?.signal?.aborted) return;
            } catch (e) {
                console.debug?.(e);
            }
            refreshDuplicateAlerts().catch(() => {});
        }, 15000);
    } catch (e) {
        console.debug?.(e);
    }

    assetsQueryController?.restoreUiState?.(initialLoadPromise);

    try {
        (window as any).__mjrLastAssetUpsert = 0;
        (window as any).__mjrLastAssetUpsertCount = 0;
    } catch (e) {
        console.debug?.(e);
    }

    const renderDuration = endTimer("panelRender", "gridRender");
    trackGridRender(renderDuration ?? 0);

    return { gridContainer };
}
