import { nextTick } from "vue";
import { get } from "../../api/client.js";
import { EVENTS } from "../../app/events.js";
import { buildListURL } from "../../api/endpoints.js";
import { APP_CONFIG } from "../../app/config.js";
import { loadMajoorSettings } from "../../app/settings.js";
import { setFileBadgeCollision } from "../../components/Badges.js";
import { pickRootId } from "../../utils/ids.js";
import { isGridHostVisible } from "./gridVisibility.js";
import { consumeEarlyFetch, peekEarlyFetchKey } from "../../features/runtime/earlyFetch.js";
import { mjrDbg } from "../../utils/logging.js";
import {
    appendAssets as cardAppendAssets,
    rebuildAssetRendererState,
} from "../../features/grid/AssetCardRenderer.js";
import { getStackAwareAssetKey, ensureDupStackCard, disposeStackGroupCards } from "../../features/grid/StackGroupCards.js";
import {
    gridListQueryKey,
    isDefaultOutputBrowseQuery,
    readGridQueryFromDataset,
    readGridQueryText,
    readGridSortKey,
} from "../grid/useGridQuery.js";
import {
    buildGridSnapshotKey,
    compactSnapshotAsset,
    flushGridSnapshotsPersist,
    getGridSnapshot,
    hasGridSnapshot,
    rememberGridSnapshot,
} from "../grid/useGridSnapshotCache.js";
import { removeAssetsFromState, syncAssetCollectionState } from "../grid/useAssetCollection.js";
import { usePagedAssets } from "../grid/usePagedAssets.js";
import {
    compareAssets,
    fetchPage as fetchGridPage,
    flushUpsertBatch,
    getUpsertBatchState,
    upsertAsset as queueUpsertAsset,
} from "./useVirtualGrid.js";
import {
    findAssetCardById,
    findSelectedAssetCard,
    readGridShownCount,
} from "../components/grid/gridDomBridge.js";

const UPSERT_BATCH_DEBOUNCE_MS = 200;
const UPSERT_BATCH_MAX_SIZE = 50;
const UPSERT_BATCH_STATE = new WeakMap();
const IMMEDIATE_RESET_REASONS = new Set([
    "collection",
    "filter",
    "initial",
    "scope",
    "search",
    "sort",
]);
// 0 = no artificial skeleton hold. The skeleton naturally lasts the duration
// of the network/render work; an additional minimum delay only made the first
// paint feel sluggish on cold opens. Keep at 0 unless flicker is reintroduced.
const MIN_LOADING_SKELETON_MS = 0;
function waitMs(ms: any) {
    return new Promise((resolve) => setTimeout(resolve, Math.max(0, Number(ms) || 0)));
}

function getStableRealtimeAssetKey(asset: any) {
    if (!asset || typeof asset !== "object") return "";
    if (asset.id != null && String(asset.id).trim()) {
        return `id:${String(asset.id).trim()}`;
    }
    const type = String(asset?.type || asset?.source || "output")
        .trim()
        .toLowerCase();
    const rootId = String(pickRootId(asset) || "")
        .trim()
        .toLowerCase();
    const subfolder = String(asset?.subfolder || "")
        .trim()
        .toLowerCase();
    const filename = String(asset?.filename || "")
        .trim()
        .toLowerCase();
    if (filename) return `${type}|${rootId}|${subfolder}|${filename}`;
    const filepath = String(
        asset?.filepath || asset?.path || asset?.fullpath || asset?.full_path || "",
    )
        .trim()
        .toLowerCase();
    return filepath ? `${type}|${rootId}|path|${filepath}` : "";
}

function sanitizeQuery(query: any) {
    if (query == null) return query;

    const raw = String(query);
    if (raw.trim() === "") return raw;
    if (raw.trim() === "*") return "*";

    const cleaned = raw
        .replace(/[\u0000-\u001f\u007f]+/g, " ")
        .replace(/[<>]/g, " ")
        .replace(/\s+/g, " ")
        .trim();

    return cleaned || raw;
}

function coerceQueryText(query: any) {
    if (typeof query === "string") return query;
    if (query && typeof query === "object") {
        if (typeof query.value === "string") return query.value;
        if (typeof query.target?.value === "string") return query.target.value;
        try {
            if (typeof query.querySelector === "function") {
                const nested =
                    query.querySelector("#mjr-search-input") ||
                    query.querySelector("input.mjr-input") ||
                    query.querySelector("input[type='text']") ||
                    query.querySelector("textarea");
                if (nested && typeof nested.value === "string") return nested.value;
            }
        } catch (e) {
            console.debug?.(e);
        }
    }
    return String(query || "");
}

function resolveElement(maybeRef: any) {
    if (!maybeRef) return null;
    if (typeof maybeRef === "object" && "value" in maybeRef) {
        return maybeRef.value || null;
    }
    return maybeRef;
}

async function waitForLayout() {
    // PERF FIX #3: single nextTick instead of nextTick + double rAF.
    // The double rAF was costing ~33ms per call for negligible benefit;
    // the virtualizer + ResizeObserver settle on their own without it.
    await nextTick();
}

function formatLoadErrorMessage(prefix: any, err: any) {
    const message = String(prefix || "Failed to load");
    try {
        const raw = String(err?.message || err || "").trim();
        const low = raw.toLowerCase();
        if (!raw) return message;
        if (
            low.includes("database is locked") ||
            low.includes("database table is locked") ||
            low.includes("database schema is locked")
        ) {
            return `${message}: the database is temporarily locked. Please retry in a few seconds.`;
        }
        return `${message}: ${raw}`;
    } catch {
        return message;
    }
}

function getAdaptivePageLimit(baseLimit: any, emptyAppendBatches: any) {
    const safeBaseLimit = Math.max(1, Math.min(APP_CONFIG.MAX_PAGE_SIZE, Number(baseLimit) || 1));
    const growthStep = Math.max(0, Number(emptyAppendBatches) || 0);
    if (growthStep <= 0) return safeBaseLimit;
    const multiplier = 2 ** Math.min(growthStep, 5);
    return Math.max(1, Math.min(APP_CONFIG.MAX_PAGE_SIZE, safeBaseLimit * multiplier));
}

function isConstrainedBrowserRuntime() {
    try {
        const nav: any = typeof navigator !== "undefined" ? navigator : null;
        const deviceMemory = Number(nav?.deviceMemory || 0) || 0;
        if (deviceMemory > 0 && deviceMemory <= 4) return true;
        if (nav?.connection?.saveData === true) return true;
    } catch (e) {
        console.debug?.(e);
    }
    return false;
}

function shouldPrefetchNextPage() {
    if (!APP_CONFIG.PREFETCH_NEXT_PAGE) return false;
    return !isConstrainedBrowserRuntime();
}

function resetAssetCollectionsState(state: any, options: Record<string, any> = {}) {
    const assets = Array.isArray(state.assets) ? state.assets : [];
    if (options.rebuildFromVisible && assets.length) {
        rebuildAssetRendererState(state, assets, {
            assetKey: options.assetKey || state.assetKeyFn,
            preserveHiddenCount: !!options.preserveHiddenCount,
        });
        return;
    }
    rebuildAssetRendererState(state, [], {
        assetKey: options.assetKey || state.assetKeyFn,
        preserveHiddenCount: false,
    });
}

