"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.useTableData = void 0;
const vue_1 = require("vue");
const vooks_1 = require("vooks");
const treemate_1 = require("treemate");
const utils_1 = require("./utils");
const _utils_1 = require("../../_utils");
// useTableData combines filter, sorter and pagination
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
function useTableData(props, { dataRelatedColsRef }) {
    const selectionColumnRef = vue_1.computed(() => {
        return (props.columns.find((col) => {
            if (col.type === 'selection') {
                return true;
            }
            return false;
        }) || null);
    });
    const treeMateRef = vue_1.computed(() => {
        const { childrenKey } = props;
        return treemate_1.createTreeMate(props.data, {
            getKey: props.rowKey,
            getChildren: (rowData) => rowData[childrenKey],
            getDisabled: (rowData) => {
                var _a, _b;
                if ((_b = (_a = selectionColumnRef.value) === null || _a === void 0 ? void 0 : _a.disabled) === null || _b === void 0 ? void 0 : _b.call(_a, rowData)) {
                    return true;
                }
                return false;
            }
        });
    });
    const firstContentfulColIndexRef = vooks_1.useMemo(() => {
        const { columns } = props;
        const { length } = columns;
        for (let i = 0; i < length; ++i) {
            if (!columns[i].type) {
                return i;
            }
        }
        return 0;
    });
    const uncontrolledFilterStateRef = vue_1.ref({});
    const uncontrolledSortStateRef = vue_1.ref(null);
    const uncontrolledCurrentPageRef = vue_1.ref(1);
    const uncontrolledPageSizeRef = vue_1.ref(10);
    dataRelatedColsRef.value.forEach((column) => {
        var _a, _b;
        if (column.sorter !== undefined) {
            uncontrolledSortStateRef.value = {
                columnKey: column.key,
                sorter: column.sorter,
                order: (_a = column.defaultSortOrder) !== null && _a !== void 0 ? _a : false
            };
        }
        if (column.filter) {
            const defaultFilterOptionValues = column.defaultFilterOptionValues;
            if (column.filterMultiple) {
                uncontrolledFilterStateRef.value[column.key] =
                    defaultFilterOptionValues || [];
            }
            else if (defaultFilterOptionValues !== undefined) {
                // this branch is for compatibility, someone may use `values` in single filter mode
                uncontrolledFilterStateRef.value[column.key] =
                    defaultFilterOptionValues === null ? [] : defaultFilterOptionValues;
            }
            else {
                uncontrolledFilterStateRef.value[column.key] =
                    (_b = column.defaultFilterOptionValue) !== null && _b !== void 0 ? _b : null;
            }
        }
    });
    const controlledCurrentPageRef = vue_1.computed(() => {
        const { pagination } = props;
        if (pagination === false)
            return undefined;
        return pagination.page;
    });
    const controlledPageSizeRef = vue_1.computed(() => {
        const { pagination } = props;
        if (pagination === false)
            return undefined;
        return pagination.pageSize;
    });
    const mergedCurrentPageRef = vooks_1.useMergedState(controlledCurrentPageRef, uncontrolledCurrentPageRef);
    const mergedPageSizeRef = vooks_1.useMergedState(controlledPageSizeRef, uncontrolledPageSizeRef);
    const mergedPageCountRef = vue_1.computed(() => {
        const { pagination } = props;
        if (pagination) {
            const { pageCount } = pagination;
            if (pageCount !== undefined)
                return pageCount;
        }
        return undefined;
    });
    const mergedSortStateRef = vue_1.computed(() => {
        // If one of the columns's sort order is false or 'ascend' or 'descend',
        // the table's controll functionality should work in controlled manner.
        const columnsWithControlledSortOrder = dataRelatedColsRef.value.filter((column) => column.type !== 'selection' &&
            column.sorter !== undefined &&
            (column.sortOrder === 'ascend' ||
                column.sortOrder === 'descend' ||
                column.sortOrder === false));
        // if multiple column is controlled sortable, then we need to find a column with active sortOrder
        const columnToSort = columnsWithControlledSortOrder.filter((col) => col.sortOrder !== false)[0];
        if (columnToSort) {
            return {
                columnKey: columnToSort.key,
                // column to sort has controlled sorter
                // sorter && sort order won't be undefined
                // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                order: columnToSort.sortOrder,
                // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                sorter: columnToSort.sorter
            };
        }
        if (columnsWithControlledSortOrder.length)
            return null;
        return uncontrolledSortStateRef.value;
    });
    const mergedFilterStateRef = vue_1.computed(() => {
        const columnsWithControlledFilter = dataRelatedColsRef.value.filter((column) => {
            return (column.filterOptionValues !== undefined ||
                column.filterOptionValue !== undefined);
        });
        const controlledFilterState = {};
        columnsWithControlledFilter.forEach((column) => {
            if (column.type === 'selection' || column.type === 'expand')
                return;
            controlledFilterState[column.key] =
                column.filterOptionValues || column.filterOptionValue || null;
        });
        const activeFilters = Object.assign(utils_1.createShallowClonedObject(uncontrolledFilterStateRef.value), controlledFilterState);
        return activeFilters;
    });
    const filteredDataRef = vue_1.computed(() => {
        const mergedFilterState = mergedFilterStateRef.value;
        const { columns } = props;
        function createDefaultFilter(columnKey) {
            return (filterOptionValue, row) => !!~String(row[columnKey]).indexOf(String(filterOptionValue));
        }
        const { value: { treeNodes: data } } = treeMateRef;
        const columnEntries = [];
        columns.forEach((column) => {
            if (column.type === 'selection' ||
                column.type === 'expand' ||
                'children' in column) {
                return;
            }
            columnEntries.push([column.key, column]);
        });
        return data
            ? data.filter((tmNode) => {
                const { rawNode: row } = tmNode;
                // traverse all filters
                for (const [columnKey, column] of columnEntries) {
                    let activeFilterOptionValues = mergedFilterState[columnKey];
                    if (activeFilterOptionValues == null)
                        continue;
                    if (!Array.isArray(activeFilterOptionValues)) {
                        activeFilterOptionValues = [activeFilterOptionValues];
                    }
                    if (!activeFilterOptionValues.length)
                        continue;
                    // When async, filter won't be set, so data won't be filtered
                    const filter = column.filter === 'default'
                        ? createDefaultFilter(columnKey)
                        : column.filter;
                    if (column && typeof filter === 'function') {
                        if (column.filterMode === 'and') {
                            if (activeFilterOptionValues.some((filterOptionValue) => !filter(filterOptionValue, row))) {
                                return false;
                            }
                        }
                        else {
                            if (activeFilterOptionValues.some((filterOptionValue) => filter(filterOptionValue, row))) {
                                continue;
                            }
                            else {
                                return false;
                            }
                        }
                    }
                }
                return true;
            })
            : [];
    });
    const sortedDataRef = vue_1.computed(() => {
        const activeSorter = mergedSortStateRef.value;
        if (activeSorter) {
            // When async, mergedSortState.sorter should be true
            // and we sort nothing, just return the filtered data
            if (activeSorter.sorter === true || activeSorter.sorter === false) {
                return filteredDataRef.value;
            }
            const filteredData = filteredDataRef.value.slice(0);
            const columnKey = activeSorter.columnKey;
            // 1 for asc
            // -1 for desc
            const order = activeSorter.order;
            const sorter = activeSorter.sorter === undefined || activeSorter.sorter === 'default'
                ? (row1, row2) => {
                    const value1 = row1[columnKey];
                    const value2 = row2[columnKey];
                    if (typeof value1 === 'number' && typeof value2 === 'number') {
                        return value1 - value2;
                    }
                    else if (typeof value1 === 'string' &&
                        typeof value2 === 'string') {
                        return value1.localeCompare(value2);
                    }
                    return 0;
                }
                : activeSorter.sorter;
            return filteredData.sort((tmNode1, tmNode2) => utils_1.getFlagOfOrder(order) * sorter(tmNode1.rawNode, tmNode2.rawNode));
        }
        return filteredDataRef.value;
    });
    const paginatedDataRef = vue_1.computed(() => {
        if (props.remote)
            return treeMateRef.value.treeNodes;
        if (!props.pagination)
            return sortedDataRef.value;
        const pageSize = mergedPageSizeRef.value;
        const startIndex = (mergedCurrentPageRef.value - 1) * pageSize;
        return sortedDataRef.value.slice(startIndex, startIndex + pageSize);
    });
    const rawPaginatedDataRef = vue_1.computed(() => {
        return paginatedDataRef.value.map((tmNode) => tmNode.rawNode);
    });
    function mergedOnUpdatePage(page) {
        const { pagination } = props;
        if (pagination) {
            const { onChange, 'onUpdate:page': _onUpdatePage, onUpdatePage } = pagination;
            if (onChange)
                _utils_1.call(onChange, page);
            if (onUpdatePage)
                _utils_1.call(onUpdatePage, page);
            if (_onUpdatePage)
                _utils_1.call(_onUpdatePage, page);
            doUpdatePage(page);
        }
    }
    function mergedOnUpdatePageSize(pageSize) {
        const { pagination } = props;
        if (pagination) {
            const { onPageSizeChange, 'onUpdate:pageSize': onUpdatePageSize } = pagination;
            if (onPageSizeChange)
                _utils_1.call(onPageSizeChange, pageSize);
            if (onUpdatePageSize)
                _utils_1.call(onUpdatePageSize, pageSize);
            doUpdatePageSize(pageSize);
        }
    }
    const mergedItemCountRef = vue_1.computed(() => {
        if (props.remote) {
            const { pagination } = props;
            if (pagination) {
                const { itemCount } = pagination;
                if (itemCount !== undefined)
                    return itemCount;
            }
            return undefined;
        }
        return filteredDataRef.value.length;
    });
    const mergedPaginationRef = vue_1.computed(() => {
        return Object.assign(Object.assign({}, props.pagination), { 
            // reset deprecated methods
            onChange: undefined, onPageSizeChange: undefined, 'onUpdate:page': mergedOnUpdatePage, 'onUpdate:pageSize': mergedOnUpdatePageSize, 
            // writing merged props after pagination to avoid
            // pagination[key] === undefined
            // key still exists but value is undefined
            page: mergedCurrentPageRef.value, pageSize: mergedPageSizeRef.value, pageCount: mergedItemCountRef.value === undefined
                ? mergedPageCountRef.value
                : undefined, itemCount: mergedItemCountRef.value });
    });
    function doUpdatePage(page) {
        const { 'onUpdate:page': onUpdatePage, onPageChange } = props;
        if (onUpdatePage)
            _utils_1.call(onUpdatePage, page);
        if (onPageChange)
            _utils_1.call(onPageChange, page);
        uncontrolledCurrentPageRef.value = page;
    }
    function doUpdatePageSize(pageSize) {
        const { 'onUpdate:pageSize': onUpdatePageSize, onPageSizeChange } = props;
        if (onPageSizeChange)
            _utils_1.call(onPageSizeChange, pageSize);
        if (onUpdatePageSize)
            _utils_1.call(onUpdatePageSize, pageSize);
        uncontrolledPageSizeRef.value = pageSize;
    }
    function doUpdateSorter(sortState) {
        const { 'onUpdate:sorter': _onUpdateSorter, onUpdateSorter, onSorterChange } = props;
        if (_onUpdateSorter)
            _utils_1.call(_onUpdateSorter, sortState);
        if (onUpdateSorter)
            _utils_1.call(onUpdateSorter, sortState);
        if (onSorterChange)
            _utils_1.call(onSorterChange, sortState);
        uncontrolledSortStateRef.value = sortState;
    }
    function doUpdateFilters(filters, sourceColumn) {
        const { onUpdateFilters, 'onUpdate:filters': _onUpdateFilters, onFiltersChange } = props;
        if (onUpdateFilters)
            _utils_1.call(onUpdateFilters, filters, sourceColumn);
        if (_onUpdateFilters)
            _utils_1.call(_onUpdateFilters, filters, sourceColumn);
        if (onFiltersChange)
            _utils_1.call(onFiltersChange, filters, sourceColumn);
        uncontrolledFilterStateRef.value = filters;
    }
    function page(page) {
        doUpdatePage(page);
    }
    function sort(columnKey, order = 'ascend') {
        if (!columnKey) {
            clearSorter();
        }
        else {
            const columnToSort = dataRelatedColsRef.value.find((column) => column.type !== 'selection' &&
                column.type !== 'expand' &&
                column.key === columnKey);
            if (!columnToSort || !columnToSort.sorter)
                return;
            const sorter = columnToSort.sorter;
            doUpdateSorter({
                columnKey,
                sorter,
                order: order
            });
        }
    }
    function clearSorter() {
        doUpdateSorter(null);
    }
    function clearFilter() {
        clearFilters();
    }
    function clearFilters() {
        filters({});
    }
    function filters(filters) {
        filter(filters);
    }
    function filter(filters) {
        if (!filters) {
            doUpdateFilters({});
        }
        else if (filters) {
            doUpdateFilters(utils_1.createShallowClonedObject(filters));
        }
        else if (process.env.NODE_ENV !== 'production') {
            _utils_1.warn('data-table', '`filters` is not an object');
        }
    }
    return {
        treeMateRef,
        mergedCurrentPageRef,
        mergedPaginationRef,
        paginatedDataRef,
        rawPaginatedDataRef,
        mergedFilterStateRef,
        mergedSortStateRef: mergedSortStateRef,
        hoverKeyRef: vue_1.ref(null),
        selectionColumnRef,
        firstContentfulColIndexRef,
        doUpdateFilters,
        doUpdateSorter,
        doUpdatePageSize,
        doUpdatePage,
        // exported methods
        filter,
        filters,
        clearFilter,
        clearFilters,
        page,
        sort
    };
}
exports.useTableData = useTableData;
