import { syncWithSharedQuery, TaskState, } from '@wix/bex-core';
import { action, makeObservable } from 'mobx';
import { createNestedOptimisticActions } from './createNestedOptimisticActions';
export class NestedCollectionFetchAllCollectionState {
    constructor(params) {
        this.initTask = new TaskState();
        const { levelDescriptor } = params;
        this.levelDescriptor = levelDescriptor;
        this.fetchAllState = params.fetchAllState;
        const { collection, parentFilter } = levelDescriptor.createCollection();
        createNestedOptimisticActions(params.fetchAllState.nestedCollectionState.container, collection, {
            levelDescriptor: this.levelDescriptor,
            parentFilter,
        });
        this.collection = collection;
        this.parentFilter = parentFilter;
        const { fetchAllState: { query: sharedQuery, options: { fetchAllThreshold }, }, } = this;
        const { query } = collection;
        syncWithSharedQuery(query, sharedQuery);
        this.originalLimit = query.pagination.limit;
        query.pagination.limit =
            levelDescriptor.fetchAllThreshold ?? fetchAllThreshold;
        makeObservable(this, {
            init: action,
            _populateCache: action,
            _emitFetchAllAbortSignalIfExceedsThreshold: action,
        });
    }
    _setWithParentFilterQueryData(itemsByParentId) {
        const { collection, parentFilter } = this;
        const { queryClient, _queryKey, query } = collection;
        const [queryName, computedQuery] = _queryKey(query.asComputed);
        for (const [parentId, subItems] of Array.from(itemsByParentId.entries())) {
            queryClient.setQueryData([
                queryName,
                {
                    ...computedQuery,
                    filtersKey: {
                        ...computedQuery.filtersKey,
                        ...(parentFilter && {
                            [parentFilter.name]: [parentId],
                        }),
                    },
                    limit: this.originalLimit,
                },
            ], {
                items: subItems,
                total: subItems.length,
                cursor: null,
                available: null,
            });
        }
    }
    _setSectionQueryData() {
        const { collection } = this;
        const { queryClient, _queryKey, query, result: { items }, } = collection;
        const [queryName, computedQuery] = _queryKey(query.asComputed);
        queryClient.setQueryData([
            queryName,
            {
                ...computedQuery,
                limit: this.originalLimit,
            },
        ], {
            items,
            total: items.length,
            cursor: null,
            available: null,
        });
    }
    _populateCacheWithEmptyResult(parents) {
        this._setWithParentFilterQueryData(new Map(parents.map(({ id }) => [id, []])));
    }
    _populateCache(next) {
        const { fetchAllState, collection, levelDescriptor: { parentKey }, } = this;
        const { result: { items, keyedItems }, } = collection;
        if (fetchAllState._otherLevelSameCollectionExists(this)) {
            this._populateCacheWithEmptyResult(keyedItems);
        }
        const itemsByParentId = items.reduce((groups, item) => {
            const parentId = String(parentKey(item) ?? null);
            const group = groups.get(parentId) ?? [];
            if (!groups.has(parentId)) {
                groups.set(parentId, group);
            }
            group.push(item);
            return groups;
        }, new Map([]));
        this._setWithParentFilterQueryData(itemsByParentId);
        this._setSectionQueryData();
        // also include current collection items treated as parents,
        // so if the next level doesn't have any items that their parent id is one of the current collection items,
        // there's still a cache entry for them
        next?._populateCacheWithEmptyResult(keyedItems);
    }
    _exceedsTotalThreshold() {
        const { fetchAllState: { options: { fetchAllThreshold }, total, }, } = this;
        return total > fetchAllThreshold;
    }
    _exceedsLevelThreshold() {
        const { levelDescriptor: { fetchAllThreshold }, collection, } = this;
        return (fetchAllThreshold != null && collection.result.total > fetchAllThreshold);
    }
    _exceedsThreshold() {
        return this._exceedsTotalThreshold() || this._exceedsLevelThreshold();
    }
    _emitFetchAllAbortSignalIfExceedsThreshold() {
        const { fetchAllState } = this;
        if (fetchAllState.isFetchAllAborted) {
            return;
        }
        if (this._exceedsThreshold()) {
            fetchAllState.events.emit('fetchAllAbortSignal');
            fetchAllState.isFetchAllAborted = true;
        }
    }
    init() {
        const { initTask, collection, fetchAllState } = this;
        const disposers = [collection.init({ skipFilterListeners: true })];
        initTask.runOnce(async () => {
            collection.initTask.runOnce();
            const collectionPromise = new Promise(async (resolve, reject) => {
                try {
                    await collection.initTask.status.promise;
                    this._emitFetchAllAbortSignalIfExceedsThreshold();
                    resolve();
                }
                catch (e) {
                    reject(e);
                }
            });
            await Promise.race([fetchAllState.fetchedAllPromise, collectionPromise]);
        });
        return () => {
            disposers.forEach((d) => d());
        };
    }
}
//# sourceMappingURL=NestedCollectionFetchAllCollectionState.js.map