import { ROW_SUMMARY_ROW_ID, WEIGHTED_AVERAGE_AGGREGATED_FUNCTION, } from '../../AdaptableState/Common/RowSummary';
import { RowSummarySet } from '../../Redux/ActionsReducers/InternalRedux';
import * as ModuleConstants from '../../Utilities/Constants/ModuleConstants';
import Helper from '../Helpers/Helper';
import { AggregatedScalarLiveValue } from './AggregatedScalarLiveValue';
import { isObjectEmpty } from '../Extensions/ObjectExtensions';
import throttle from 'lodash/throttle';
import isEqual from 'lodash/isEqual';
/**
 * The logic is extracted here to make it easier to follow
 */
export class RowSummaryService {
    constructor(api) {
        this.api = api;
        this.cachedCellSummary = new Map();
        this._throttleAcumulatedColumnsThatChanged = new Set();
        /**
         *
         * @param colId optional to evaluate only one column
         */
        this._throttledEvaluateRowSummary = throttle(this.evaluateRowSummary, 300);
    }
    onAdaptableReady() {
        this.rowSummariesSubscriptions();
    }
    rowSummariesSubscriptions() {
        // return;
        if (this.api.isDestroyed()) {
            return;
        }
        // Currently not available for serverside model
        if (!this.api.layoutApi.internalApi.getLayoutSupportedFeatures().RowSummaries) {
            return;
        }
        this.throttledEvaluateRowSummary();
        this.api.eventApi.on('AdaptableStateReloaded', () => {
            this.throttledEvaluateRowSummary();
        });
        this.api.internalApi
            .getDataService()
            .on('RowDataChanged', (rowDataChangedInfo) => {
            this.throttledEvaluateRowSummary();
        });
        this.api.eventApi.on('CellChanged', (event) => {
            const columnId = event.cellDataChange.column.columnId;
            this.throttledEvaluateRowSummary({
                columnIds: [columnId],
            });
        });
        this.api.eventApi.on('LayoutChanged', (event) => {
            // exclude filter events, those are handled in another event
            if (event.actionName.includes('FILTER')) {
                return;
            }
            setTimeout(() => {
                // the timeout is added so the grid has time to repond to the layout changed
                this.throttledEvaluateRowSummary();
            }, 16);
        });
        const adaptable = this.api.internalApi.getAdaptableInstance();
        adaptable._on('AdapTableFiltersApplied', () => {
            // we need to use this instead of layout changed so the rows have time to update
            this.throttledEvaluateRowSummary();
        });
        adaptable._on('FirstDataRendered', () => {
            this.throttledEvaluateRowSummary();
        });
    }
    throttledEvaluateRowSummary(reason) {
        if (reason) {
            reason.columnIds.forEach((col) => this._throttleAcumulatedColumnsThatChanged.add(col));
        }
        this._throttledEvaluateRowSummary(reason);
    }
    evaluateRowSummary(reason) {
        if (this._throttleAcumulatedColumnsThatChanged.size > 0) {
            const columnIds = Array.from(this._throttleAcumulatedColumnsThatChanged.values());
            reason = {
                columnIds,
            };
            this._throttleAcumulatedColumnsThatChanged.clear();
        }
        if (this.api.isDestroyed() || this.api.layoutApi.isCurrentLayoutPivot()) {
            return;
        }
        const currentLayout = this.api.layoutApi.getCurrentLayout();
        let previousLayout = this.previousLayout;
        // it is added here to be sure it is saved
        this.previousLayout = currentLayout;
        /**
         * If the previous & current layout does not have row summaries, it is safe to exit
         */
        if (isObjectEmpty(currentLayout?.RowSummaries) && isObjectEmpty(previousLayout?.RowSummaries)) {
            return;
        }
        const rowSummaries = currentLayout.RowSummaries ?? [];
        const aggColsMap = (currentLayout.TableAggregationColumns || []).reduce((acc, { ColumnId, AggFunc }) => {
            acc[ColumnId] = AggFunc;
            return acc;
        }, {});
        const rowSummariesResults = rowSummaries
            .filter((rowSummary) => !rowSummary.IsSuspended)
            .map((rowSummary, index) => {
            const { ColumnsMap, Position, 
            // it defaults to true
            IncludeOnlyFilteredRows = true, } = rowSummary;
            return {
                Position,
                RowData: Object.entries(ColumnsMap ?? {}).reduce((acc, [columnId, expression]) => {
                    if (columnId === 'Uuid' || columnId === 'Source' || columnId === 'AdaptableVersion') {
                        return acc;
                    }
                    const key = `${columnId}-${expression}-IncludeOnlyFilteredRows=${IncludeOnlyFilteredRows ? 'filtered' : 'all'}`;
                    let expressionLiveValue = this.cachedCellSummary.get(key);
                    if (expressionLiveValue) {
                        if (!reason) {
                            // refresh all of them
                            expressionLiveValue.refresh();
                        }
                        else if ('columnIds' in reason && reason.columnIds.includes(columnId)) {
                            expressionLiveValue.refresh();
                        }
                    }
                    if (!expressionLiveValue) {
                        try {
                            let aggregatedScalarExpression = `${expression}([${columnId}])`;
                            if (aggregatedScalarExpression.includes(WEIGHTED_AVERAGE_AGGREGATED_FUNCTION) &&
                                aggColsMap[columnId] &&
                                typeof aggColsMap[columnId] === 'object') {
                                const weight = aggColsMap[columnId].weightedColumnId;
                                if (weight) {
                                    aggregatedScalarExpression = `AVG([${columnId}], WEIGHT([${weight}]))`;
                                }
                            }
                            expressionLiveValue = new AggregatedScalarLiveValue({
                                aggregatedScalarExpression,
                            }, ModuleConstants.LayoutModuleId, this.api, () => {
                                if (rowSummary.IncludeOnlyFilteredRows ?? true) {
                                    return this.api.gridApi.getVisibleRowNodes();
                                }
                                else {
                                    return this.api.gridApi.getAllRowNodes();
                                }
                            });
                        }
                        catch (e) {
                            this.api.logError('Error evaluating row summary', e);
                        }
                        this.cachedCellSummary.set(key, expressionLiveValue);
                    }
                    let value = null;
                    if (expressionLiveValue) {
                        value = expressionLiveValue.getGlobalAggregatedValue();
                        if (typeof value === 'number' && !isNaN(value)) {
                            value = Helper.roundNumber(value, 2);
                        }
                    }
                    const column = this.api.columnApi.getColumnWithColumnId(columnId);
                    const fieldName = column?.field ?? column?.columnId ?? columnId;
                    acc = this.api.internalApi.setValueUsingField(acc, fieldName, value);
                    return acc;
                }, {
                    [ROW_SUMMARY_ROW_ID]: `row-summary-${index}`,
                }),
            };
        });
        if (this.previousRowSummaries && isEqual(rowSummariesResults, this.previousRowSummaries)) {
            return;
        }
        this.api.internalApi.dispatchReduxAction(RowSummarySet(rowSummariesResults));
        this.previousRowSummaries = rowSummariesResults;
    }
}
