import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/**
 * Copyright (c) 2020-present, Goldman Sachs
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { observer } from 'mobx-react-lite';
import { useState, useCallback, useEffect } from 'react';
import { DataGrid, } from '@finos/legend-lego/data-grid';
import { getRowDataFromExecutionResult, filterByOrOutValues, } from './QueryBuilderTDSResultShared.js';
import { QueryBuilderResultWavgAggregationState, } from '../../../stores/QueryBuilderResultState.js';
import { PRIMITIVE_TYPE, } from '@finos/legend-graph';
import { QueryBuilderTDSState } from '../../../stores/fetch-structure/tds/QueryBuilderTDSState.js';
import { DEFAULT_LOCALE } from '../../../graph-manager/QueryBuilderConst.js';
import { assertErrorThrown, guaranteeNonNullable, isBoolean, isNumber, isString, isValidURL, } from '@finos/legend-shared';
import { useApplicationStore } from '@finos/legend-application';
import { QUERY_BUILDER_TEST_ID } from '../../../__lib__/QueryBuilderTesting.js';
import { clsx, Modal, ModalBody, ModalHeader, ModalFooter, ModalFooterButton, Dialog, CustomSelectorInput, } from '@finos/legend-art';
import { getTDSColumnCustomizations, MAXIMUM_FRACTION_DIGITS, } from './QueryBuilderTDSSimpleGridResult.js';
export var QueryBuilderDataGridCustomAggregationFunction;
(function (QueryBuilderDataGridCustomAggregationFunction) {
    QueryBuilderDataGridCustomAggregationFunction["wavg"] = "wavg";
    QueryBuilderDataGridCustomAggregationFunction["WAVG"] = "WAVG";
})(QueryBuilderDataGridCustomAggregationFunction || (QueryBuilderDataGridCustomAggregationFunction = {}));
const getAggregationTDSColumnCustomizations = (result, columnName) => {
    const columnType = result.builder.columns.find((col) => col.name === columnName)?.type;
    switch (columnType) {
        case PRIMITIVE_TYPE.STRING:
            return {
                filter: 'agTextColumnFilter',
                allowedAggFuncs: ['count'],
            };
        case PRIMITIVE_TYPE.DATE:
        case PRIMITIVE_TYPE.DATETIME:
        case PRIMITIVE_TYPE.STRICTDATE:
            return {
                filter: 'agDateColumnFilter',
                allowedAggFuncs: ['count'],
            };
        case PRIMITIVE_TYPE.DECIMAL:
        case PRIMITIVE_TYPE.NUMBER:
        case PRIMITIVE_TYPE.INTEGER:
        case PRIMITIVE_TYPE.FLOAT:
            return {
                filter: 'agNumberColumnFilter',
                allowedAggFuncs: ['count', 'sum', 'max', 'min', 'avg', 'wavg'],
            };
        default:
            return {
                allowedAggFuncs: ['count'],
            };
    }
};
const QueryResultCellRenderer = observer((params) => {
    const resultState = params.resultState;
    const cellValue = params.value;
    const formattedCellValue = () => {
        if (isNumber(cellValue)) {
            return Intl.NumberFormat(DEFAULT_LOCALE, {
                maximumFractionDigits: MAXIMUM_FRACTION_DIGITS,
            }).format(Number(cellValue));
        }
        else if (isBoolean(cellValue)) {
            return String(cellValue);
        }
        return cellValue;
    };
    const cellValueUrlLink = isString(cellValue) && isValidURL(cellValue) ? cellValue : undefined;
    const mouseDown = (event) => {
        event.preventDefault();
        if (event.button === 0 || event.button === 2) {
            resultState.setMouseOverCell(resultState.selectedCells[0] ?? null);
        }
    };
    const mouseUp = (event) => {
        resultState.setIsSelectingCells(false);
    };
    const mouseOver = (event) => {
        resultState.setMouseOverCell(resultState.selectedCells[0] ?? null);
    };
    return (_jsx("div", { className: clsx('query-builder__result__values__table__cell'), onMouseDown: (event) => mouseDown(event), onMouseUp: (event) => mouseUp(event), onMouseOver: (event) => mouseOver(event), children: cellValueUrlLink ? (_jsx("a", { href: cellValueUrlLink, target: "_blank", rel: "noreferrer", children: cellValueUrlLink })) : (_jsx("span", { children: formattedCellValue() })) }));
});
const getLocalColDefs = (executionResult, resultState) => executionResult.result.columns.map((colName) => {
    const col = {
        minWidth: 50,
        sortable: true,
        resizable: true,
        field: colName,
        flex: 1,
        enablePivot: true,
        enableRowGroup: true,
        enableValue: true,
        cellRenderer: QueryResultCellRenderer,
        cellRendererParams: {
            resultState: resultState,
            tdsExecutionResult: executionResult,
        },
        ...getAggregationTDSColumnCustomizations(executionResult, colName),
        ...getTDSColumnCustomizations(executionResult, colName),
    };
    const persistedColumn = resultState.gridConfig?.columns.find((c) => c.colId === colName);
    if (persistedColumn) {
        if (persistedColumn.width) {
            col.width = persistedColumn.width;
        }
        col.pinned = persistedColumn.pinned ?? null;
        col.rowGroup = persistedColumn.rowGroup ?? false;
        col.rowGroupIndex = persistedColumn.rowGroupIndex ?? null;
        col.aggFunc = persistedColumn.aggFunc ?? null;
        col.pivot = persistedColumn.pivot ?? false;
        col.hide = persistedColumn.hide ?? false;
    }
    return col;
});
export const getFilterTDSColumnCustomizations = (result, columnName) => {
    const columnType = result.builder.columns.find((col) => col.name === columnName)?.type;
    switch (columnType) {
        case PRIMITIVE_TYPE.DATE:
        case PRIMITIVE_TYPE.DATETIME:
        case PRIMITIVE_TYPE.STRICTDATE:
            return {
                filter: 'agDateColumnFilter',
            };
        case PRIMITIVE_TYPE.DECIMAL:
        case PRIMITIVE_TYPE.INTEGER:
        case PRIMITIVE_TYPE.NUMBER:
        case PRIMITIVE_TYPE.FLOAT:
            return {
                filter: 'agNumberColumnFilter',
            };
        default:
            // we default all other columns to use filter true which defaults to set filters
            return {
                filter: true,
            };
    }
};
const getColDefs = (executionResult, resultState) => executionResult.result.columns.map((colName) => ({
    minWidth: 50,
    sortable: true,
    resizable: true,
    field: colName,
    flex: 1,
    cellRenderer: QueryResultCellRenderer,
    cellRendererParams: {
        resultState: resultState,
        tdsExecutionResult: executionResult,
    },
    ...getFilterTDSColumnCustomizations(executionResult, colName),
    ...getTDSColumnCustomizations(executionResult, colName),
}));
export const QueryBuilderTDSGridResult = observer((props) => {
    const { executionResult, queryBuilderState } = props;
    const applicationStore = useApplicationStore();
    const darkMode = !applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled;
    const [gridApi, setGridApi] = useState(undefined);
    const [aggFuncParams, setAggFuncParams] = useState(undefined);
    const resultState = queryBuilderState.resultState;
    const isLocalModeEnabled = queryBuilderState.isLocalModeEnabled;
    const colDefs = isLocalModeEnabled
        ? getLocalColDefs(executionResult, resultState)
        : getColDefs(executionResult, resultState);
    const onSaveGridColumnState = () => {
        if (!gridApi) {
            return;
        }
        resultState.setGridConfig({
            columns: gridApi.getColumnState(),
            isPivotModeEnabled: gridApi.isPivotMode(),
            isLocalModeEnabled: true,
            previewLimit: resultState.previewLimit,
            ...(resultState.wavgAggregationState?.weightedColumnIdPairs && {
                weightedColumnPairs: resultState.wavgAggregationState.weightedColumnIdPairs,
            }),
        });
    };
    const getSelectedCells = (api) => {
        const selectedRanges = api.getCellRanges();
        const nodes = [];
        api.forEachNode((node) => nodes.push(node));
        const columns = api.getColumnDefs();
        const selectedCells = [];
        if (selectedRanges) {
            for (const selectedRange of selectedRanges) {
                const rangeStart = selectedRange.startRow?.rowIndex ?? 0;
                const rangeEnd = selectedRange.endRow?.rowIndex ?? 0;
                const startRow = rangeStart < rangeEnd ? rangeStart : rangeEnd;
                const endRow = rangeStart < rangeEnd ? rangeEnd : rangeStart;
                const selectedColumns = selectedRange.columns.map((col) => col.getColId());
                for (let x = startRow; x <= endRow; x++) {
                    const curRowData = nodes.find((n) => n.rowIndex === x)?.data;
                    if (curRowData) {
                        for (const col of selectedColumns) {
                            const valueAndColumnId = {
                                value: Object.entries(curRowData)
                                    .find((rData) => rData[0] === col)
                                    ?.at(1),
                                columnName: col,
                                coordinates: {
                                    rowIndex: x,
                                    colIndex: columns.findIndex((colDef) => colDef.colId === col),
                                },
                            };
                            selectedCells.push(valueAndColumnId);
                        }
                    }
                }
            }
        }
        return selectedCells;
    };
    const getContextMenuItems = useCallback((params) => {
        let result = [];
        const fetchStructureImplementation = resultState.queryBuilderState.fetchStructureState.implementation;
        if (fetchStructureImplementation instanceof QueryBuilderTDSState) {
            result = [
                {
                    name: 'Filter By',
                    action: () => {
                        filterByOrOutValues(applicationStore, resultState.mousedOverCell, true, fetchStructureImplementation).catch(queryBuilderState.applicationStore.alertUnhandledError);
                    },
                },
                {
                    name: 'Filter Out',
                    action: () => {
                        filterByOrOutValues(applicationStore, resultState.mousedOverCell, false, fetchStructureImplementation).catch(queryBuilderState.applicationStore.alertUnhandledError);
                    },
                },
                'copy',
                'copyWithHeaders',
                {
                    name: 'Copy Row Value',
                    action: () => {
                        params.api.copySelectedRowsToClipboard();
                    },
                },
            ];
        }
        return result;
    }, [
        applicationStore,
        resultState.mousedOverCell,
        resultState.queryBuilderState.fetchStructureState.implementation,
        queryBuilderState.applicationStore.alertUnhandledError,
    ]);
    const weightedColumnOptions = gridApi
        ?.getColumns()
        ?.filter((c) => c.getColDef().cellDataType === 'number')
        .map((col) => ({
        label: col.getColId(),
        value: col.getColId(),
    }));
    const selectedWeightedColumn = aggFuncParams?.colDef.field &&
        resultState.wavgAggregationState?.weightedColumnIdPairs.get(aggFuncParams.colDef.field)
        ? {
            label: guaranteeNonNullable(resultState.wavgAggregationState.weightedColumnIdPairs.get(aggFuncParams.colDef.field)),
            value: guaranteeNonNullable(resultState.wavgAggregationState.weightedColumnIdPairs.get(aggFuncParams.colDef.field)),
        }
        : null;
    const onWeightedColumnOptionChange = (option) => {
        if (aggFuncParams?.colDef.field && option?.value) {
            resultState.wavgAggregationState?.addWeightedColumnIdPair(aggFuncParams.colDef.field, option.value);
        }
    };
    const weightedAverage = (param) => {
        if (param.colDef.field) {
            if (!resultState.wavgAggregationState) {
                resultState.setWavgAggregationState(new QueryBuilderResultWavgAggregationState());
            }
            resultState.wavgAggregationState?.addWeightedColumnIdPair(param.colDef.field, param.colDef.field);
            resultState.wavgAggregationState?.setIsApplyingWavg(true);
            setAggFuncParams(param);
        }
        else {
            applicationStore.notificationService.notifyError('The id of this column can`t be retrieved to perform weighted average');
        }
    };
    const weightedAverageHelper = (param) => {
        try {
            const column = param.colDef.field;
            if (column) {
                const weightedColumnId = resultState.wavgAggregationState?.weightedColumnIdPairs.get(column);
                if (weightedColumnId) {
                    const weightedColumnSum = (param.rowNode.allLeafChildren ?? [])
                        .map((node) => node.data[weightedColumnId])
                        .reduce((a, b) => a + b);
                    const weightedColumnMultiply = (param.rowNode.allLeafChildren ?? [])
                        .map((node) => node.data[weightedColumnId] * node.data[column])
                        .reduce((a, b) => a + b);
                    if (weightedColumnSum !== 0) {
                        onSaveGridColumnState();
                        return weightedColumnMultiply / weightedColumnSum;
                    }
                    else {
                        applicationStore.notificationService.notifyError('The weighted column sum is 0');
                    }
                }
                else {
                    applicationStore.notificationService.notifyError('The weighted column Id is not defined');
                }
            }
        }
        catch (error) {
            assertErrorThrown(error);
            applicationStore.notificationService.notifyError(error);
        }
        return -1;
    };
    useEffect(() => {
        if (aggFuncParams) {
            aggFuncParams.api.setColumnAggFunc(guaranteeNonNullable(aggFuncParams.colDef.field), QueryBuilderDataGridCustomAggregationFunction.WAVG);
        }
    }, [resultState.wavgAggregationState, aggFuncParams]);
    return (_jsx("div", { "data-testid": QUERY_BUILDER_TEST_ID.QUERY_BUILDER_RESULT_VALUES_TDS, className: "query-builder__result__values__table", children: _jsxs("div", { className: clsx('query-builder__result__tds-grid', {
                'ag-theme-balham': !darkMode,
                'ag-theme-balham-dark': darkMode,
            }), children: [isLocalModeEnabled ? (_jsx(DataGrid, { rowData: getRowDataFromExecutionResult(executionResult), onGridReady: (params) => {
                        setGridApi(params.api);
                        params.api.updateGridOptions({
                            pivotMode: Boolean(resultState.gridConfig?.isPivotModeEnabled),
                        });
                    }, gridOptions: {
                        suppressScrollOnNewData: true,
                        getRowId: (data) => `${data.data.rowNumber}`,
                        rowSelection: {
                            mode: 'multiRow',
                            checkboxes: false,
                            headerCheckbox: false,
                        },
                        pivotPanelShow: 'always',
                        rowGroupPanelShow: 'always',
                        cellSelection: true,
                    }, 
                    // NOTE: when column definition changed, we need to force refresh the cell to make sure the cell renderer is updated
                    // See https://stackoverflow.com/questions/56341073/how-to-refresh-an-ag-grid-when-a-change-occurs-inside-a-custom-cell-renderer-com
                    onRowDataUpdated: (params) => {
                        params.api.refreshCells({ force: true });
                    }, suppressFieldDotNotation: true, suppressContextMenu: false, columnDefs: colDefs, aggFuncs: {
                        wavg: weightedAverage,
                        WAVG: weightedAverageHelper,
                    }, sideBar: ['columns', 'filters'], onColumnVisible: onSaveGridColumnState, onColumnPinned: onSaveGridColumnState, onColumnResized: onSaveGridColumnState, onColumnRowGroupChanged: onSaveGridColumnState, onColumnValueChanged: onSaveGridColumnState, onColumnPivotChanged: onSaveGridColumnState, onColumnPivotModeChanged: onSaveGridColumnState })) : (_jsx(DataGrid, { rowData: getRowDataFromExecutionResult(executionResult), gridOptions: {
                        suppressScrollOnNewData: true,
                        getRowId: (data) => `${data.data.rowNumber}`,
                        rowSelection: {
                            mode: 'multiRow',
                            checkboxes: false,
                            headerCheckbox: false,
                        },
                        cellSelection: true,
                    }, 
                    // NOTE: when column definition changed, we need to force refresh the cell to make sure the cell renderer is updated
                    // See https://stackoverflow.com/questions/56341073/how-to-refresh-an-ag-grid-when-a-change-occurs-inside-a-custom-cell-renderer-com
                    onRowDataUpdated: (params) => {
                        params.api.refreshCells({ force: true });
                    }, onCellSelectionChanged: (event) => {
                        const selectedCells = getSelectedCells(event.api);
                        resultState.setSelectedCells([]);
                        selectedCells.forEach((cell) => resultState.addSelectedCell(cell));
                    }, suppressFieldDotNotation: true, suppressClipboardPaste: false, suppressContextMenu: false, columnDefs: colDefs, getContextMenuItems: (params) => getContextMenuItems(params) })), resultState.wavgAggregationState?.isApplyingWavg && (_jsx(Dialog, { open: resultState.wavgAggregationState.isApplyingWavg, onClose: () => resultState.wavgAggregationState?.setIsApplyingWavg(false), classes: {
                        root: 'editor-modal__root-container',
                        container: 'editor-modal__container',
                        paper: 'editor-modal__content',
                    }, children: _jsxs(Modal, { darkMode: !applicationStore.layoutService
                            .TEMPORARY__isLightColorThemeEnabled, className: "query-editor__blocking-alert", children: [_jsx(ModalHeader, { title: "Applying Weighted Average" }), _jsxs(ModalBody, { children: [_jsx("div", { className: "query-builder__result__tds-grid__text", children: "choose a weighted column from dropdown" }), _jsx(CustomSelectorInput, { options: weightedColumnOptions, onChange: onWeightedColumnOptionChange, value: selectedWeightedColumn, placeholder: 'Choose a weighted column', darkMode: !applicationStore.layoutService
                                            .TEMPORARY__isLightColorThemeEnabled })] }), _jsx(ModalFooter, { children: _jsx(ModalFooterButton, { onClick: () => {
                                        resultState.wavgAggregationState?.setIsApplyingWavg(false);
                                    }, text: "Apply" }) })] }) }))] }) }));
});
//# sourceMappingURL=QueryBuilderTDSGridResult.js.map