import { SortOrder } from '../../AdaptableState/Common/Enums';
import * as InternalRedux from '../../Redux/ActionsReducers/InternalRedux';
import { getDateComparatorForGridCell, getGenericComparatorForGridCell, getNumericComparatorForGridCell, sortCellValueArrayNumeric, sortCellValueArrayDates, sortCellValueArray, } from '../../Utilities/Extensions/ArrayExtensions';
import { multisort } from '@infinite-table/infinite-react';
import { convertAdaptableStyleToCSS } from '../../Utilities/Helpers/StyleHelper';
import UIHelper from '../../View/UIHelper';
import { ApiBase } from '../Implementation/ApiBase';
export class GridInternalApi extends ApiBase {
    /**
     * Gets all distinct display values in the Column for given ColumnId
     * @param columnId Column to check
     */
    getDistinctDisplayValuesForColumnOld(columnId) {
        const abColumn = this.getColumnApi().getColumnWithColumnId(columnId);
        if (abColumn == undefined) {
            return [];
        }
        const returnValues = this._adaptable.getDistinctGridCellsForColumn(abColumn);
        return this.sortDistinctValues(returnValues, abColumn).map((cv) => {
            return cv.normalisedValue;
        });
    }
    /**
     * Gets all distinct Filter values for the Column with the given ColumnId
     * used for Floating Filter and Column Header filter
     * either returns a list of values or al ist a list of values with count
     *
     * This is a general method, and it is used in:
     * - Column Filters
     * - Query Builder
     * - Permitted Values
     *
     * @param columnId Column to check
     */
    async getDistinctFilterDisplayValuesForColumn(options) {
        const abColumn = this.getColumnApi().getColumnWithColumnId(options.columnId);
        if (abColumn == undefined) {
            return { values: [] };
        }
        const inFilterValues = await this._adaptable.getDistinctFilterValuesForColumn({
            column: abColumn,
            get currentSearchValue() {
                return options.currentSearchValue;
            },
            previousResult: options.previousResult,
        });
        return inFilterValues || { values: [] };
    }
    async getDistinctValuesForColumn(columnId) {
        const abColumn = this.getColumnApi().getColumnWithColumnId(columnId);
        if (!abColumn) {
            return undefined;
        }
        const gridCells = await this._adaptable.getDistinctValuesForColumn(abColumn);
        return gridCells;
    }
    async getDistinctEditDisplayValuesForColumn(options) {
        const abColumn = this.getColumnApi().getColumnWithColumnId(options.columnId);
        if (!abColumn) {
            return undefined;
        }
        const valueOptions = await this._adaptable.getDistinctEditValuesForColumn({
            column: abColumn,
            gridCell: options.gridCell,
            get currentSearchValue() {
                return options.currentSearchValue;
            },
        });
        return valueOptions || [];
    }
    /**
     * Gets all distinct raw values in Column. Values are sorted.
     * @param columnId Column to check
     */
    getDistinctRawValuesForColumn(columnId) {
        const abColumn = this.getColumnApi().getColumnWithColumnId(columnId);
        if (abColumn == undefined) {
            return [];
        }
        return this.sortDistinctValues(this.getUnsortedDistinctRawValuesForColumn(columnId), abColumn).map((cv) => {
            return cv.rawValue;
        });
    }
    /**
     * Gets all distinct raw values in Column. Values are un-sorted.
     * @param columnId Column to check
     */
    getUnsortedDistinctRawValuesForColumn(columnId) {
        const abColumn = this.getColumnApi().getColumnWithColumnId(columnId);
        if (abColumn == undefined) {
            return [];
        }
        return this._adaptable.getDistinctGridCellsForColumn(abColumn);
    }
    getSortedRowNodesWithCurrentLayoutSortOrder(rowNodes) {
        const currentLayout = this.getLayoutApi().getCurrentLayout();
        const columnSorts = currentLayout.ColumnSorts || [];
        const columnApi = this.getColumnApi();
        const rowNodesMapToCells = new Map();
        const allRowNodes = [];
        // for each row node,let's create an object
        // with only those gridcells that are needed - meaning
        // for those column that are in the current sort
        const rowNodeCallback = (node) => {
            allRowNodes.push(node);
            const rowNodeCells = {};
            rowNodesMapToCells.set(node, rowNodeCells);
            columnSorts.forEach((columnSort, i) => {
                const { ColumnId } = columnSort;
                const gridCell = this._adaptable.getGridCellFromRowNode(node, ColumnId);
                rowNodeCells[ColumnId] = gridCell;
            });
        };
        if (rowNodes) {
            rowNodes.forEach(rowNodeCallback);
        }
        else {
            this.getAgGridApi().forEachNode(rowNodeCallback);
        }
        if (!columnSorts.length) {
            return allRowNodes;
        }
        const sortInfo = columnSorts.map((columnSort) => {
            const { ColumnId } = columnSort;
            // NOTE: the comparator we pass to multisort should always sort in ascending order
            // that's why we pass SortOrder.Asc to the comparators
            const comparator = columnApi.hasNumberDataType(ColumnId)
                ? getNumericComparatorForGridCell(SortOrder.Asc)
                : columnApi.hasDateDataType(ColumnId)
                    ? getDateComparatorForGridCell(SortOrder.Asc)
                    : getGenericComparatorForGridCell(SortOrder.Asc);
            return {
                // but here we pass the actual direction of the column sort
                dir: columnSort.SortOrder === 'Asc' ? 1 : -1,
                fn: (rowNodeA, rowNodeB) => {
                    const gridCellA = rowNodesMapToCells.get(rowNodeA)?.[ColumnId];
                    const gridCellB = rowNodesMapToCells.get(rowNodeB)?.[ColumnId];
                    if (!gridCellA || !gridCellB) {
                        return 0;
                    }
                    return comparator(gridCellA, gridCellB);
                },
            };
        });
        return multisort(sortInfo, allRowNodes);
    }
    sortDistinctValues(returnValues, column) {
        // this does NOT into account Custom Sort - far too hard for now...
        let sortOrder = undefined;
        let columnSort = this.getGridApi().getColumnSortForColumn(column.columnId);
        if (columnSort && columnSort.SortOrder) {
            sortOrder =
                columnSort.SortOrder === 'Asc'
                    ? SortOrder.Asc
                    : columnSort.SortOrder === 'Desc'
                        ? SortOrder.Desc
                        : undefined;
        }
        if (sortOrder) {
            if (this.getColumnApi().hasNumberDataType(column?.columnId)) {
                returnValues = sortCellValueArrayNumeric(returnValues, sortOrder);
            }
            else if (this.getColumnApi().hasDateDataType(column?.columnId)) {
                returnValues = sortCellValueArrayDates(returnValues, sortOrder);
            }
            else {
                returnValues = sortCellValueArray(returnValues, sortOrder);
            }
        }
        return returnValues;
    }
    setColumns(columns) {
        this.dispatchAction(InternalRedux.SetColumns(columns));
    }
    setSelectedCells(selectedCellInfo) {
        this.dispatchAction(InternalRedux.SetSelectedCells(selectedCellInfo));
    }
    setSelectedRows(selectedRowInfo) {
        this.dispatchAction(InternalRedux.SetSelectedRows(selectedRowInfo));
    }
    buildStandaloneColumnHeader(column) {
        return this._adaptable.agGridMenuAdapter.buildStandaloneColumnHeader(column);
    }
    getRowHighlightStyle(params) {
        const primaryKeyValue = this.getGridApi().getPrimaryKeyValueForRowNode(params.node);
        const highlightRow = this.getAdaptableInternalApi()
            .getInternalState()
            .RowHighlightInfo?.find((highlightRow) => {
            return highlightRow.primaryKeyValue === primaryKeyValue;
        });
        if (highlightRow) {
            return convertAdaptableStyleToCSS(highlightRow.highlightStyle);
        }
    }
    getAlertRowStyle(params) {
        const alert = this.getAlertApi().internalApi.getAdaptableAlertWithHighlightRow(params.node);
        const highlightRow = alert?.alertDefinition?.AlertProperties?.HighlightRow;
        if (highlightRow) {
            return typeof highlightRow === 'object'
                ? convertAdaptableStyleToCSS(highlightRow)
                : {
                    backgroundColor: UIHelper.getColorByMessageType(alert.alertDefinition.MessageType),
                };
        }
        return null;
    }
    getAlertRowClass(params) {
        const alert = this.getAlertApi().internalApi.getAdaptableAlertWithHighlightRow(params.node);
        const highlightRow = alert?.alertDefinition?.AlertProperties?.HighlightRow;
        return typeof highlightRow === 'object' && highlightRow?.ClassName
            ? highlightRow?.ClassName
            : null;
    }
    getRowHighlightClass(params) {
        const primaryKeyValue = this.getGridApi().getPrimaryKeyValueForRowNode(params.node);
        const highlightRow = this.getAdaptableInternalApi()
            .getInternalState()
            .RowHighlightInfo?.find((highlightRow) => {
            return highlightRow.primaryKeyValue === primaryKeyValue;
        });
        return typeof highlightRow?.highlightStyle === 'object'
            ? highlightRow.highlightStyle.ClassName
            : null;
    }
    deriveSpecialColumnSettingsFromAgGridDefaultColDef() {
        const defaultColumnDefinition = this._adaptable.agGridAdapter.getDefaultColumnDefinition();
        return {
            Filterable: defaultColumnDefinition.filter,
            Resizable: defaultColumnDefinition.resizable,
            Groupable: defaultColumnDefinition.enableRowGroup,
            Sortable: defaultColumnDefinition.sortable,
            Pivotable: defaultColumnDefinition.enablePivot,
            Aggregatable: defaultColumnDefinition.enableValue,
            SuppressMenu: defaultColumnDefinition.suppressHeaderMenuButton,
            SuppressMovable: defaultColumnDefinition.suppressMovable,
            HeaderToolTip: defaultColumnDefinition.headerTooltip,
            Width: defaultColumnDefinition.width,
        };
    }
    hasCellEditableAccordingToEditOptions() {
        return typeof this.getEditOptions()?.isCellEditable === 'function';
    }
    /**
     * Returns UNDEFINED if no EditOptions.isCellEditable is provided, otherwise returns the result of the function
     *
     * DO NOT USE THIS METHOD DIRECTLY - use `GridApi.isCellEditable` instead
     */
    isCellEditableAccordingToEditOptions(gridCell, defaultColDefinitionEditableValue) {
        const cellEditable = this.getEditOptions()?.isCellEditable;
        if (cellEditable) {
            const cellEditableContext = {
                ...this.getAdaptableInternalApi().buildBaseContext(),
                gridCell,
                defaultColDefEditableValue: defaultColDefinitionEditableValue,
            };
            return cellEditable(cellEditableContext);
        }
        return undefined;
    }
    isRowFilterable(rowNode) {
        // first assess if the Row is filterable - if not, then return true so it appears in Grid
        const isRowFilterable = this.getOptionsApi().getFilterOptions().isRowFilterable;
        if (typeof isRowFilterable === 'function') {
            const rowFilterableContext = {
                ...this.getAdaptableApi().internalApi.buildBaseContext(),
                rowNode,
                data: rowNode.data,
            };
            if (!isRowFilterable(rowFilterableContext)) {
                return false;
            }
        }
        return true;
    }
}