export function useGridLoader({
    gridContainerRef,
    state,
    setLoadingMessage,
    clearLoadingMessage,
    setStatusMessage,
    clearStatusMessage,
    resetAssets,
    setSelection,
    reconcileSelection,
    readScrollElement = () => null,
    readRenderedCards = () => [],
    scrollToAssetId = () => {},
    canLoadMore = null,
}: Record<string, any> = {}) {
    let deferVisualResetUntilNextPage = false;
    let deferredExecutionReload: any = null;
    let prefetchTimer: any = null;
    const metrics = {
        pagesRequested: 0,
        assetsReceived: 0,
        visibleAssetsAdded: 0,
        hiddenOrDedupedAssets: 0,
        apiTimeMs: 0,
        renderTimeMs: 0,
        resetCount: 0,
        blockedImmediateResetCount: 0,
        scrollRestoreCount: 0,
        lastReloadReason: "",
        lastResetReason: "",
        lastAppendReason: "",
        lastOperation: "",
    };

    function nowMs() {
        try {
            return typeof performance !== "undefined" && typeof performance.now === "function"
                ? performance.now()
                : Date.now();
        } catch {
            return Date.now();
        }
    }

    function recordOperation(name: any, detail: Record<string, any> = {}) {
        metrics.lastOperation = String(name || "");
        if (detail.reloadReason) metrics.lastReloadReason = String(detail.reloadReason || "");
        if (detail.appendReason) metrics.lastAppendReason = String(detail.appendReason || "");
    }

    function getGridContainer() {
        return resolveElement(gridContainerRef);
    }

    function getContextState() {
        return buildCurrentSnapshotParts();
    }

    function getCanonicalState() {
        const scrollElement = readScrollElement();
        return {
            assets: Array.isArray(state.assets) ? state.assets : [],
            pagination: {
                query: String(state.query || "*"),
                offset: Number(state.offset || 0) || 0,
                total: state.total == null ? null : Number(state.total) || 0,
                loading: !!state.loading,
                done: !!state.done,
                requestId: Number(state.requestId || 0) || 0,
            },
            selection: {
                selectedIds: Array.isArray(state.selectedIds)
                    ? state.selectedIds.map(String).filter(Boolean)
                    : [],
                activeId: String(state.activeId || ""),
                anchorId: String(state.selectionAnchorId || ""),
            },
            viewport: {
                scrollTop: Number(scrollElement?.scrollTop || 0) || 0,
                clientHeight: Number(scrollElement?.clientHeight || 0) || 0,
                scrollHeight: Number(scrollElement?.scrollHeight || 0) || 0,
            },
            context: getContextState(),
        };
    }

    function getDebugSnapshot() {
        const canonical = getCanonicalState();
        return {
            ...canonical,
            counts: {
                loaded: canonical.assets.length,
                visible: readGridShownCount(getGridContainer(), canonical.assets.length),
                total: canonical.pagination.total,
            },
            metrics: { ...metrics },
        };
    }

    function buildCurrentSnapshotParts() {
        const gridContainer = getGridContainer();
        return readGridQueryFromDataset(gridContainer?.dataset || {}, {
            q: readGridQueryText(gridContainer, state.query || "*"),
            resolutionCompare: gridContainer?.dataset?.mjrFilterResolutionCompare || "",
        });
    }

    function getCurrentResetContext(query = state.query || "*") {
        return {
            ...buildCurrentSnapshotParts(),
            query: String(query || "*"),
        };
    }

    function classifyContextResetReason(nextContext: any) {
        const previous = state._mjrLastGridContext || null;
        if (!previous) {
            return Array.isArray(state.assets) && state.assets.length ? "unknown" : "initial";
        }
        if (String(previous.collectionId || "") !== String(nextContext.collectionId || "")) {
            return "collection";
        }
        if (
            String(previous.scope || "") !== String(nextContext.scope || "") ||
            String(previous.customRootId || "") !== String(nextContext.customRootId || "") ||
            String(previous.subfolder || "") !== String(nextContext.subfolder || "") ||
            String(previous.viewScope || "") !== String(nextContext.viewScope || "")
        ) {
            return "scope";
        }
        if (String(previous.sort || "") !== String(nextContext.sort || "")) {
            return "sort";
        }
        const filterKeys = [
            "kind",
            "workflowOnly",
            "minRating",
            "minSizeMB",
            "maxSizeMB",
            "resolutionCompare",
            "minWidth",
            "minHeight",
            "maxWidth",
            "maxHeight",
            "workflowType",
            "workflowId",
            "dateRange",
            "dateExact",
            "semanticMode",
        ];
        if (filterKeys.some((key) => String(previous[key] || "") !== String(nextContext[key] || ""))) {
            return "filter";
        }
        if (String(previous.query || "*") !== String(nextContext.query || "*")) {
            return "search";
        }
        return "refresh";
    }

    function resolveResetReason(query: any, options: Record<string, any> = {}) {
        const explicit = String(options?.resetReason || options?.reason || "").trim().toLowerCase();
        const nextContext = getCurrentResetContext(query);
        const reason = explicit || classifyContextResetReason(nextContext);
        return { reason, nextContext };
    }

    function canResetImmediately(reason: any) {
        return IMMEDIATE_RESET_REASONS.has(String(reason || "").toLowerCase());
    }

    function rememberCurrentGridContext(context: any = null) {
        try {
            state._mjrLastGridContext = context || getCurrentResetContext(state.query || "*");
        } catch (e) {
            console.debug?.(e);
        }
    }

    function shouldPreserveVisibleOnReset({ reset = true, preserveVisibleUntilReady = true } = {}) {
        if (!reset || preserveVisibleUntilReady === false) return false;
        return Array.isArray(state.assets) && state.assets.length > 0;
    }

    function canLoadFromHost() {
        try {
            if (typeof canLoadMore === "function") {
                return !!canLoadMore();
            }
        } catch (e) {
            console.debug?.(e);
        }
        return isGridHostVisible(getGridContainer(), readScrollElement());
    }

    function isExecutionBusy() {
        if (!APP_CONFIG.DEFER_GRID_FETCH_DURING_EXECUTION) {
            return false;
        }
        // PERF FIX #2: never defer the very first load (empty grid).
        // The user expects assets to appear immediately when the panel opens,
        // even if a generation is already in progress.
        try {
            if (!Array.isArray(state.assets) || state.assets.length === 0) {
                return false;
            }
        } catch (e) {
            console.debug?.(e);
        }
        try {
            return !!String((window as any).__MJR_EXECUTION_RUNTIME__?.active_prompt_id || "").trim();
        } catch (e) {
            console.debug?.(e);
            return false;
        }
    }

    function clearDeferredExecutionReload() {
        try {
            if (deferredExecutionReload?.timer) {
                clearTimeout(deferredExecutionReload.timer);
            }
        } catch (e) {
            console.debug?.(e);
        }
        try {
            if (deferredExecutionReload?.listener) {
                window.removeEventListener(EVENTS.RUNTIME_STATUS, deferredExecutionReload.listener);
            }
        } catch (e) {
            console.debug?.(e);
        }
        deferredExecutionReload = null;
    }

    function clearPrefetchTimer() {
        try {
            if (prefetchTimer) {
                clearTimeout(prefetchTimer);
            }
        } catch (e) {
            console.debug?.(e);
        }
        prefetchTimer = null;
    }

    function scheduleNextPagePrefetch(requestId: any, delayMs = APP_CONFIG.PREFETCH_NEXT_PAGE_DELAY_MS) {
        if (!shouldPrefetchNextPage()) {
            return;
        }
        clearPrefetchTimer();
        const resolvedDelayMs = Math.max(0, Number(delayMs) || 0);
        prefetchTimer = setTimeout(() => {
            prefetchTimer = null;
            if (state.requestId !== requestId || state.done || state.loading) {
                return;
            }
            loadNextPage().catch((error) => {
                console.debug?.("[AssetsManager][GridLoader] Delayed prefetch failed", error);
            });
        }, resolvedDelayMs);
    }

    function scheduleDeferredExecutionReload(query: any, options = {}) {
        clearDeferredExecutionReload();
        // Capture the snapshot context at schedule time so we can detect a
        // user-initiated scope/sort/filter switch during execution. If the
        // user navigates away (e.g. starts a generation on Output, switches
        // to Custom, generation finishes) the deferred reload must NOT fire
        //  -  otherwise it would clobber the new context's grid state.
        const scheduledScopeKey = (() => {
            try {
                // Ensure we capture to correct snapshot parts even during rapid clicks.
                return buildGridSnapshotKey(buildCurrentSnapshotParts());
            } catch (e) {
                console.debug?.(e);
                return null;
            }
        })();
        const retry = () => {
            if (isExecutionBusy()) {
                try {
                    deferredExecutionReload = {
                        ...deferredExecutionReload,
                        timer: setTimeout(retry, 1250),
                    };
                } catch (e) {
                    console.debug?.(e);
                }
                return;
            }
            clearDeferredExecutionReload();
            // Re-read the current query from the grid container at retry time
            // instead of using the stale captured value. The user may have
            // changed the search query while execution was in progress.
            const gridContainer = getGridContainer();
            // Bail if the grid context (scope/sort/filters) changed during
            // execution  -  the user is now looking at a different view and a
            // reload would corrupt it.
            if (scheduledScopeKey) {
                let currentKey: any = null;
                try {
                    currentKey = buildGridSnapshotKey(buildCurrentSnapshotParts());
                } catch (e) {
                    console.debug?.(e);
                }
                if (currentKey && currentKey !== scheduledScopeKey) {
                    mjrDbg(
                        "[Grid] Deferred execution reload skipped  -  scope/filter changed during execution",
                    );
                    return;
                }
            }
            const freshQuery = readGridQueryText(gridContainer, query);
            Promise.resolve()
                .then(() =>
                    loadAssets(freshQuery, {
                        ...(options || {}),
                        reset: true,
                        preserveVisibleUntilReady: true,
                    }),
                )
                .catch((e) => console.debug?.(e));
        };
        const listener = (event: any) => {
            const activePromptId = String(event?.detail?.active_prompt_id || "").trim();
            if (!activePromptId) {
                retry();
            }
        };
        deferredExecutionReload = {
            listener,
            timer: setTimeout(retry, 1250),
        };
        try {
            window.addEventListener(EVENTS.RUNTIME_STATUS, listener);
        } catch (e) {
            console.debug?.(e);
        }
    }

    function assetKey(asset: any, gridContainer = null) {
        if (!asset || typeof asset !== "object") return "";
        const fallback =
            asset.id != null
                ? `id:${asset.id}`
                : `${asset.type || ""}|${pickRootId(asset)}|${asset.filepath || ""}|${asset.subfolder || ""}|${asset.filename || ""}`;
        try {
            const grid = gridContainer || getGridContainer() || (globalThis as any)?.__MJR_LAST_ASSETKEY_GRID__;
            return getStackAwareAssetKey(grid, asset, fallback);
        } catch (e) {
            console.debug?.(e);
            return fallback;
        }
    }

    function canUseSnapshotCache() {
        try {
            const settings = loadMajoorSettings?.() || {};
            const grid = (settings as Record<string, any>).grid || {};
            return !!(
                grid.snapshotCacheEnabled === true ||
                grid.enableSnapshotCache === true ||
                (settings as Record<string, any>).snapshotCacheEnabled === true ||
                (settings as Record<string, any>).enableSnapshotCache === true
            );
        } catch (e) {
            console.debug?.(e);
            return false;
        }
    }

    function rememberSnapshot(title = "") {
        try {
            if (!canUseSnapshotCache()) return;
            const gridContainer = getGridContainer();
            const assets = Array.isArray(state.assets) ? state.assets : [];
            if (!gridContainer || !assets.length) return;
            const snapshotAssets = assets
                .map(compactSnapshotAsset)
                .filter(Boolean);
            if (!snapshotAssets.length) return;
            const wasTruncated = snapshotAssets.length < assets.length;
            const rawOffset = Number(state.offset || snapshotAssets.length) || snapshotAssets.length;
            const rawTotal = Number(state.total ?? snapshotAssets.length);
            const snapshotParts = readGridQueryFromDataset(gridContainer.dataset || {}, {
                q: readGridQueryText(gridContainer, state.query || "*"),
                resolutionCompare: gridContainer.dataset?.mjrFilterResolutionCompare || "",
            });
            const key = buildGridSnapshotKey(snapshotParts);
            if (!key) return;
            rememberGridSnapshot(key, {
                assets: snapshotAssets,
                title: String(title || "").trim(),
                query: String(state.query || snapshotParts.query || "*").trim() || "*",
                total: Number.isFinite(rawTotal) ? rawTotal : snapshotAssets.length,
                offset: wasTruncated
                    ? snapshotAssets.length
                    : Math.max(snapshotAssets.length, rawOffset),
                done: wasTruncated ? false : !!state.done,
            });
        } catch (e) {
            console.debug?.(e);
        }
    }

    function clearPendingUpserts() {
        const gridContainer = getGridContainer();
        if (!gridContainer) return;
        try {
            const batchState = UPSERT_BATCH_STATE.get(gridContainer);
            if (!batchState) return;
            if (batchState.timer) {
                clearTimeout(batchState.timer);
                batchState.timer = null;
            }
            batchState.pending.clear();
            batchState.flushing = false;
        } catch (e) {
            console.debug?.(e);
        }
    }

    function appendAssets(gridContainer: any, assets: any) {
        const startedAt = nowMs();
        const receivedCount = Array.isArray(assets) ? assets.length : 0;
        const addedCount: any = cardAppendAssets(gridContainer, assets, state, {
            loadMajoorSettings,
            clearGridMessage: () => {
                clearStatusMessage();
            },
            ensureVirtualGrid: () => state.virtualGrid,
            setFileBadgeCollision,
            assetKey: (asset: any) => assetKey(asset, gridContainer),
            ensureDupStackCard: (gc: any, card: any, asset: any) => ensureDupStackCard(gc, card, asset),
        });
        metrics.renderTimeMs += Math.max(0, nowMs() - startedAt);
        metrics.assetsReceived += receivedCount;
        metrics.visibleAssetsAdded += Number(addedCount || 0) || 0;
        metrics.hiddenOrDedupedAssets += Math.max(0, receivedCount - (Number(addedCount || 0) || 0));
        return addedCount;
    }

    /**
     * Build a key fingerprint that matches `_buildEarlyFetchKey` in
     * features/runtime/earlyFetch.js. The two MUST stay in sync  -  when they
     * diverge the grid silently falls back to a second /list round-trip,
     * which is precisely what the early fetch was meant to avoid.
     */
    function buildEarlyFetchMatchKey(gridContainer: any, query: any) {
        const normalizedQuery = String(query || "*").trim() || "*";
        return gridListQueryKey(readGridQueryFromDataset(gridContainer?.dataset || {}, {
            q: normalizedQuery,
        }));
    }

    function isDefaultOutputBrowseContext(query: any) {
        const context = buildCurrentSnapshotParts();
        const safeQuery = sanitizeQuery(coerceQueryText(query).trim() || "*") || "*";
        return isDefaultOutputBrowseQuery({ ...context, q: safeQuery });
    }

    function normalizePageForPagination(page: any, { query = "*", limit = 0, offset = 0 } = {}) {
        if (!page?.ok || page.total == null || !isDefaultOutputBrowseContext(query)) {
            return page;
        }
        const total = Number(page.total);
        const count = Number(page.count ?? page.assets?.length ?? 0) || 0;
        const requestedLimit = Math.max(1, Number(limit) || 1);
        const currentOffset = Math.max(0, Number(offset) || 0);
        const pageEnd = currentOffset + count;
        const totalLooksLikeReturnedWindow =
            Number.isFinite(total) &&
            total > 0 &&
            total <= pageEnd &&
            count > 0 &&
            count < requestedLimit;
        if (!totalLooksLikeReturnedWindow) return page;
        return { ...page, total: null };
    }

    async function fetchPage(query: any, limit: any, offset: any, { requestId = 0, signal = null as AbortSignal | null } = {}) {
        const gridContainer = getGridContainer();
        if (!gridContainer) {
            return { ok: false, error: "Grid unavailable" };
        }

        // Try to use early-fetched data for the first page of default browse context.
        // This significantly reduces perceived load time when opening the panel.
        if (offset === 0) {
            // Match against the FULL filter fingerprint, not just scope+sort.
            // The early-fetch URL is built with the persisted filters too, so
            // a workflowOnly/minRating/etc match here means the prefetched
            // payload is the right one and we skip a redundant /list call.
            const matchKey = buildEarlyFetchMatchKey(gridContainer, query);
            const earlyKey = peekEarlyFetchKey();
            if (earlyKey && earlyKey === matchKey) {
                const earlyFetchPromise = consumeEarlyFetch(matchKey);
                if (earlyFetchPromise) {
                    try {
                        const earlyResult = await earlyFetchPromise;
                        // Discard early fetch if the request has been superseded
                        // (e.g. scope switch while the promise was pending).
                        if (Number(state.requestId) !== Number(requestId)) {
                            return { ok: false, stale: true, error: "Stale early fetch" };
                        }
                        if (signal?.aborted) {
                            return { ok: false, aborted: true, error: "Aborted" };
                        }
                        // API returns { ok, data: { assets: [...], total, ... } }
                        const earlyAssets = earlyResult?.data?.assets;
                        if (earlyResult?.ok && Array.isArray(earlyAssets)) {
                            mjrDbg("[Grid] Using early-fetched data:", earlyAssets.length, "assets");
                            // Use null when the backend did not return a total (includeTotal=false).
                            // Falling back to earlyAssets.length would incorrectly set state.total
                            // to the page size (e.g. 80), causing state.done=true after the first
                            // page and blocking all subsequent pagination / infinite scroll.
                            const rawTotal = earlyResult.data?.total ?? earlyResult.meta?.total ?? null;
                            return normalizePageForPagination({
                                ok: true,
                                assets: earlyAssets,
                                total: rawTotal != null ? Number(rawTotal) || 0 : null,
                                count: earlyAssets.length,
                                limit: limit,
                                offset: 0,
                                nextCursor: earlyResult.data?.next_cursor || earlyResult.data?.nextCursor || null,
                            }, { query, limit, offset });
                        }
                    } catch (e) {
                        mjrDbg("[Grid] Early fetch failed, falling back to normal fetch", e);
                    }
                }
            }
        }

        const page = await fetchGridPage(
            gridContainer,
            query,
            limit,
            offset,
            {
                sanitizeQuery,
                buildListURL,
                get,
                getGridState: () => state,
            },
            { requestId, signal: (signal ?? undefined) as undefined, cursor: state.cursor || null },
        );
        return normalizePageForPagination(page, { query, limit, offset });
    }

    const pagedAssets = usePagedAssets({
        query: {
            get value() {
                return state.query || "*";
            },
            set value(value) {
                state.query = String(value || "*") || "*";
            },
        },
        collection: {
            append(assets = []) {
                const gridContainer = getGridContainer();
                return gridContainer && assets.length
                    ? Number((appendAssets(gridContainer, assets) as any) || 0) || 0
                    : 0;
            },
            reset() {},
        },
        fetchPage: async ({ query, limit, offset, requestId }: { query: any; limit: any; offset: any; requestId: any }) => {
            const pageStartedAt = nowMs();
            metrics.pagesRequested += 1;
            const page = await fetchPage(query, limit, offset, {
                requestId,
                signal: state.abortController?.signal || null,
            });
            metrics.apiTimeMs += Math.max(0, nowMs() - pageStartedAt);
            return page;
        },
        pageSize: APP_CONFIG.DEFAULT_PAGE_SIZE,
    });

    function syncPagedStateFromLegacy() {
        pagedAssets.setPageState({
            offset: state.offset,
            cursor: state.cursor,
            total: state.total,
            done: state.done,
            requestId: state.requestId,
            loading: false,
            error: null,
        });
    }

    function setLegacyPageState(nextState: Record<string, any> = {}, { syncPaged = true }: Record<string, any> = {}) {
        if (!nextState || typeof nextState !== "object") return;
        if (Object.prototype.hasOwnProperty.call(nextState, "offset")) {
            state.offset = Math.max(0, Number(nextState.offset || 0) || 0);
        }
        if (Object.prototype.hasOwnProperty.call(nextState, "cursor")) {
            state.cursor = nextState.cursor || null;
        }
        if (Object.prototype.hasOwnProperty.call(nextState, "total")) {
            state.total = nextState.total == null ? null : Math.max(0, Number(nextState.total) || 0);
        }
        if (Object.prototype.hasOwnProperty.call(nextState, "done")) {
            state.done = !!nextState.done;
        }
        if (syncPaged) syncPagedStateFromLegacy();
    }

    function syncLegacyStateFromPaged() {
        const pageState = pagedAssets.getPageState();
        setLegacyPageState(pageState, { syncPaged: false });
    }

    function repairOffsetFromLoadedAssets() {
        const loadedCount = Array.isArray(state.assets) ? state.assets.length : 0;
        if (loadedCount <= 0 || state.done) return;
        if (Number(state.offset || 0) >= loadedCount) return;
        setLegacyPageState({ offset: loadedCount }, { syncPaged: true });
    }

    function finalizeLoad({ title = "", showEmptyMessage = true }: Record<string, any> = {}) {
        clearLoadingMessage();
        if (!(Array.isArray(state.assets) && state.assets.length)) {
            if (showEmptyMessage && (!state.statusMessage || !state.statusError)) {
                setStatusMessage("No assets found");
            }
        } else {
            clearStatusMessage();
            rememberSnapshot(title || state.query);
        }
        const visibleIds = (Array.isArray(state.assets) ? state.assets : [])
            .map((asset: any) => String(asset?.id || ""))
            .filter(Boolean);
        const resultIsComplete =
            !!state.done ||
            (Number.isFinite(Number(state.total)) &&
                Number(state.total || 0) <= visibleIds.length);
        if (resultIsComplete) {
            reconcileSelection(visibleIds, { activeId: state.activeId });
        }
    }

    // Append invariant: pagination may advance offset and append/patch assets,
    // but it must not visually reset the grid, move scroll, or prune selection.
    async function loadNextPage() {
        const gridContainer = getGridContainer();
        if (!gridContainer || state.loading || state.done) return { ok: true, skipped: true };
        const scrollElement = readScrollElement();
        const scrollTopBefore = Number(scrollElement?.scrollTop || 0) || 0;
        let userScrolledDuringLoad = false;
        const markUserScrollDuringLoad = () => {
            userScrolledDuringLoad = true;
        };
        try {
            scrollElement?.addEventListener?.("scroll", markUserScrollDuringLoad, {
                passive: true,
                once: true,
            });
        } catch (e) {
            console.debug?.(e);
        }
        const selectionBefore = Array.isArray(state.selectedIds) ? state.selectedIds.join("|") : "";
        const activeBefore = String(state.activeId || "");
        recordOperation("appendNextPage", { appendReason: "pagination" });
        if (!canLoadFromHost()) {
            // When the grid is empty (initial load or post-reattach) and the host
            // isn't measured yet, schedule a single retry so the first page loads
            // once the layout settles  -  prevents a permanent blank grid.
            if (!state.assets.length && !state.loading && shouldPrefetchNextPage()) {
                scheduleNextPagePrefetch(state.requestId, 400);
            }
            return { ok: true, skipped: true, hidden: true };
        }
        if (isExecutionBusy()) {
            return { ok: true, skipped: true, busy: true };
        }

        const baseLimit = Math.max(
            1,
            Math.min(APP_CONFIG.MAX_PAGE_SIZE, APP_CONFIG.DEFAULT_PAGE_SIZE),
        );
        const loadingStartedAt = Date.now();
        state.loading = true;
        clearStatusMessage();
        try {
            if (!state.done) {
                if (!canLoadFromHost()) {
                    return { ok: true, skipped: true, hidden: true };
                }
                if (!deferVisualResetUntilNextPage) {
                    repairOffsetFromLoadedAssets();
                }
                syncPagedStateFromLegacy();
                // Capture requestId at load start so beforeApplyPage can abort
                // if a scope switch incremented it while the fetch was in flight.
                const _loadReqId = state.requestId;
                const pageResult = await pagedAssets.loadUntilVisible({
                    maxEmptyPages: 6,
                    canContinue: () => canLoadFromHost(),
                    getLimit: (emptyPageIndex: any) => getAdaptivePageLimit(baseLimit, emptyPageIndex),
                    beforeApplyPage: (page: any) => {
                        // Scope switch (or any requestId bump) happened while the
                        // fetch was in flight  -  discard the page to avoid applying
                        // stale data from the wrong offset / scope.
                        if (state.requestId !== _loadReqId) {
                            return { ok: false, stale: true };
                        }
                        const pageAssets = Array.isArray(page.assets) ? page.assets : [];
                        const responseHasItems = pageAssets.length > 0;
                        // Only commit a visual reset when the new page has
                        // something to render OR when the backend reports
                        // total=0. Transient empty pages must keep cached
                        // cards visible during scope switches.
                        if (!deferVisualResetUntilNextPage) return null;
                        const responseAssertsEmpty = page.total != null && Number(page.total) === 0;
                        if (responseHasItems || responseAssertsEmpty) {
                            resetAssets({ query: state.query || "*", total: null, done: false });
                            setLegacyPageState({ offset: 0, cursor: null, total: null, done: false });
                            resetAssetCollectionsState(state);
                            metrics.resetCount += 1;
                            deferVisualResetUntilNextPage = false;
                            return null;
                        }
                        if (Array.isArray(state.assets) && state.assets.length) {
                            mjrDbg(
                                "[Grid LoadPage] empty response with cached visible assets  -  preserving cached view",
                                { offset: state.offset, query: state.query },
                            );
                            return {
                                ok: true,
                                count: 0,
                                total: state.total,
                                preservedCached: true,
                            };
                        }
                        return null;
                    },
                    onEmptyPage: (result: any) => {
                        if (result.emptyPageIndex <= 0) return;
                        mjrDbg(`[Grid LoadPage] fetched adaptive page with no visible additions: offset=${state.offset}, fetched=${result.count}, consumed=${result.advanced}, added=${result.added}, limit=${result.limit}, done=${state.done}, visibleCount=${state.assets.length}, total=${state.total}`);
                    },
                });
                syncLegacyStateFromPaged();
                repairOffsetFromLoadedAssets();

                if (!pageResult.ok) {
                    if (pageResult.aborted || pageResult.stale) {
                        return pageResult;
                    }
                    setLegacyPageState({ done: true });
                    setStatusMessage(
                        formatLoadErrorMessage("Failed to load assets", pageResult?.error || "Unknown error"),
                        { error: true },
                    );
                    return pageResult;
                }

                if (pageResult.preservedCached) return pageResult;

                if (state.done || pageResult.added > 0) {
                    rememberSnapshot(state.query || readGridQueryText(gridContainer, "*"));
                    return {
                        ok: true,
                        count: pageResult.count,
                        total: state.total,
                    };
                }

                const emptyResult = pageResult.lastResult || pageResult.firstResult || pageResult;
                mjrDbg(`[Grid LoadPage] fetched one page with no visible additions: offset=${state.offset}, fetched=${pageResult.count}, consumed=${emptyResult.advanced}, added=${emptyResult.added}, limit=${emptyResult.limit || baseLimit}, done=${state.done}, visibleCount=${state.assets.length}, total=${state.total}`);
                if (!state.done && shouldPrefetchNextPage()) {
                    scheduleNextPagePrefetch(
                        Number(state.requestId ?? 0) || 0,
                        Math.min(APP_CONFIG.PREFETCH_NEXT_PAGE_DELAY_MS ?? 700, 250),
                    );
                }
                return {
                    ok: true,
                    count: pageResult.count,
                    total: state.total,
                    skippedEmpty: true,
                };
            }

            return {
                ok: true,
                count: 0,
                total: state.total,
            };
        } catch (err) {
            try {
                if (String((err as any)?.name || "") === "AbortError") {
                    return { ok: false, aborted: true, error: "Aborted" };
                }
            } catch (e) {
                console.debug?.(e);
            }
            setStatusMessage(formatLoadErrorMessage("Failed to load assets", err), {
                error: true,
            });
            return { ok: false, error: (err as any)?.message || String(err) };
        } finally {
            try {
                scrollElement?.removeEventListener?.("scroll", markUserScrollDuringLoad);
            } catch (e) {
                console.debug?.(e);
            }
            try {
                if (
                    scrollElement &&
                    !userScrolledDuringLoad &&
                    Number(scrollElement.scrollTop || 0) !== scrollTopBefore
                ) {
                    scrollElement.scrollTop = scrollTopBefore;
                    metrics.scrollRestoreCount += 1;
                }
            } catch (e) {
                console.debug?.(e);
            }
            try {
                const selectionAfter = Array.isArray(state.selectedIds)
                    ? state.selectedIds.join("|")
                    : "";
                if (
                    (selectionAfter !== selectionBefore || String(state.activeId || "") !== activeBefore) &&
                    typeof setSelection === "function"
                ) {
                    setSelection(selectionBefore ? selectionBefore.split("|").filter(Boolean) : [], activeBefore, {
                        preserveAnchor: true,
                    });
                }
            } catch (e) {
                console.debug?.(e);
            }
            try {
                const elapsedMs = Date.now() - loadingStartedAt;
                if (elapsedMs < MIN_LOADING_SKELETON_MS) {
                    await waitMs(MIN_LOADING_SKELETON_MS - elapsedMs);
                }
            } catch (e) {
                console.debug?.(e);
            }
            state.loading = false;
            clearLoadingMessage();
        }
    }

    /**
     * Background "head refresh" used after the snapshot fast-path returns
     * cached:true. The snapshot may be stale (assets were generated while
     * the user was on another scope, or the snapshot was restored from
     * localStorage at page load), so we silently re-fetch page 0 and
     * upsert any new items at their correct sort position. Existing cards
     * stay put, scroll position is preserved, no flicker.
     *
     * Returns early when the request is superseded (state.requestId
     * changed during the fetch) or when ComfyUI is generating.
     */
    let _headRefreshTimer: any = null;
    function scheduleSnapshotHeadRefresh(delayMs = 250) {
        recordOperation("refreshHead", { appendReason: "snapshotHeadRefresh" });
        try {
            if (_headRefreshTimer) clearTimeout(_headRefreshTimer);
        } catch (e) {
            console.debug?.(e);
        }
        const requestId = state.requestId;
        // Capture the active grid context (scope/sort/filters) at schedule
        // time. If the user changes any filter or scope before the head
        // refresh fires, we must abort  -  otherwise upsertAsset() would
        // inject items that don't match the current filter into the grid.
        let scheduledKey: any = null;
        try {
            scheduledKey = buildGridSnapshotKey(buildCurrentSnapshotParts());
        } catch (e) {
            console.debug?.(e);
        }
        _headRefreshTimer = setTimeout(async () => {
            _headRefreshTimer = null;
            if (Number(state.requestId) !== Number(requestId)) return;
            if (isExecutionBusy()) return;
            const gridContainer = getGridContainer();
            if (!gridContainer) return;
            // Filter/scope guard: bail if the user navigated away.
            if (scheduledKey) {
                let currentKey: any = null;
                try {
                    currentKey = buildGridSnapshotKey(buildCurrentSnapshotParts());
                } catch (e) {
                    console.debug?.(e);
                }
                if (currentKey && currentKey !== scheduledKey) {
                    mjrDbg("[Grid] Snapshot head refresh skipped  -  context changed");
                    return;
                }
            }
            const headLimit = Math.max(
                1,
                Math.min(APP_CONFIG.MAX_PAGE_SIZE, APP_CONFIG.DEFAULT_PAGE_SIZE),
            );
            try {
                const page = await fetchPage(state.query || "*", headLimit, 0, {
                    requestId,
                    signal: state.abortController?.signal || null,
                });
                if (Number(state.requestId) !== Number(requestId)) return;
                // Re-check context after the await  -  the user may have
                // switched scope while the fetch was in flight.
                if (scheduledKey) {
                    let currentKey: any = null;
                    try {
                        currentKey = buildGridSnapshotKey(buildCurrentSnapshotParts());
                    } catch (e) {
                        console.debug?.(e);
                    }
                    if (currentKey && currentKey !== scheduledKey) {
                        mjrDbg("[Grid] Snapshot head refresh discarded  -  context changed mid-fetch");
                        return;
                    }
                }
                if (!page?.ok) return;
                const pageAssets = Array.isArray(page.assets) ? page.assets : [];
                if (page.total != null) setLegacyPageState({ total: page.total });
                if (!pageAssets.length) return;
                let inserted = 0;
                for (const asset of pageAssets) {
                    if (asset && asset.id != null && upsertAsset(asset)) inserted += 1;
                }
                if (inserted > 0) {
                    rememberSnapshot(state.query || "*");
                    mjrDbg("[Grid] Snapshot head refresh: inserted", inserted, "new asset(s)");
                }
            } catch (e) {
                console.debug?.("[Grid] Snapshot head refresh failed", e);
            }
        }, Math.max(0, Number(delayMs) || 0));
    }

    async function loadAssets(query = "*", options: Record<string, any> = {}) {
        const startRequestId = state.requestId;
        const { reset = true, preserveVisibleUntilReady = true } = options || {};
        const gridContainer = getGridContainer();
        if (!gridContainer) {
            return { ok: false, error: "Grid unavailable" };
        }

        if (state.requestId !== startRequestId) {
            console.debug?.("[Grid] loadAssets aborted early due to rapid scope switch");
            return { ok: false, aborted: true };
        }

        try {
            (globalThis as any).__MJR_LAST_ASSETKEY_GRID__ = gridContainer;
        } catch (e) {
            console.debug?.(e);
        }

        const requestedQuery = coerceQueryText(query).trim() || "*";
        const normalizedRequestedQuery = /^\[object\s+HTML.*Element\]$/i.test(requestedQuery)
            ? "*"
            : requestedQuery;
        const safeQuery = sanitizeQuery(normalizedRequestedQuery) || normalizedRequestedQuery;
        state.query = safeQuery;
        const resetInfo = reset
            ? resolveResetReason(safeQuery, options)
            : { reason: "append", nextContext: getCurrentResetContext(safeQuery) };
        recordOperation("reload", { reloadReason: resetInfo.reason || "loadAssets" });

        if (reset && isExecutionBusy()) {
            // Try to hydrate from a cached snapshot before deferring so the
            // grid never appears empty during a generation. Only the API
            // refresh is deferred  -  the user still sees the previously loaded
            // assets immediately. Falls back to the original "deferred" state
            // when no snapshot is available.
            let hydratedFromSnapshot = false;
            try {
                const snapshotParts = buildCurrentSnapshotParts();
                (snapshotParts as Record<string, any>).query = safeQuery;
                const snapshotKey = buildGridSnapshotKey(snapshotParts);
                if (canUseSnapshotCache() && hasGridSnapshot(snapshotKey)) {
                    hydratedFromSnapshot = await hydrateFromSnapshot(snapshotParts, {
                        allowReplaceExisting: true,
                    });
                    if (state.requestId > startRequestId + (hydratedFromSnapshot ? 1 : 0)) {
                        console.debug?.("[Grid] loadAssets aborted during deferral due to scope switch");
                        return { ok: false, aborted: true };
                    }
                }
            } catch (e) {
                console.debug?.(e);
            }
            if (hydratedFromSnapshot) {
                clearLoadingMessage();
                state.loading = false;
            } else {
                setLoadingMessage("Grid refresh deferred while ComfyUI is generating...");
            }
            scheduleDeferredExecutionReload(safeQuery, options || {});
            return {
                ok: true,
                deferred: true,
                cached: hydratedFromSnapshot,
                count: Number(state.offset || 0) || 0,
                total: Number(state.total || 0) || 0,
            };
        }

        if (reset) {
            const resetReason = resetInfo.reason || "unknown";
            metrics.lastResetReason = resetReason;
            const allowImmediateReset = canResetImmediately(resetReason);
            if (!allowImmediateReset && Array.isArray(state.assets) && state.assets.length) {
                metrics.blockedImmediateResetCount += 1;
            }
            deferVisualResetUntilNextPage = !allowImmediateReset || shouldPreserveVisibleOnReset({
                reset,
                preserveVisibleUntilReady,
            });
            try {
                state.abortController?.abort?.();
            } catch (e) {
                console.debug?.(e);
            }
            state.loading = false;
            try {
                state.abortController =
                    typeof AbortController !== "undefined" ? new AbortController() : null;
            } catch {
                state.abortController = null;
            }
            state.requestId = (Number(state.requestId) || 0) + 1;
            clearPrefetchTimer();
            clearPendingUpserts();
            clearStatusMessage();
            if (deferVisualResetUntilNextPage) {
                state.query = safeQuery;
                setLegacyPageState({ offset: 0, cursor: null, total: null, done: false });
                resetAssetCollectionsState(state, {
                    rebuildFromVisible: true,
                    preserveHiddenCount: true,
                    assetKey: (asset: any) => assetKey(asset, gridContainer),
                });
            } else {
                resetAssets({ query: safeQuery, total: null, done: false });
                setLegacyPageState({ offset: 0, cursor: null, total: null, done: false });
                resetAssetCollectionsState(state);
                metrics.resetCount += 1;
            }
            setLoadingMessage(
                safeQuery === "*" ? "Loading assets..." : `Searching for "${safeQuery}"...`,
            );
        }

        const result = await loadNextPage();
        repairOffsetFromLoadedAssets();

        // Prefetch next page after the first render settles.
        if (
            shouldPrefetchNextPage() &&
            result?.ok &&
            !result?.skipped &&
            !state.done &&
            !result?.aborted
        ) {
            scheduleNextPagePrefetch(state.requestId);
        }

        if (reset) {
            const showEmptyMessage = !(
                result?.skipped ||
                result?.hidden ||
                result?.aborted ||
                result?.stale ||
                result?.preservedCached ||
                result?.busy
            );
            finalizeLoad({ title: safeQuery, showEmptyMessage });
            rememberCurrentGridContext(resetInfo.nextContext);
        }
        return {
            ok: !!result?.ok,
            count: Number(state.offset || 0) || 0,
            total: Number(state.total || 0) || 0,
            error: result?.error,
            aborted: !!result?.aborted,
        };
    }

    async function loadAssetsFromList(assets: any, options: Record<string, any> = {}) {
        const {
            title = "Collection",
            reset = true,
            showLoading = true,
            preserveVisibleUntilReady = true,
        } = options || {};
        recordOperation("reload", { reloadReason: options?.resetReason || options?.reason || "collection" });
        const gridContainer = getGridContainer();
        if (!gridContainer) {
            return { ok: false, error: "Grid unavailable" };
        }

        const list = Array.isArray(assets) ? assets.slice() : [];
        const loadingStartedAt = Date.now();

        const preserveVisible = shouldPreserveVisibleOnReset({
            reset,
            preserveVisibleUntilReady,
        });

        if (reset) {
            metrics.lastResetReason = String(options?.resetReason || options?.reason || "collection");

            try {
                state.abortController?.abort?.();
            } catch (e) {
                console.debug?.(e);
            }
            try {
                state.abortController =
                    typeof AbortController !== "undefined" ? new AbortController() : null;
            } catch {
                state.abortController = null;
            }
            state.requestId = (Number(state.requestId) || 0) + 1;
            clearPendingUpserts();
            clearStatusMessage();
            if (preserveVisible) {
                state.query = String(title || "Collection");
                setLegacyPageState({ offset: 0, cursor: null, total: list.length, done: true });
            } else {
                resetAssets({
                    query: String(title || "Collection"),
                    total: list.length,
                    done: true,
                });
                setLegacyPageState({ offset: 0, cursor: null, total: list.length, done: true });
                resetAssetCollectionsState(state);
                metrics.resetCount += 1;
            }
            if (showLoading) {
                setLoadingMessage(list.length ? `Loading ${title}...` : `${title} is empty`);
                state.loading = true;
            } else {
                clearLoadingMessage();
                state.loading = false;
            }
        }

        try {
            const sortKey = readGridSortKey(gridContainer, "mtime_desc");
            const sorted = list.sort((a, b) => compareAssets(a, b, sortKey));
            if (reset && preserveVisible) {
                resetAssets({
                    query: String(title || "Collection"),
                    total: list.length,
                    done: true,
                });
                resetAssetCollectionsState(state);
                metrics.resetCount += 1;
            }
            appendAssets(gridContainer, sorted);
            setLegacyPageState({ offset: sorted.length, cursor: null, total: sorted.length, done: true });
            finalizeLoad({ title });
            rememberCurrentGridContext(getCurrentResetContext(String(title || "Collection")));
            return { ok: true, count: sorted.length, total: sorted.length };
        } catch (err) {
            setStatusMessage(formatLoadErrorMessage("Failed to load collection", err), {
                error: true,
            });
            return { ok: false, error: (err as any)?.message || String(err) };
        } finally {
            if (showLoading) {
                try {
                    const elapsedMs = Date.now() - loadingStartedAt;
                    if (elapsedMs < MIN_LOADING_SKELETON_MS) {
                        await waitMs(MIN_LOADING_SKELETON_MS - elapsedMs);
                    }
                } catch (e) {
                    console.debug?.(e);
                }
            }
            state.loading = false;
            if (showLoading) {
                clearLoadingMessage();
            }
        }
    }

    function prepareGridForScopeSwitch() {
        recordOperation("reload", { reloadReason: "scopeSwitch" });
        try {
            disposeStackGroupCards(getGridContainer());
        } catch (e) {
            console.debug?.(e);
        }
        try {
            state.abortController?.abort?.();
        } catch (e) {
            console.debug?.(e);
        }
        state.abortController = null;
        state.requestId = (Number(state.requestId) || 0) + 1;
        state.loading = true;
        // Reset pagination state so that any maybeFillViewport call triggered
        // by the visual reset cannot start a competing load before reloadGrid()
        // hands the new request to loadAssets(reset).
        setLegacyPageState({ offset: 0, cursor: null, total: null, done: false });
        clearPrefetchTimer();
        clearPendingUpserts();
        clearLoadingMessage();
        clearStatusMessage();
        deferVisualResetUntilNextPage = true;
        // void hydrateFromSnapshot(buildCurrentSnapshotParts(), { allowReplaceExisting: true }); // Disabled: prevents old images from flashing during tab switch
        setSelection([], "");
    }

    function refreshGrid() {
        state.virtualGrid?.setItems?.(state.assets || []);
    }

    function removeAssets(assetIds: any, { updateSelection = true } = {}) {
        const removedCount = removeAssetsFromState(state, assetIds, {
            assetKey: (asset) => assetKey(asset, getGridContainer()),
        });
        if (!removedCount) {
            return { ok: false, removed: 0, selectedIds: state.selectedIds.slice() };
        }

        if (Number.isFinite(Number(state.total))) {
            setLegacyPageState({ total: Math.max(0, Number(state.total || 0) - removedCount) });
        }

        if (updateSelection) {
            const visibleIds = state.assets.map((asset: any) => String(asset?.id || "")).filter(Boolean);
            reconcileSelection(visibleIds, { activeId: state.activeId });
        }

        if (!state.assets.length && !state.loading) {
            setStatusMessage("No assets found");
        }

        return {
            ok: true,
            removed: removedCount,
            selectedIds: state.selectedIds.slice(),
        };
    }

    function captureAnchor() {
        const gridContainer = getGridContainer();
        const scrollElement = readScrollElement();
        if (!gridContainer || !scrollElement) return null;

        const isCardVisibleInHost = (card: any) => {
            if (!card) return false;
            try {
                const rect = card.getBoundingClientRect();
                const hostRect = scrollElement.getBoundingClientRect();
                return rect.bottom >= hostRect.top && rect.top <= hostRect.bottom;
            } catch {
                return false;
            }
        };

        const selectedElement = findSelectedAssetCard(gridContainer);
        const visibleCards = readRenderedCards();
        const firstVisible = visibleCards.find((card: any) => isCardVisibleInHost(card)) || visibleCards[0] || null;
        const selectedVisible = isCardVisibleInHost(selectedElement);
        const anchorEl = selectedVisible ? selectedElement : firstVisible;
        if (!anchorEl) return null;

        try {
            const rect = anchorEl.getBoundingClientRect();
            const hostRect = scrollElement.getBoundingClientRect();
            return {
                id: String(anchorEl.dataset?.mjrAssetId || ""),
                top: rect.top - hostRect.top,
                scrollTop: Number(scrollElement.scrollTop || 0) || 0,
            };
        } catch (e) {
            console.debug?.(e);
            return null;
        }
    }

    async function restoreAnchor(anchor: any) {
        const gridContainer = getGridContainer();
        const scrollElement = readScrollElement();
        if (!gridContainer || !scrollElement || !anchor) return;

        await waitForLayout();

        const id = String(anchor.id || "").trim();
        if (!id) {
            scrollElement.scrollTop = Number(anchor.scrollTop || 0) || 0;
            metrics.scrollRestoreCount += 1;
            return;
        }

        scrollToAssetId(id, { align: "start" });
        await waitForLayout();

        const target = findAssetCardById(gridContainer, id);
        if (!target) {
            scrollElement.scrollTop = Number(anchor.scrollTop || 0) || 0;
            metrics.scrollRestoreCount += 1;
            return;
        }

        try {
            const rect = target.getBoundingClientRect();
            const hostRect = scrollElement.getBoundingClientRect();
            const delta = rect.top - hostRect.top - Number(anchor.top || 0);
            scrollElement.scrollTop = (Number(scrollElement.scrollTop || 0) || 0) + delta;
            metrics.scrollRestoreCount += 1;
        } catch (e) {
            console.debug?.(e);
            scrollElement.scrollTop = Number(anchor.scrollTop || 0) || 0;
            metrics.scrollRestoreCount += 1;
        }
    }

    async function hydrateFromSnapshot(parts: Record<string, any> = {}, options: Record<string, any> = {}) {
        const gridContainer = getGridContainer();
        if (!gridContainer) return false;
        if (!canUseSnapshotCache()) return false;
        const key = buildGridSnapshotKey(parts);
        const snapshot = getGridSnapshot(key);
        if (!snapshot || !Array.isArray(snapshot.assets) || !snapshot.assets.length) {
            return false;
        }
        if (!options.allowReplaceExisting && Array.isArray(state.assets) && state.assets.length) {
            return false;
        }
        metrics.lastResetReason = "snapshot";
        try {
            state.abortController?.abort?.();
        } catch (e) {
            console.debug?.(e);
        }
        try {
            state.abortController =
                typeof AbortController !== "undefined" ? new AbortController() : null;
        } catch {
            state.abortController = null;
        }
        state.requestId = (Number(state.requestId) || 0) + 1;
        clearPrefetchTimer();
        clearPendingUpserts();
        resetAssets({
            query: snapshot.query || options.title || "Cached",
            total: Number(snapshot.total ?? snapshot.assets.length) || snapshot.assets.length,
            done: !!snapshot.done,
        });
        resetAssetCollectionsState(state);
        metrics.resetCount += 1;
        clearStatusMessage();
        clearLoadingMessage();
        state.loading = false;

        appendAssets(gridContainer, snapshot.assets);
        setLegacyPageState({
            offset: Math.max(
                Number(snapshot.assets.length || 0) || 0,
                Number(snapshot.offset || snapshot.assets.length) || snapshot.assets.length,
            ),
            cursor: (snapshot as any).cursor || null,
            total: Number(snapshot.total ?? snapshot.assets.length) || snapshot.assets.length,
            done: !!snapshot.done,
        });
        // Mark this hydrate so loadAssets fast-path can skip a redundant
        // re-hydrate triggered immediately after by scopeController.
        state._mjrLastHydrateKey = key;
        state._mjrLastHydrateAt = Date.now();
        rememberCurrentGridContext({ ...parts, query: snapshot.query || parts.query || "*" });
        // The snapshot is now on screen  -  any subsequent prefetch must
        // APPEND, not reset. Clear the defer flag set by
        // prepareGridForScopeSwitch / loadAssets so loadNextPage doesn't
        // wipe visible cards on the next response.
        deferVisualResetUntilNextPage = false;
        finalizeLoad({ title: snapshot.title || options.title || "Cached" });
        return true;
    }

    function _buildUpsertDeps(gridContainer: any) {
        return {
            getOrCreateState: () => state,
            ensureVirtualGrid: () => state.virtualGrid,
            upsertState: UPSERT_BATCH_STATE,
            maxBatchSize: UPSERT_BATCH_MAX_SIZE,
            debounceMs: UPSERT_BATCH_DEBOUNCE_MS,
            assetKey: (asset: any) => getStableRealtimeAssetKey(asset) || assetKey(asset, gridContainer),
            loadMajoorSettings,
        };
    }

    function upsertAsset(asset: any) {
        recordOperation("upsertRealtime", { appendReason: "realtime" });
        const gridContainer = getGridContainer();
        if (!gridContainer || !asset || !asset.id) return false;
        return queueUpsertAsset(gridContainer, asset, _buildUpsertDeps(gridContainer));
    }

    /**
     * Upsert an asset and flush within the current microtask boundary.
     *
     * Multiple calls in the same event-loop tick are coalesced into a single
     * flush via queueMicrotask.  This avoids N separate flush+setItems calls
     * when N WebSocket events arrive nearly simultaneously (e.g. 5 images
     * generated in a batch), while still being much faster than the 200 ms
     * debounce path.
     */
    let _immediateFlushScheduled = false;
    function upsertAssetNow(asset: any) {
        recordOperation("upsertRealtime", { appendReason: "realtimeImmediate" });
        const gridContainer = getGridContainer();
        if (!gridContainer || !asset || !asset.id) return false;
        const deps = _buildUpsertDeps(gridContainer);
        const ok = queueUpsertAsset(gridContainer, asset, deps);
        if (ok && !_immediateFlushScheduled) {
            _immediateFlushScheduled = true;
            queueMicrotask(() => {
                _immediateFlushScheduled = false;
                const gc = getGridContainer();
                if (gc) flushUpsertBatch(gc, _buildUpsertDeps(gc));
            });
        }
        return ok;
    }

    function dispose() {
        rememberSnapshot(state.query || "Cached");
        // Force any pending debounced storage write to flush before unload.
        try { flushGridSnapshotsPersist(); } catch (e) { console.debug?.(e); }
        clearDeferredExecutionReload();
        clearPrefetchTimer();
        try {
            state.abortController?.abort?.();
        } catch (e) {
            console.debug?.(e);
        }
        const gridContainer = getGridContainer();
        if (gridContainer) {
            try {
                const batchState = getUpsertBatchState(gridContainer, UPSERT_BATCH_STATE);
                if (batchState?.timer) {
                    clearTimeout(batchState.timer);
                    batchState.timer = null;
                }
                batchState?.pending?.clear?.();
            } catch (e) {
                console.debug?.(e);
            }
        }
    }

    // Abort any in-flight requests before the page unloads so they don't
    // appear as epoch-1 cancelled requests in the next session's network log.
    try {
        window.addEventListener("pagehide", dispose, { once: true });
    } catch (e) {
        console.debug?.(e);
    }

    return {
        reload: loadAssets,
        appendNextPage: loadNextPage,
        refreshHead: scheduleSnapshotHeadRefresh,
        upsertRealtime: upsertAssetNow,
        loadAssets,
        loadAssetsFromList,
        loadNextPage,
        prepareGridForScopeSwitch,
        refreshGrid,
        removeAssets,
        captureAnchor,
        restoreAnchor,
        hydrateFromSnapshot,
        upsertAsset,
        upsertAssetNow,
        getCanonicalState,
        getDebugSnapshot,
        dispose,
    };
}
