import { action, computed, makeObservable, observable } from 'mobx';
import { initialCursor, Pagination } from './Pagination';
import mapValues from 'lodash/mapValues';
import pickBy from 'lodash/pickBy';
import { EventEmitter } from 'events';
import { StringFilterState } from './StringFilterState';
import { SortState } from './SortState';
import { addEventListener } from '../util';
import { hashQueryKey } from 'react-query/core';
import { QueryHistoryState } from './QueryHistoryState';
export class QueryState {
    constructor(params) {
        this.events = new EventEmitter();
        this._columns = observable.set([], { deep: false });
        this._fields = observable.set([], { deep: false });
        this.addFilterListeners = (filter) => {
            if (this._skipFilterListeners) {
                return [];
            }
            return [
                addEventListener(filter.events, 'change', () => this.events.emit('change')),
                addEventListener(filter.events, 'refresh', () => this.events.emit('refresh')),
                addEventListener(filter.events, 'scheduleRefresh', () => this.events.emit('scheduleRefresh')),
            ];
        };
        this.refreshOnColumnsChange = params.refreshOnColumnsChange;
        this.pagination = new Pagination({ limit: params.limit });
        this.history = params.history;
        this.sort = new SortState({});
        this.customFilters = mapValues(params.filters, (filter, name) => {
            if (filter) {
                filter.name = name;
            }
            return filter;
        });
        this._filters = {
            ...this.customFilters,
            search: params.search ??
                new StringFilterState({
                    name: 'search',
                    defaultValue: '',
                }),
        };
        this.queryHistory = this.history
            ? new QueryHistoryState({
                query: this,
                history: this.history,
            })
            : null;
        makeObservable(this, {
            changeLimit: action,
            reset: action,
            toComputed: action,
            _filters: observable.ref,
            customFilters: observable.ref,
            addFilterIfNeeded: action,
            asComputed: computed,
            activeFilters: computed,
            nonPersistentFilters: computed,
            nonPersistentFiltersEntries: computed,
            nonPersistentFiltersMap: computed,
            activeFiltersEntries: computed,
            activeFiltersCount: computed,
            hasActiveFilters: computed,
            hasNonPersistentActiveFilters: computed,
            resetFilters: action.bound,
            resetPage: action.bound,
            filtersKey: computed,
            filtersKeyHash: computed,
            fields: computed,
            fieldsKey: computed,
            fieldsKeyHash: computed,
            hasNonPersistentCustomFilters: computed,
            nonPersistentCustomFiltersCount: computed,
        });
    }
    init({ skipFilterListeners } = {}) {
        this._skipFilterListeners = skipFilterListeners;
        const disposers = [
            ...Object.values(this.filtersEntries).flatMap(([_, filter]) => {
                return this.addFilterListeners(filter);
            }),
            this.queryHistory?.init(),
        ];
        return () => {
            disposers.forEach((d) => d?.());
        };
    }
    get limit() {
        return this.pagination.limit;
    }
    get fields() {
        return [
            ...Array.from(this._columns.values()),
            ...Array.from(this._fields.values()),
        ];
    }
    get fieldsKey() {
        return this.fields.sort();
    }
    get fieldsKeyHash() {
        return this.fieldsKey.join('');
    }
    get filtersKey() {
        const { customFilters } = this;
        return mapValues(customFilters, (filter) => filter != null
            ? filter.isEmpty
                ? null
                : filter.toArray.map(filter.itemKey)
            : undefined);
    }
    get filtersKeyHash() {
        return hashQueryKey([this.filtersKey]);
    }
    changeLimit(limit) {
        this.pagination.changeLimit(limit);
    }
    reset() {
        this.pagination.moveToPage(0);
    }
    _toComputed() {
        const { pagination, customFilters, nonPersistentFiltersMap, search: searchFilter, sort, hasActiveFilters, hasNonPersistentActiveFilters, fields, refreshOnColumnsChange, } = this;
        const { page, limit, offset, cursor } = pagination;
        const filters = mapValues(customFilters, (filter) => filter != null ? (filter.isEmpty ? undefined : filter.value) : filter);
        const rawFilters = mapValues(customFilters, (filter) => (filter != null ? filter.value : filter));
        const filtersKey = mapValues(customFilters, (filter) => filter != null
            ? filter.isEmpty
                ? null
                : filter.toArray.map(filter.itemKey)
            : undefined);
        const nonPersistentFiltersKey = mapValues(nonPersistentFiltersMap, (filter) => filter != null
            ? filter.isEmpty
                ? null
                : filter.toArray.map(filter.itemKey)
            : undefined);
        const rawSearch = searchFilter.value;
        const search = searchFilter.isEmpty ? undefined : rawSearch;
        return {
            page,
            limit,
            sort: sort.value.map(({ field: fieldName, direction: order }) => ({
                fieldName,
                order,
            })),
            offset,
            cursor: cursor == null || typeof cursor === 'string' ? cursor : null,
            rawFilters,
            filtersKey,
            nonPersistentFiltersKey,
            filters,
            rawSearch,
            search,
            hasActiveFilters,
            hasNonPersistentActiveFilters,
            columns: fields,
            fields: refreshOnColumnsChange
                ? fields
                : Array.from(this._fields.values()),
        };
    }
    toComputed() {
        return this._toComputed();
    }
    get asComputed() {
        return this._toComputed();
    }
    get filtersArray() {
        const { _filters } = this;
        return Object.values(_filters).filter((e) => e != null);
    }
    get nonPersistentFilters() {
        const { filtersArray } = this;
        return filtersArray.filter((filter) => !filter.persistent);
    }
    get filtersEntries() {
        const { _filters } = this;
        return Object.entries(_filters).filter((entry) => entry[1] != null);
    }
    get nonPersistentFiltersEntries() {
        const { filtersEntries } = this;
        return filtersEntries.filter(([_, filter]) => !filter.persistent);
    }
    get nonPersistentCustomFiltersCount() {
        return Object.values(this.nonPersistentCustomFilters).reduce((size, filter) => size + filter.size, 0);
    }
    get hasNonPersistentCustomFilters() {
        return this.nonPersistentCustomFiltersCount > 0;
    }
    get nonPersistentCustomFilters() {
        const { customFilters } = this;
        return Object.values(customFilters).filter((e) => e != null && !e?.persistent);
    }
    get nonPersistentFiltersMap() {
        const { customFilters } = this;
        return pickBy(customFilters, (filter) => !filter?.persistent);
    }
    get activeFiltersEntries() {
        const { nonPersistentFiltersEntries } = this;
        return nonPersistentFiltersEntries.filter(([_, filter]) => !filter.isEmpty);
    }
    get activeFilters() {
        const { nonPersistentFilters } = this;
        return nonPersistentFilters.filter((filter) => !filter.isEmpty);
    }
    get activeFiltersCount() {
        return this.activeFilters.reduce((size, filter) => size + filter.size, 0);
    }
    get hasActiveFilters() {
        return (this.filtersArray.reduce((size, filter) => size + filter.size, 0) > 0);
    }
    get hasNonPersistentActiveFilters() {
        return this.activeFiltersCount > 0;
    }
    get hasActiveSort() {
        return this.sort.value.length > 0;
    }
    addFilterIfNeeded(key, filter) {
        if (filter === undefined || this._filters[key]) {
            return;
        }
        this._filters = {
            ...this._filters,
            [key]: filter,
        };
        this.customFilters = {
            ...this.customFilters,
            [key]: filter,
        };
        const disposers = this.addFilterListeners(filter);
        return () => {
            disposers.forEach((d) => d?.());
        };
    }
    resetFilters(options) {
        const { nonPersistentFilters } = this;
        nonPersistentFilters.forEach((filter) => {
            filter?.reset(options);
        });
    }
    resetCustomFilters(options) {
        const { nonPersistentCustomFilters } = this;
        nonPersistentCustomFilters.forEach((filter) => {
            filter?.reset(options);
        });
    }
    resetSorting(params = {}) {
        this.sort.reset(params);
    }
    resetPage() {
        const { pagination } = this;
        pagination.page = 1;
        pagination.cursor = initialCursor;
    }
    get search() {
        return this._filters.search;
    }
    get filters() {
        return this._filters;
    }
}
//# sourceMappingURL=QueryState.js.map