import { TaskState, withoutDefaults, } from '@wix/bex-core';
import { action, computed, makeObservable, reaction } from 'mobx';
import { CustomColumnsHistoryState } from './CustomColumnsHistoryState';
import { indexOfCompareFn } from './indexOfCompareFn';
import { EventEmitter } from 'events';
import { cairoCustomizeColumnsInSidePanel, cairoFiltersPanelUsed, } from '@wix/bex-core/bi';
import { CustomColumnsPanelStateBase } from './CustomColumnsPanelStateBase';
// Legacy `custom-columns` stays bound to non-spreadsheet layouts so existing
// stored values keep their meaning; spreadsheet uses a sibling key.
export const CUSTOM_COLUMNS_KEY = 'custom-columns';
export const SPREADSHEET_CUSTOM_COLUMNS_KEY = 'spreadsheet-custom-columns';
const getCustomColumnsKeyForLayout = (layout) => layout === 'spreadsheet'
    ? SPREADSHEET_CUSTOM_COLUMNS_KEY
    : CUSTOM_COLUMNS_KEY;
const asStoredColumnsOrNull = (customColumnsResult) => Array.isArray(customColumnsResult)
    ? customColumnsResult
    : null;
export class CustomColumnsState extends CustomColumnsPanelStateBase {
    constructor(params) {
        super({
            container: params.container,
            queryName: [
                params.container.environment.componentId,
                params.table.collection.queryName,
                'customColumns',
            ]
                .filter(Boolean)
                .join('/'),
        });
        this._performedCustomColumnsActions = new Map();
        this.initTask = new TaskState();
        this._touched = false;
        this.events = new EventEmitter();
        this.table = params.table;
        if (params.dragAndDrop) {
            const { CustomColumnsDragAndDropState } = params.dragAndDrop;
            this.dragAndDrop = new CustomColumnsDragAndDropState({
                customColumns: this,
                dataSource: {
                    get columns() {
                        return params.table.columns;
                    },
                    selectedColumns: params.table.selectedColumns,
                },
                components: params.dragAndDrop,
                modalsContainerRef: params.modalsContainerRef,
            });
        }
        this.customColumnsHistory = this.table.collection.persistQueryToUrl
            ? new CustomColumnsHistoryState({
                customColumns: this,
                history: params.container.history,
            })
            : null;
        this.initBaseObservables();
        makeObservable(this, {
            persistCustomColumns: action.bound,
            toSelectedColumns: computed,
            selectedOrderedColumns: computed,
            init: action,
        });
    }
    get orderedColumns() {
        const { dragAndDrop, table } = this;
        const { columns, customColumnsState } = table;
        if (customColumnsState?.dragAndDrop && dragAndDrop) {
            return dragAndDrop.orderedColumns;
        }
        return columns;
    }
    get selectedOrderedColumns() {
        const { orderedColumns, table: { selectedColumns: { value: selectedColumns }, }, } = this;
        return orderedColumns.filter((column) => selectedColumns.some((selectedColumn) => selectedColumn.id === column.id && selectedColumn.isSelected));
    }
    async persistCustomColumns() {
        const { table } = this;
        const { dataCapsule, viewsProps } = table;
        // if views enabled columns persists inside the view instead of data capsule
        if (viewsProps) {
            return;
        }
        this._touched = true;
        await dataCapsule.setItem(getCustomColumnsKeyForLayout(table.layoutState?.viewType), table.selectedColumns.value);
    }
    registerSelectedColumnChange() {
        const { table } = this;
        table.selectedColumns.events.on('refresh', this.persistCustomColumns);
        return () => {
            table.selectedColumns.events.off('refresh', this.persistCustomColumns);
        };
    }
    mergeNewColumns() {
        const { table: { columns, selectedColumns, _toStoredColumn, customColumnsState }, _didFirstColumnsMerge, } = this;
        const defaultHidden = !_didFirstColumnsMerge && !!selectedColumns.value.length;
        const mergeTargetSelectedColumns = [...selectedColumns.value];
        if (customColumnsState?.dragAndDrop == null) {
            return [
                ...mergeTargetSelectedColumns.filter((s) => columns.every((c) => s.id !== c.id)),
                ...columns.map((c) => {
                    const existing = selectedColumns.value.find((s) => s.id === c.id);
                    if (existing == null) {
                        return _toStoredColumn(c);
                    }
                    return {
                        ...existing,
                        isSelected: c.hideable === false ||
                            c.hiddenFromCustomColumnsSelection ||
                            existing.isSelected,
                    };
                }),
            ];
        }
        const diffColumns = columns
            .filter((c) => selectedColumns.value.every((s) => s.id !== c.id))
            .map((c) => _toStoredColumn({
            ...c,
            defaultHidden: defaultHidden || c.defaultHidden,
        }));
        // place new columns according to their order in the original columns prop
        diffColumns.forEach((d) => {
            const originalIndex = columns.findIndex((c) => c.id === d.id);
            const afterId = originalIndex > 0 ? columns[originalIndex - 1].id : null;
            const afterIndex = afterId != null
                ? mergeTargetSelectedColumns.findIndex((c) => c.id === afterId) + 1
                : originalIndex === 0
                    ? 0
                    : mergeTargetSelectedColumns.length;
            mergeTargetSelectedColumns.splice(afterIndex, 0, d);
        });
        this._didFirstColumnsMerge = true;
        return mergeTargetSelectedColumns;
    }
    getStickyColumnsCount({ stickyColumns }) {
        const { dragAndDrop, table: { selectedColumns: { value: selectedColumns }, }, } = this;
        if (dragAndDrop == null) {
            return stickyColumns;
        }
        const { reorderDisabled: reorderDisabledColumns } = dragAndDrop.orderedColumnsByReorderDisabled;
        if (!reorderDisabledColumns.length) {
            return stickyColumns;
        }
        return dragAndDrop.orderedColumnsByReorderDisabled.reorderDisabled.filter((column) => selectedColumns.some((selectedColumn) => selectedColumn.id === column.id && selectedColumn.isSelected)).length;
    }
    get toSelectedColumns() {
        const { select, table: { selectedColumns: { value }, }, } = this;
        return value.map(({ id }) => ({
            id,
            isSelected: select.isChecked(id),
        }));
    }
    get selectionChanged() {
        const { toSelectedColumns, table: { selectedColumns: { value }, }, } = this;
        if (value.length !== toSelectedColumns.length) {
            return true;
        }
        for (let i = 0; i < value.length; i++) {
            const a = value[i];
            const b = toSelectedColumns[i];
            if (a.id !== b.id || a.isSelected !== b.isSelected) {
                return true;
            }
        }
        return false;
    }
    init() {
        const { table } = this;
        table.customColumnsStateInitialized = true;
        const disposers = [
            reaction(() => this.table.selectedColumns.value, async (selectedColumns) => {
                const { select, columnsCollection } = this;
                select.clear();
                select.forceSetMany(selectedColumns.map(({ id, isSelected }) => ({
                    value: { id },
                    status: isSelected ? select.finalStatus : select.startStatus,
                })));
                await columnsCollection.refreshAllPages();
            }, {
                fireImmediately: true,
            }),
            reaction(() => this.select.selectedValues, () => {
                const { table: { selectedColumns }, } = this;
                if (!this.selectionChanged) {
                    return;
                }
                selectedColumns.setValue(this.toSelectedColumns, {
                    emitEvents: ['change', 'beforeRefresh', 'refresh'],
                });
            }),
            this.registerSelectedColumnChange(),
            this.columnsCollection.init(),
            this.dragAndDrop?.init(),
            this.customColumnsHistory?.init(),
            reaction(() => this.table.layoutState?.viewType, async (newLayout, prevLayout) => {
                if (!newLayout || !prevLayout || newLayout === prevLayout) {
                    return;
                }
                if (this.table._cacheRestoredLayout === newLayout) {
                    return;
                }
                // Views path is handled by CollectionViewsState's own reaction.
                if (this.table.viewsProps) {
                    return;
                }
                const stored = await this._getStoredCustomColumns();
                if (stored == null) {
                    this.table.selectedColumns.reset();
                }
                else {
                    await this._fetchFromStorage();
                }
            }),
        ];
        return () => {
            disposers.forEach((disposer) => disposer?.());
        };
    }
    async _getStoredCustomColumns() {
        const { dataCapsule } = this.table;
        try {
            const storedValue = await asStoredColumnsOrNull(await dataCapsule.getItem(getCustomColumnsKeyForLayout(this.table.layoutState?.viewType)));
            this._touched = storedValue != null;
            return storedValue;
        }
        catch (e) {
            if (e instanceof Error && e.message === 'Key was not found in capsule') {
                return null;
            }
            throw e;
        }
    }
    _tableInit() {
        const { initTask, table: { multi: { collections }, }, } = this;
        initTask.runOnce(async () => {
            try {
                const beforeInitialFetchListeners = this.events.listeners('beforeInitialFetch');
                await Promise.all(beforeInitialFetchListeners.map((l) => l()));
                await this._fetchFromStorage();
            }
            catch (e) {
                console.error(e);
            }
        });
        collections.forEach((collection) => {
            if (collection.query.refreshOnColumnsChange) {
                collection.hooks.addListener('beforeInitialFetch', () => initTask.status.promise);
            }
        });
    }
    async _fetchFromStorage() {
        const { table: { viewsProps, columns, selectedColumns, customColumnsState, _toStoredColumn, }, } = this;
        if (viewsProps) {
            return;
        }
        if (customColumnsState?.customColumnsHistory?.hasUrlColumnsForCurrentLayout) {
            return;
        }
        const storedCustomColumns = await this._getStoredCustomColumns();
        if (storedCustomColumns == null) {
            return;
        }
        const selectedColumnsFromStorage = columns.map((tableColumn) => {
            const existing = storedCustomColumns.find((c) => c?.id === tableColumn.id);
            // If the column is not found in the stored columns, it means it was added after the last visit, so its "isSelected" state should be calculated based on the column prop passed via code
            if (existing == null) {
                return _toStoredColumn(tableColumn);
            }
            // If the column is found in the stored columns, it means it was already configured by the user, so its  "isSelected" state should be taken from the stored columns
            const { id, name, hideable, hiddenFromCustomColumnsSelection } = tableColumn;
            return {
                id,
                name,
                isSelected: storedCustomColumns.some((c) => c?.id === id && c?.isSelected !== false) ||
                    !hideable ||
                    hiddenFromCustomColumnsSelection === true,
            };
        });
        selectedColumns.setValue(customColumnsState?.dragAndDrop
            ? selectedColumnsFromStorage.sort(indexOfCompareFn(storedCustomColumns, (e) => e?.id))
            : selectedColumnsFromStorage);
    }
    customizeColumnsInSidePanelBi({ actionName, }) {
        if (this._performedCustomColumnsActions.get(actionName)) {
            return;
        }
        this._performedCustomColumnsActions.set(actionName, true);
        this.table.reportBi(withoutDefaults(cairoCustomizeColumnsInSidePanel)({
            actionName,
        }));
    }
    get showFieldTypeIcons() {
        return this.table.showFieldTypeIcons;
    }
    get collectionDataExtension() {
        return this.table.collectionDataExtension;
    }
    get sidePanelTransition() {
        return this.table.sidePanel.transition;
    }
    onClose() {
        this.table.reportBi(withoutDefaults(cairoFiltersPanelUsed)({
            isOpened: false,
            feature: 'Custom Columns',
            origin: 'X Button',
        }));
        this.table.sidePanel.close();
    }
}
//# sourceMappingURL=CustomColumnsState.js.map