import { queryStatus, } from '../model';
import { QueryClient, QueryObserver, } from 'react-query/core';
import { action, computed, makeObservable, observable, reaction, runInAction, } from 'mobx';
import { EventEmitter } from 'events';
import { QueryState } from './QueryState';
import { QueryResultState } from './QueryResultState';
import { BulkSelectState } from './BulkSelectState';
import { PromisesState } from './PromisesState';
import { initialCursor } from './Pagination';
import { PaginationMode } from './PaginationMode';
import { TaskState } from './TaskState';
import { addEventListener, OfflineError } from '../util';
import { TransitionState } from './TransitionState';
import { FetchTotalState } from './FetchTotalState';
const MAX_RETRIES = 3;
export class CollectionState {
    get queryKeyOptions() {
        const { query } = this;
        return {
            queryKey: this._queryKey(query.asComputed),
        };
    }
    get originQuery() {
        return this.result.originQuery;
    }
    get status() {
        return this.result.status;
    }
    isOnline() {
        return this.onlineState.isOnline;
    }
    get optimisticActions() {
        if (this._optimisticActions == null &&
            process.env.NODE_ENV !== 'production') {
            throw new Error('optimisticActions is not initialized');
        }
        return this._optimisticActions;
    }
    constructor(params) {
        this.scrollableContent = null;
        this.searchInputRef = null;
        this.emitter = new EventEmitter();
        this.initialPromises = new PromisesState();
        this.refreshPromises = new PromisesState();
        this.initTask = new TaskState();
        /**
         * Possible flows:
         * result->refresh, refresh->result, result->fetchMore, fetchMore->result, refresh->refresh, fetchMore->fetchMore
         */
        this.lifecycleState = 'result';
        this._queryKey = (asComputed) => {
            const { queryName } = this;
            const { filters, rawFilters, search, hasActiveFilters, hasNonPersistentActiveFilters, nonPersistentFiltersKey, columns, 
            // Pick only properties relevant to `queryKey` uniqueness
            ...computedQuery } = asComputed;
            return [queryName, computedQuery];
        };
        this._queryFn = async (computedQuery) => {
            const { items, total, available = null, cursor = null, ...rest } = await this.fetchData(computedQuery);
            return {
                items,
                total,
                available,
                cursor,
                ...rest,
            };
        };
        this.animationFramePromise = () => new Promise((resolve) => {
            requestAnimationFrame(() => {
                requestAnimationFrame(() => {
                    resolve();
                });
            });
        });
        this.onError = action((err) => {
            if (err == null) {
                return;
            }
            const { onlineState, events, errorMonitor } = this;
            console.error(err);
            const { isOnline } = onlineState;
            if (!(err instanceof OfflineError)) {
                const handled = events.onError?.({
                    err,
                    isOnline,
                });
                if (!handled) {
                    errorMonitor.captureException(err);
                }
            }
            this.getCurrentQuery().invalidate();
            this._resetReactQuery();
            this.updateStatus();
        });
        this.fetchNextPage = action(async () => {
            const { query, result, events: { onNewPageAdded, onNewPageStart }, emitter, } = this;
            // if current page has an error, do not skip to next page
            if (!result.status.isError) {
                query.pagination.nextPage();
            }
            this.lifecycleState = 'fetchMore';
            const onNewPageAddedListeners = emitter
                .listeners('newPageStart')
                .map((l) => l());
            const maybeOnNewPageAdded = onNewPageStart?.({
                query: query.asComputed,
            });
            const { promise, currentQuery, onResultListeners } = this.fetch();
            await promise;
            runInAction(() => {
                if (promise === this._currentQueryPromise) {
                    const updateResultInfo = this.updateResult([currentQuery]);
                    onResultListeners.forEach((l) => l?.());
                    if (updateResultInfo) {
                        const { newPages } = updateResultInfo;
                        const onNewPageAddedParams = {
                            totalNewItems: newPages.reduce((agg, page) => agg + page.items.length, 0),
                            newPages,
                            query: result.originQuery,
                            result: result.asComputed,
                        };
                        onNewPageAddedListeners.forEach((l) => l(onNewPageAddedParams));
                        if (maybeOnNewPageAdded != null) {
                            maybeOnNewPageAdded(onNewPageAddedParams);
                        }
                        onNewPageAdded?.(onNewPageAddedParams);
                    }
                }
            });
        });
        this._recoverIfError = action(() => {
            const { onlineState } = this;
            const { focusManager } = onlineState;
            if (focusManager.isFocused() &&
                onlineState.isOnline &&
                this.result.status.isError) {
                if (this.lifecycleState === 'fetchMore') {
                    // error state is not shown, fetch next page
                    this.fetchNextPageIfNeeded();
                }
                else {
                    // error state is shown, move to start
                    this.clearResultAndMoveToStart({ force: true });
                }
            }
        });
        this.onOnlineStateChange = (isOnline) => {
            const currentQuery = this.getCurrentQuery();
            if (isOnline) {
                this._recoverIfError();
            }
            else if (!isOnline && currentQuery.state.isPaused) {
                currentQuery.dispatch({
                    type: 'error',
                    error: new OfflineError('CollectionState.onOnlineStateChange: offline'),
                });
                this.updateStatus();
            }
        };
        this.retryErrorState = action(() => {
            const { initTask } = this;
            if (initTask.status.isError) {
                initTask.runOnce();
            }
            else {
                this.clearResultAndMoveToStart();
            }
        });
        this._topNotification = new TransitionState();
        this.lodash = params.lodash;
        this.persistQueryToUrl = params.persistQueryToUrl;
        this.toExtendedFields = params.toExtendedFields;
        this.hooks =
            params.hooks ??
                new EventEmitter();
        makeObservable(this, {
            fetchNextPageIfNeeded: action.bound,
            clearAndMoveToPage: action.bound,
            clearResultAndMoveToStart: action.bound,
            refreshAllPages: action.bound,
            updateStatus: action.bound,
            updateResult: action.bound,
            search: action,
            sort: action,
            fetch: action,
            init: action,
            setInitialPage: action,
            initialFetch: action,
            totalPages: computed,
            hasAvailableItems: computed,
            showInitialLoader: computed,
            showRefreshLoader: computed,
            showEmptyState: computed,
            showErrorState: computed,
            resetFiltersAndRefresh: action.bound,
            lifecycleState: observable.ref,
            clearResultBeforeRefresh: action,
            scrollableContent: observable.ref,
            searchInputRef: observable.ref,
            _commonDynamicBiParams: action,
        });
        this.queryName = params.queryName;
        this.fqdn = params.fqdn;
        this.deferInitialLoading = params.deferInitialLoading;
        this.queryCache = params.queryCache;
        this.errorMonitor = params.errorMonitor;
        this.errorHandler = params.errorHandler;
        this.onlineState = params.onlineState;
        this.history = params.history;
        this.fetchErrorMessage = params.fetchErrorMessage;
        this.itemKey = params.itemKey;
        this.itemName = params.itemName;
        this.translate = params.translate;
        this.fetchData = params.fetchData;
        this.events = params.events ?? {};
        this.initDependencies = params.initDependencies;
        this.showToast = params.showToast ?? (() => ({ remove: () => { } }));
        // Default retry function: retry up to 3 times
        const defaultRetry = (failureCount, error) => {
            if (failureCount < MAX_RETRIES) {
                this.errorHandler?.reportRetryAttempt?.(error);
                return true;
            }
            return false;
        };
        const defaultQueryOptions = {
            queryFn: action(() => this._queryFn(this.query.asComputed)),
            keepPreviousData: true,
            suspense: false,
            enabled: false,
            retry: defaultRetry,
            retryDelay: 200,
            staleTime: 120000,
            onError: this.onError,
            refetchOnMount: false,
            retryOnMount: false,
            ...params.queryOptions,
        };
        this.queryClient = new QueryClient({
            queryCache: this.queryCache,
            defaultOptions: {
                queries: defaultQueryOptions,
            },
        });
        this.queryObserver = new QueryObserver(this.queryClient, defaultQueryOptions);
        this.query = new QueryState({
            filters: params.filters,
            search: params.search,
            limit: params.limit,
            history: params.persistQueryToUrl ? params.history : undefined,
            refreshOnColumnsChange: params.refreshOnColumnsChange,
        });
        this._paginationModeRef =
            params.paginationModeRef ??
                { current: params.paginationMode };
        this._paginationMode = new PaginationMode(this);
        this.selectionConsistencyMode =
            params.selectionConsistencyMode ?? 'preserve';
        this.result = new QueryResultState({
            itemKey: params.itemKey,
            originQuery: this.query.toComputed(),
            fetchTotal: params.fetchTotal
                ? new FetchTotalState({
                    collection: this,
                    queryCache: this.queryCache,
                    fetchTotal: params.fetchTotal,
                })
                : undefined,
        });
        this._resetReactQuery();
        this.fetchesHistory = [];
        this.bulkSelect = new BulkSelectState({
            disableAutoSelectAllCount: params.disableAutoSelectAllCount,
            useNewSelectAllLogic: params.useNewSelectAllLogic,
            collection: this,
            scrollToTop: () => {
                this.scrollableContent?.scrollTo({
                    top: 0,
                    left: 0,
                });
            },
        });
        if (params.initialPage) {
            this.setInitialPage(params.initialPage);
        }
        if (params.deferInitialLoading) {
            this.initialPromises.addPromise(this.animationFramePromise);
        }
        this._runSort = action(({ clearResult }) => {
            this.emitter.emit('sort', { clearResult });
            if (clearResult) {
                this.clearResultBeforeRefresh();
            }
            return this.moveToFirstPage();
        });
        this.scheduleSort = this.whenNotIdle(this.lodash.debounce(this._runSort, 300));
        this._runSearch = action(async (clearResult) => {
            const { events: { onSearch, onSearchResults }, emitter, query, } = this;
            const searchEndListeners = emitter
                .listeners('searchStart')
                .map((l) => l());
            onSearch?.({
                query: query.asComputed,
            });
            emitter.emit('search', { clearResult });
            if (clearResult) {
                this.clearResultBeforeRefresh();
            }
            if (this.selectionConsistencyMode === 'clear') {
                this.bulkSelect.resetToDefaults();
            }
            return this.moveToFirstPage({
                onResultUpdated: (params) => {
                    searchEndListeners.forEach((l) => l());
                    onSearchResults?.(params);
                },
            });
        });
        this.scheduleSearch = this.lodash.debounce(this._runSearch, 300);
    }
    _resetReactQuery() {
        this.queryObserver.setOptions({
            ...this.queryObserver.options,
            ...this.queryKeyOptions,
            enabled: false,
        }, {
            onError: true,
        });
    }
    setInitialPage(initialPage) {
        const { items, total: maybeTotal, available = null, cursor = null, hasNext, } = initialPage;
        const total = maybeTotal ?? items.length;
        this.queryClient.setQueryData(this.queryKeyOptions.queryKey, {
            items,
            total,
            available,
            cursor,
            hasNext,
        });
    }
    getCurrentQuery() {
        const query = this.queryObserver.getCurrentQuery();
        return query;
    }
    get paginationMode() {
        return this._paginationMode;
    }
    _getProcessedResult({ unprocessedPages, unprocessedTotal, currentItemsMap, }) {
        const { emitter, result } = this;
        const { originQuery } = result;
        const beforeAddNewPagesListeners = emitter.listeners('beforeAddNewPages');
        const processedPages = beforeAddNewPagesListeners.reduce((pages, listener) => listener({
            pages,
            currentItemsMap,
            originQuery,
        }), unprocessedPages);
        const processedTotal = this._getProcessedTotal(unprocessedTotal);
        return {
            processedPages,
            processedTotal,
        };
    }
    _getProcessedTotal(unprocessedTotal) {
        const { emitter, result } = this;
        const { originQuery } = result;
        const beforeSetTotalListeners = emitter.listeners('beforeSetTotal');
        return beforeSetTotalListeners.reduce((total, listener) => listener({
            total,
            originQuery,
        }), unprocessedTotal);
    }
    _addToFetchesHistory(newPages) {
        const { page, cursor, offset } = this.query.asComputed;
        this.fetchesHistory.push({
            itemsKeys: newPages
                .flatMap((p) => p.items)
                .map((item) => this.itemKey(item)),
            queryPageInfo: {
                page,
                cursor,
                offset,
            },
        });
    }
    updateResult(queries) {
        const { result, paginationMode, emitter } = this;
        const newPages = queries
            .map((reactQuery) => reactQuery.state.data)
            .filter((d) => d != null);
        this.updateStatus();
        if (newPages.length) {
            emitter.emit('beforeResultUpdated');
            const lastPage = newPages[newPages.length - 1];
            const { available, cursor, hasNext } = lastPage;
            const unprocessedTotal = lastPage.total ?? lastPage.items.length;
            const unprocessedPages = newPages.map((data) => data.items);
            const { processedPages, processedTotal } = this._getProcessedResult({
                unprocessedPages,
                unprocessedTotal,
                currentItemsMap: result._keyedItemsMap,
            });
            result.addPages({
                unprocessed: unprocessedPages,
                processed: processedPages,
            });
            result.setTotals({
                unprocessed: unprocessedTotal,
                processed: processedTotal,
            });
            this._addToFetchesHistory(newPages);
            result.setSupportTotal(lastPage.total != null);
            result.setHasNext(hasNext);
            paginationMode.setCursor(cursor);
            if (available != null) {
                result.available = available;
            }
            this.lifecycleState = 'result';
            emitter.emit('resultUpdated');
            return {
                newPages,
            };
        }
        return null;
    }
    whenNotIdle(fn) {
        return (...args) => {
            if (this.result.status.isIdle) {
                return;
            }
            fn(...args);
        };
    }
    updateStatus() {
        const { queryObserver, result, query } = this;
        const { status, error, isFetching } = queryObserver.getOptimisticResult(queryObserver.options);
        result.status = {
            ...queryStatus[status],
            isFetching,
            error,
        };
        result.originQuery = query.asComputed;
    }
    fetch() {
        const { queryObserver, onlineState, emitter } = this;
        queryObserver.setOptions({
            ...queryObserver.options,
            ...this.queryKeyOptions,
            enabled: true,
        }, {
            onError: true,
        });
        const currentQuery = this.getCurrentQuery();
        this._currentQueryPromise = new Promise((resolve) => {
            if (currentQuery.promise) {
                currentQuery.promise.then(() => resolve()).catch(() => resolve());
            }
            else {
                resolve();
            }
        });
        const isStale = currentQuery.isStale();
        const fetchListeners = emitter.listeners('fetch');
        const onResultListeners = fetchListeners.map((l) => l({ isStale }));
        if (!onlineState.isOnline) {
            currentQuery.dispatch({
                type: 'error',
                error: new OfflineError('CollectionState.fetch: offline'),
            });
        }
        this.updateStatus();
        return {
            currentQuery,
            isStale,
            onResultListeners,
            promise: this._currentQueryPromise,
        };
    }
    get totalPages() {
        const { result: { _totals: { unprocessed: total }, }, query: { pagination: { limit }, }, } = this;
        return Math.ceil(total / limit);
    }
    get total() {
        return this.result.total;
    }
    get totalStatus() {
        return this.result.totalStatus;
    }
    get fetchedLastPage() {
        const { paginationMode: { fetchedLastPage }, } = this;
        return fetchedLastPage;
    }
    async fetchNextPageIfNeeded() {
        const { result, fetchedLastPage, lifecycleState } = this;
        if (lifecycleState !== 'refresh' &&
            !result.status.isFetching &&
            !fetchedLastPage) {
            await this.fetchNextPage();
        }
    }
    // for rendering only a single page (<PaginationTable />)
    async clearAndMoveToPage(page, options = {}) {
        let minLoaderTime = 0;
        const { wait = { min: 200, max: 500 } } = options;
        const { query, result, events: { onNewPageStart }, } = this;
        const { pagination } = query;
        pagination.page = page;
        const maybeOnNewPageAdded = onNewPageStart?.({
            query: query.asComputed,
        });
        const { promise, currentQuery, onResultListeners } = this.fetch();
        const fetchPromise = promise;
        let timeoutId = null;
        const maxWaitPromise = new Promise((resolve) => {
            timeoutId = setTimeout(() => {
                // timeoutId was not cleared yet, start showing a loader and set minimal time for the loader to show
                minLoaderTime = wait.min;
                this.clearResultBeforeRefresh();
                resolve();
            }, wait.max);
        });
        // minimal time to wait (usually scrolling to top time) before applying the new result
        const minWaitPromise = new Promise((resolve) => {
            setTimeout(resolve, wait.min);
        });
        await Promise.race([maxWaitPromise, fetchPromise]);
        if (timeoutId != null) {
            clearTimeout(timeoutId);
        }
        if (minLoaderTime) {
            await new Promise((resolve) => setTimeout(resolve, minLoaderTime));
        }
        await minWaitPromise;
        await fetchPromise;
        if (promise === this._currentQueryPromise) {
            runInAction(() => {
                result.clear();
                const updateResultInfo = this.updateResult([currentQuery]);
                onResultListeners.forEach((l) => l?.());
                if (updateResultInfo) {
                    const { newPages } = updateResultInfo;
                    if (maybeOnNewPageAdded != null) {
                        maybeOnNewPageAdded({
                            totalNewItems: newPages.reduce((agg, page) => agg + page.items.length, 0),
                            query: result.originQuery,
                            result: result.asComputed,
                        });
                    }
                }
            });
        }
    }
    clearResultBeforeRefresh() {
        const { result } = this;
        this.lifecycleState = 'refresh';
        result.clear();
    }
    async moveToFirstPage({ onResultUpdated, } = {}) {
        const { query: { pagination }, } = this;
        pagination.page = 1;
        pagination.cursor = initialCursor;
        await this.fetchAndReplaceResult({ onResultUpdated });
    }
    invalidate({ filters } = {}) {
        const { queryCache, queryName } = this;
        const queryLookup = {};
        if (filters) {
            Object.assign(queryLookup, {
                filtersKey: filters,
            });
        }
        queryCache
            .findAll({ queryKey: [queryName, queryLookup] })
            .forEach((q) => q.invalidate());
    }
    clearResultAndMoveToStart({ onResultUpdated, force, filters, } = {}) {
        if (force) {
            this.invalidate({ filters });
        }
        this._resetReactQuery();
        this.clearResultBeforeRefresh();
        return this.moveToFirstPage({ onResultUpdated });
    }
    sort(col, { clearResult, multiple, forceDirection, } = {}) {
        const { query, emitter } = this;
        emitter.emit('beforeSortStart', {
            origin: 'ColumnHeader',
        });
        query.sort.changeSort(col, {
            multiple,
            forceDirection,
        });
        emitter.emit('sortStart', { col });
        return this.scheduleSort({
            clearResult,
            multiple,
            forceDirection,
        });
    }
    async fetchAndReplaceResult({ onResultUpdated, } = {}) {
        const { result, query, emitter } = this;
        const { promise, currentQuery, isStale, onResultListeners } = this.fetch();
        // page=1 means starting over, i.e refresh
        if (query.pagination.page === 1) {
            emitter.emit('refresh', { isStale });
        }
        await promise;
        if (promise === this._currentQueryPromise) {
            runInAction(() => {
                result.slice(0, query.pagination.page - 1);
                this.emitter.emit('refreshUpdateResult');
                this.updateResult([currentQuery]);
                onResultListeners.forEach((l) => l?.());
                onResultUpdated?.({
                    query: result.originQuery,
                    result: result.asComputed,
                });
            });
        }
        return currentQuery;
    }
    search(value, { force, clearResult } = {}) {
        const { query } = this;
        const { search } = query;
        if (force || value !== search.value) {
            search.changeValue(value);
            this.scheduleSearch(clearResult);
        }
    }
    clearSearch() {
        const { query, events: { onSearchResults }, emitter, } = this;
        const { search } = query;
        if (search.value === '') {
            return;
        }
        search.changeValue('');
        emitter.emit('clearSearch');
        if (this.selectionConsistencyMode === 'clear') {
            this.bulkSelect.resetToDefaults();
        }
        return this.clearResultAndMoveToStart({ onResultUpdated: onSearchResults });
    }
    get filters() {
        return this.query.filters;
    }
    changeItem(newItem) {
        return this.changeItems([newItem]);
    }
    changeItems(newItems) {
        const changeResult = this.result.changeItems(newItems);
        if (changeResult != null) {
            this.invalidate();
            return changeResult.rollback;
        }
        return undefined;
    }
    addItem(newItem) {
        return this.addItems([newItem]);
    }
    addItems(newItems) {
        const changeResult = this.result.addItems(newItems);
        this.invalidate();
        return changeResult.rollback;
    }
    async refreshCurrentPage({ noInvalidate } = {}) {
        const { emitter } = this;
        if (!noInvalidate) {
            this.invalidate();
        }
        this._resetReactQuery();
        emitter.emit('refreshCurrentPage');
        await this.fetchAndReplaceResult();
    }
    async refreshAllPages({ beforeUpdateResult, filters, } = {}) {
        const { query, result, paginationMode, emitter } = this;
        const { pagination } = query;
        const { page: currentPage } = pagination;
        this.invalidate({ filters });
        const promises = [];
        pagination.cursor = initialCursor;
        emitter.emit('refreshAllPages');
        emitter.emit('refresh', { isStale: true }); // Because of invalidation above, all queries are now stale
        let lastPagePromise = undefined;
        for (let page = 1; page <= currentPage; page++) {
            pagination.page = page;
            this._resetReactQuery();
            const { currentQuery: pageQuery, promise } = this.fetch();
            if (pageQuery.promise) {
                if (paginationMode.id === 'cursor') {
                    await pageQuery.promise;
                    runInAction(() => (pagination.cursor = pageQuery.state.data?.cursor ?? null));
                }
                promises.push(pageQuery.promise.then(() => pageQuery));
            }
            if (page === currentPage) {
                lastPagePromise = promise;
            }
        }
        this.updateStatus();
        const results = await Promise.all(promises);
        runInAction(() => {
            beforeUpdateResult?.();
            if (lastPagePromise === this._currentQueryPromise) {
                result.slice(currentPage);
                this.emitter.emit('refreshUpdateResult');
                this.updateResult(results);
            }
        });
    }
    get hasAvailableItems() {
        return this.result.hasAvailableItems;
    }
    resetFiltersAndRefresh() {
        const { query } = this;
        query.resetFilters({ emitEvents: ['change', 'refresh'] });
    }
    resetCustomFiltersAndRefresh() {
        const { query } = this;
        query.resetCustomFilters({ emitEvents: ['change', 'refresh'] });
    }
    async initialFetch() {
        const { result, events: { onInitialPageFetched }, hooks, query: { pagination }, } = this;
        const prevQuery = this.getCurrentQuery();
        if (!result.status.isIdle && !result.status.isError) {
            return prevQuery.promise;
        }
        this._resetReactQuery();
        pagination.page = 1;
        pagination.cursor = initialCursor;
        this.lifecycleState = 'refresh';
        const isStale = prevQuery.isStale();
        const beforeInitialFetchListeners = hooks.listeners('beforeInitialFetch');
        await Promise.all(beforeInitialFetchListeners.map((f) => f({ isStale })));
        // reset paging state again for any possible edge case caused by external side effect while waiting `beforeInitialFetchListeners`
        runInAction(() => {
            pagination.page = 1;
            pagination.cursor = initialCursor;
            this.lifecycleState = 'refresh';
        });
        const currentQuery = await this.fetchAndReplaceResult();
        await currentQuery.promise;
        this.emitter.emit('ready');
        runInAction(() => {
            const { total, available } = result;
            onInitialPageFetched?.({
                result: {
                    total,
                    available: available ?? 0,
                },
                query: result.originQuery,
            });
        });
    }
    init({ skipInitialFetch, skipFilterListeners, } = {}) {
        const { bulkSelect, onlineState, query, initTask, result } = this;
        const { focusManager } = onlineState;
        result.originQuery = query.asComputed;
        onlineState.events.on('change', this.onOnlineStateChange);
        const disposers = [
            query.init({ skipFilterListeners }),
            this._registerQueryEvents(),
            result.init(),
            bulkSelect.init(),
            // react-query considers "mounted" only if listeners are subscribed
            this.queryObserver.subscribe(() => { }),
            focusManager.subscribe(this._recoverIfError),
        ];
        initTask.runOnce(async () => {
            await Promise.all([
                skipInitialFetch ? null : this.initialFetch(),
                this.initDependencies?.(),
            ]);
        });
        return action(() => {
            onlineState.events.off('change', this.onOnlineStateChange);
            disposers.forEach((disposer) => disposer());
            initTask.status = {
                ...queryStatus.idle,
            };
        });
    }
    _registerQueryEvents() {
        const { query } = this;
        const disposers = [
            addEventListener(query.events, 'refresh', (opts) => {
                if (this.selectionConsistencyMode === 'clear') {
                    this.bulkSelect.resetToDefaults();
                }
                if (this.deferInitialLoading) {
                    this.refreshPromises.addPromise(this.animationFramePromise);
                }
                if (opts?.clearResult !== false) {
                    this.clearResultBeforeRefresh();
                }
                else {
                    this.scrollableContent?.scrollTo({ top: 0, behavior: 'smooth' });
                }
                this.moveToFirstPage();
            }),
            addEventListener(query.events, 'scheduleRefresh', (opts) => {
                this.scheduleSearch(opts?.clearResult);
            }),
            ...(query.refreshOnColumnsChange
                ? [
                    reaction(() => query.fieldsKeyHash, () => {
                        this.clearResultBeforeRefresh();
                        this.moveToFirstPage();
                    }),
                ]
                : []),
        ];
        return () => {
            disposers.forEach((d) => d());
        };
    }
    getItem(key) {
        const keyedItem = this.result.get(key);
        return keyedItem?.item;
    }
    getKeyedItem(key) {
        return this.result.get(key);
    }
    getKeyedItemByIndex(index) {
        return this.result.keyedItems[index];
    }
    get keyedItems() {
        return this.result.keyedItems;
    }
    addRefreshPromise(createPromise) {
        return this.refreshPromises.addPromise(createPromise);
    }
    _commonDynamicBiParams(query = this.result.originQuery) {
        const { result, fqdn } = this;
        return {
            ...fqdn,
            currentFilters: JSON.stringify(query.filtersKey),
            currentSortOrder: query.sort?.length
                ? JSON.stringify(query.sort)
                : undefined,
            filteredListSize: result.total,
            initialItems: result.size,
            numSortedColumns: query.sort?.length,
        };
    }
    get showInitialLoader() {
        const { initTask: { status: { isLoading }, }, initialPromises: { isLoadingPromises }, } = this;
        return isLoading || isLoadingPromises;
    }
    get showRefreshLoader() {
        const { refreshPromises: { isLoadingPromises }, result: { status }, lifecycleState, } = this;
        return (isLoadingPromises ||
            Boolean(status.isFetching && lifecycleState === 'refresh'));
    }
    get showEmptyState() {
        const { lifecycleState, result: { isEmpty }, } = this;
        return lifecycleState === 'result' && isEmpty;
    }
    get showErrorState() {
        const { result: { status }, lifecycleState, } = this;
        return (!status.isFetching && status.isError && lifecycleState !== 'fetchMore');
    }
    get topNotification() {
        const { _topNotification } = this;
        return {
            set text(t) {
                _topNotification.set(t);
            },
            get text() {
                return runInAction(() => _topNotification.data);
            },
            get show() {
                return runInAction(() => _topNotification.show);
            },
        };
    }
}
//# sourceMappingURL=CollectionState.js.map