import { getMemoOptions, makeStateUpdater, memo } from '../utils';
//
const getDefaultRowPinningState = () => ({
    top: [],
    bottom: [],
});
export const RowPinning = {
    getInitialState: (state) => {
        return {
            rowPinning: getDefaultRowPinningState(),
            ...state,
        };
    },
    getDefaultOptions: (table) => {
        return {
            onRowPinningChange: makeStateUpdater('rowPinning', table),
        };
    },
    createRow: (row, table) => {
        row.pin = (position, includeLeafRows, includeParentRows) => {
            const leafRowIds = includeLeafRows ? row.getLeafRows().map(({ id }) => id) : [];
            const parentRowIds = includeParentRows ? row.getParentRows().map(({ id }) => id) : [];
            const rowIds = new Set([...parentRowIds, row.id, ...leafRowIds]);
            table.setRowPinning((old) => {
                var _a, _b, _c, _d, _e, _f;
                if (position === 'bottom') {
                    return {
                        top: ((_a = old === null || old === void 0 ? void 0 : old.top) !== null && _a !== void 0 ? _a : []).filter((d) => !(rowIds === null || rowIds === void 0 ? void 0 : rowIds.has(d))),
                        bottom: [...((_b = old === null || old === void 0 ? void 0 : old.bottom) !== null && _b !== void 0 ? _b : []).filter((d) => !(rowIds === null || rowIds === void 0 ? void 0 : rowIds.has(d))), ...Array.from(rowIds)],
                    };
                }
                if (position === 'top') {
                    return {
                        top: [...((_c = old === null || old === void 0 ? void 0 : old.top) !== null && _c !== void 0 ? _c : []).filter((d) => !(rowIds === null || rowIds === void 0 ? void 0 : rowIds.has(d))), ...Array.from(rowIds)],
                        bottom: ((_d = old === null || old === void 0 ? void 0 : old.bottom) !== null && _d !== void 0 ? _d : []).filter((d) => !(rowIds === null || rowIds === void 0 ? void 0 : rowIds.has(d))),
                    };
                }
                return {
                    top: ((_e = old === null || old === void 0 ? void 0 : old.top) !== null && _e !== void 0 ? _e : []).filter((d) => !(rowIds === null || rowIds === void 0 ? void 0 : rowIds.has(d))),
                    bottom: ((_f = old === null || old === void 0 ? void 0 : old.bottom) !== null && _f !== void 0 ? _f : []).filter((d) => !(rowIds === null || rowIds === void 0 ? void 0 : rowIds.has(d))),
                };
            });
        };
        row.getCanPin = () => {
            var _a;
            const { enableRowPinning, enablePinning } = table.options;
            if (typeof enableRowPinning === 'function') {
                return enableRowPinning(row);
            }
            return (_a = enableRowPinning !== null && enableRowPinning !== void 0 ? enableRowPinning : enablePinning) !== null && _a !== void 0 ? _a : true;
        };
        row.getIsPinned = () => {
            const rowIds = [row.id];
            const { top, bottom } = table.getState().rowPinning;
            const isTop = rowIds.some((d) => top === null || top === void 0 ? void 0 : top.includes(d));
            const isBottom = rowIds.some((d) => bottom === null || bottom === void 0 ? void 0 : bottom.includes(d));
            if (isTop)
                return 'top';
            if (isBottom)
                return 'bottom';
            return false;
        };
        row.getPinnedIndex = () => {
            var _a, _b;
            const position = row.getIsPinned();
            if (!position)
                return -1;
            const visiblePinnedRowIds = (_a = (position === 'top' ? table.getTopRows() : table.getBottomRows())) === null || _a === void 0 ? void 0 : _a.map(({ id }) => id);
            return (_b = visiblePinnedRowIds === null || visiblePinnedRowIds === void 0 ? void 0 : visiblePinnedRowIds.indexOf(row.id)) !== null && _b !== void 0 ? _b : -1;
        };
    },
    createTable: (table) => {
        table.setRowPinning = (updater) => { var _a, _b; return (_b = (_a = table.options).onRowPinningChange) === null || _b === void 0 ? void 0 : _b.call(_a, updater); };
        table.resetRowPinning = (defaultState) => {
            var _a, _b;
            return table.setRowPinning(defaultState
                ? getDefaultRowPinningState()
                : ((_b = (_a = table.initialState) === null || _a === void 0 ? void 0 : _a.rowPinning) !== null && _b !== void 0 ? _b : getDefaultRowPinningState()));
        };
        table.getIsSomeRowsPinned = (position) => {
            var _a, _b, _c;
            const pinningState = table.getState().rowPinning;
            if (!position) {
                return Boolean(((_a = pinningState.top) === null || _a === void 0 ? void 0 : _a.length) || ((_b = pinningState.bottom) === null || _b === void 0 ? void 0 : _b.length));
            }
            return Boolean((_c = pinningState[position]) === null || _c === void 0 ? void 0 : _c.length);
        };
        table._getPinnedRows = (visibleRows, pinnedRowIds, position) => {
            var _a;
            const rows = ((_a = table.options.keepPinnedRows) !== null && _a !== void 0 ? _a : true)
                ? // get all rows that are pinned even if they would not be otherwise visible
                    // account for expanded parent rows, but not pagination or filtering
                    (pinnedRowIds !== null && pinnedRowIds !== void 0 ? pinnedRowIds : []).map((rowId) => {
                        const row = table.getRow(rowId, true);
                        return row.getIsAllParentsExpanded() ? row : null;
                    })
                : // else get only visible rows that are pinned
                    (pinnedRowIds !== null && pinnedRowIds !== void 0 ? pinnedRowIds : []).map((rowId) => visibleRows.find((row) => row.id === rowId));
            return rows.filter(Boolean).map((d) => ({ ...d, position }));
        };
        table.getTopRows = memo(() => [table.getRowModel().rows, table.getState().rowPinning.top], (allRows, topPinnedRowIds) => table._getPinnedRows(allRows, topPinnedRowIds, 'top'), getMemoOptions(table.options, 'debugRows', 'getTopRows'));
        table.getBottomRows = memo(() => [table.getRowModel().rows, table.getState().rowPinning.bottom], (allRows, bottomPinnedRowIds) => table._getPinnedRows(allRows, bottomPinnedRowIds, 'bottom'), getMemoOptions(table.options, 'debugRows', 'getBottomRows'));
        table.getCenterRows = memo(() => [
            table.getRowModel().rows,
            table.getState().rowPinning.top,
            table.getState().rowPinning.bottom,
        ], (allRows, top, bottom) => {
            const topAndBottom = new Set([...(top !== null && top !== void 0 ? top : []), ...(bottom !== null && bottom !== void 0 ? bottom : [])]);
            return allRows.filter((d) => !topAndBottom.has(d.id));
        }, getMemoOptions(table.options, 'debugRows', 'getCenterRows'));
    },
};
