import * as FlashingCellRedux from '../Redux/ActionsReducers/FlashingCellRedux';
import * as ModuleConstants from '../Utilities/Constants/ModuleConstants';
import { ArrayExtensions } from '../Utilities/Extensions/ArrayExtensions';
import { AdaptableModuleBase } from './AdaptableModuleBase';
import { isReactiveQuery } from '../AdaptableState/Common/AdaptableQuery';
import { getFlashingTargetViewItems } from './Utilities/FlashingCell/getFlashingTargetViewItems';
import { getFlashingCellStyleViewItems } from './Utilities/FlashingCell/getFlashingCellStyleViewItems';
import { getObjectTagsViewItems } from '../Utilities/getObjectTagsViewItems';
import { getRuleViewItems } from '../Utilities/getRuleViewItems';
import { getScopeViewItems } from '../Utilities/getScopeViewItems';
import { FlashingCellWizard } from '../View/FlashingCell/Wizard/FlashingCellWizard';
import { getFlashingCellDurationViewItems } from './Utilities/FlashingCell/getFlashingCellDurationViewItems';
import { errorOnce } from '../agGrid/AdaptableLogger';
export class FlashingCellModule extends AdaptableModuleBase {
    isListeningToCellDataChanges;
    constructor(api) {
        super(ModuleConstants.FlashingCellModuleId, ModuleConstants.FlashingCellFriendlyName, 'lightning', 'FlashingAlert', 'Flash cells when they change', api);
        this.isListeningToCellDataChanges = false;
    }
    onAdaptableReady() {
        this.checkListenToCellDataChanged();
    }
    shouldListenToDataChanges() {
        return ArrayExtensions.IsNotNullOrEmpty(this.api.flashingCellApi.getActiveFlashingCellDefinitions());
    }
    checkListenToCellDataChanged() {
        if (!this.isListeningToCellDataChanges) {
            if (this.shouldListenToDataChanges()) {
                this.setupCellDataChangeListener();
                this.isListeningToCellDataChanges = true;
            }
        }
    }
    setupCellDataChangeListener() {
        this.api.internalApi
            .getDataService()
            .on('CellDataChanged', (cellDataChangedInfo) => {
            if (cellDataChangedInfo.trigger === 'undo') {
                // do NOT handle reverted changes
                return;
            }
            const activeFlashingCellDefinitions = this.api.flashingCellApi.getActiveFlashingCellDefinitions();
            if (ArrayExtensions.IsNotNullOrEmpty(activeFlashingCellDefinitions)) {
                // TODO: think about flashing row value only
                // - flash for the underlying small data changes, but the visual does not
                if (this.api.optionsApi.getAlertOptions().dataChangeDetectionPolicy === 'formattedValue') {
                    const { oldValue, newValue, rowNode } = cellDataChangedInfo;
                    const columnId = cellDataChangedInfo.column.columnId;
                    const oldFormattedValue = this.api.gridApi.getDisplayValueFromRawValue(rowNode, columnId, oldValue);
                    const newFormattedValue = this.api.gridApi.getDisplayValueFromRawValue(rowNode, columnId, newValue);
                    if (oldFormattedValue === newFormattedValue) {
                        // if the formattedValues are identical, then no alert is fired
                        return;
                    }
                }
                const relevantFlashingCellDefinitions = this.getFlashingCellDefinitionsForDataChange(activeFlashingCellDefinitions, cellDataChangedInfo);
                if (ArrayExtensions.IsNotNullOrEmpty(relevantFlashingCellDefinitions)) {
                    this.showFlashingCellsForDefinitions(cellDataChangedInfo, relevantFlashingCellDefinitions);
                }
            }
        });
    }
    getFlashingCellDefinitionsForDataChange(activeFlashingCellDefinitions, cellDataChangedInfo) {
        const defaultNoPredicateReturn = false;
        let relatedFlashingCellsDefinitions = activeFlashingCellDefinitions
            .filter((v) => this.api.columnScopeApi.isColumnInScope(cellDataChangedInfo.column, v.Scope))
            .filter((flashingCellDefinition) => !isReactiveQuery(flashingCellDefinition.Rule));
        let triggeredFlashingCells = [];
        if (ArrayExtensions.IsNotNullOrEmpty(relatedFlashingCellsDefinitions)) {
            relatedFlashingCellsDefinitions.forEach((flashingCellDefinition) => {
                // for aggregation the data-change needs to be aggChange
                if (cellDataChangedInfo.trigger !== 'aggChange' &&
                    this.isFlashingTargetOnlyAggChange(flashingCellDefinition)) {
                    return;
                }
                if (flashingCellDefinition.Rule.BooleanExpression) {
                    let expression = flashingCellDefinition.Rule.BooleanExpression;
                    let rowNode = cellDataChangedInfo.rowNode;
                    if (!rowNode) {
                        rowNode = this.api.gridApi.getRowNodeForPrimaryKey(cellDataChangedInfo.primaryKeyValue);
                    }
                    const isValidExpression = this.api.expressionApi.isValidBooleanExpression(expression, this.moduleInfo.ModuleName, `Invalid Alert boolean expression '${expression}'`);
                    let isSatisfiedExpression = false;
                    try {
                        isSatisfiedExpression =
                            isValidExpression &&
                                this.api.internalApi
                                    .getQueryLanguageService()
                                    .evaluateBooleanExpression(expression, this.moduleInfo.ModuleName, rowNode, {
                                    dataChangedEvent: cellDataChangedInfo,
                                });
                    }
                    catch (error) {
                        isSatisfiedExpression = false;
                        errorOnce(error.message);
                    }
                    if (isSatisfiedExpression) {
                        triggeredFlashingCells.push(flashingCellDefinition);
                    }
                }
                else if (this.isAlertPredicateTriggered(flashingCellDefinition, cellDataChangedInfo, defaultNoPredicateReturn)) {
                    triggeredFlashingCells.push(flashingCellDefinition);
                }
            });
        }
        return triggeredFlashingCells;
    }
    showFlashingCellsForDefinitions(cellDataChangedInfo, flashingCellDefinitions = []) {
        const allColumnIds = this.api.columnApi.getUIAvailableColumns().map((c) => c.columnId);
        const columnDataType = cellDataChangedInfo.column.dataType;
        const numeric = columnDataType === 'number';
        const isComparableType = numeric || columnDataType === 'date';
        let up = false;
        let down = false;
        let direction = 'neutral';
        if (isComparableType) {
            const newValue = numeric
                ? Number(cellDataChangedInfo.newValue)
                : cellDataChangedInfo.newValue;
            const oldValue = numeric
                ? Number(cellDataChangedInfo.oldValue)
                : cellDataChangedInfo.oldValue;
            up = newValue > oldValue;
            down = newValue < oldValue;
            direction = up ? 'up' : down ? 'down' : 'neutral';
        }
        flashingCellDefinitions.forEach((flashingCellDefinition) => {
            let flashTarget = this.api.flashingCellApi.getFlashingCellFlashTarget(flashingCellDefinition);
            const flashColumnIds = { [cellDataChangedInfo.column.columnId]: true };
            /**
             * When flashing a row, all columns in that row are marked to flash.
             */
            if (flashTarget === 'row' || flashTarget?.includes?.('row')) {
                allColumnIds.forEach((colId) => {
                    flashColumnIds[colId] = true;
                });
            }
            const rowPrimaryKey = cellDataChangedInfo.primaryKeyValue;
            this.api.flashingCellApi.showFlashingCell({
                rowPrimaryKey,
                flashingCellDefinition: flashingCellDefinition,
                cellDataChangedInfo: cellDataChangedInfo,
                direction,
                flashTarget,
                flashColumnIds,
            });
        });
    }
    getModuleAdaptableObjects(config) {
        return this.api.flashingCellApi.getFlashingCellDefinitions(config);
    }
    getExplicitlyReferencedColumnIds(alertDefinition) {
        const queryExpression = this.api.expressionApi.getAdaptableQueryExpression(alertDefinition.Rule);
        if (queryExpression) {
            return this.api.expressionApi.getColumnsFromExpression(queryExpression);
        }
        else if (this.api.columnScopeApi.scopeHasColumns(alertDefinition.Scope)) {
            return this.api.columnScopeApi
                .getColumnsInScope(alertDefinition.Scope)
                .map((adaptableColumn) => adaptableColumn.columnId);
        }
        return [];
    }
    getReferencedNamedQueryNames(alertDefinition) {
        const queryExpression = this.api.expressionApi.getAdaptableQueryExpression(alertDefinition.Rule);
        if (!queryExpression) {
            return [];
        }
        return this.api.namedQueryApi.internalApi.getReferencedNamedQueryNames(queryExpression);
    }
    createColumnMenuItems(column) {
        if (column && this.isModuleEditable()) {
            if (!column.isCalculatedColumn && !column.isTreeColumn) {
                const flashingCellDefinitions = this.api.flashingCellApi.getFlashingCellDefinitions();
                const flashingCellForCurrentColumn = flashingCellDefinitions.find((flashingCellDefinition) => {
                    return this.api.columnScopeApi.isColumnInScope(column, flashingCellDefinition.Scope);
                });
                if (flashingCellForCurrentColumn) {
                    return [
                        this.createMenuItemReduxAction('flashing-cell-delete', 'Delete Flashing Cell', this.moduleInfo.Glyph, FlashingCellRedux.FlashingCellDefinitionDelete(flashingCellForCurrentColumn)),
                    ];
                }
                return [
                    this.createMenuItemReduxAction('flashing-cell-add', 'Add Flashing Cell', this.moduleInfo.Glyph, FlashingCellRedux.FlashingCellDefinitionAdd({
                        Name: `Any Change [${column.columnId}]`,
                        Scope: {
                            ColumnIds: [column.columnId],
                        },
                        Rule: {
                            BooleanExpression: 'ANY_CHANGE([' + column.columnId + '])',
                        },
                    })),
                ];
            }
        }
    }
    createContextMenuItems(menuContext) {
        const items = [];
        if (!menuContext.isRowGroupColumn && this.isModuleVisible()) {
            if (menuContext.adaptableColumn && menuContext.rowNode) {
                const flashingCellForRow = this.api.flashingCellApi.internalApi.getAdaptableFlashingCellFor(menuContext.primaryKeyValue);
                const flashingCellForCell = this.api.flashingCellApi.internalApi.getAdaptableFlashingCellFor(menuContext.primaryKeyValue, menuContext.adaptableColumn.columnId);
                if (flashingCellForRow) {
                    if (flashingCellForRow.flashTarget === 'row' ||
                        (Array.isArray(flashingCellForRow?.flashTarget) &&
                            flashingCellForRow.flashTarget.includes('row'))) {
                        items.push(this.createMenuItemClickFunction('flashing-row-clear', 'Clear Flashing Row', this.moduleInfo.Glyph, () => this.api.internalApi
                            .getFlashingCellService()
                            .clearGridCellFlash(flashingCellForRow)));
                    }
                }
                else if (flashingCellForCell && flashingCellForCell.flashTarget === 'cell') {
                    items.push(this.createMenuItemClickFunction('flashing-cell-clear', 'Clear Flashing Cell', this.moduleInfo.Glyph, () => this.api.internalApi
                        .getFlashingCellService()
                        .clearGridCellFlash(flashingCellForCell)));
                }
            }
        }
        return items;
    }
    isFlashingTargetOnlyAggChange(flashingCellDefinition) {
        if (flashingCellDefinition.FlashTarget === 'aggFuncCell') {
            return true;
        }
        return (Array.isArray(flashingCellDefinition.FlashTarget) &&
            flashingCellDefinition.FlashTarget.length === 1 &&
            flashingCellDefinition.FlashTarget.includes('aggFuncCell'));
    }
    isAlertPredicateTriggered(alert, dataChangedEvent, defaultNoPredicateReturn = false) {
        const predicateDefHandlerContext = {
            value: dataChangedEvent.newValue,
            oldValue: dataChangedEvent.oldValue,
            rawValue: dataChangedEvent.newValue,
            displayValue: dataChangedEvent.newValue,
            node: dataChangedEvent.rowNode,
            column: dataChangedEvent.column,
            ...this.api.internalApi.buildBaseContext(),
        };
        return this.api.predicateApi.handleColumnPredicates(alert.Rule?.Predicates, predicateDefHandlerContext, defaultNoPredicateReturn);
    }
    getTeamSharingAction() {
        return {
            ModuleEntities: this.api.flashingCellApi.getFlashingCellDefinitions(),
            AddAction: FlashingCellRedux.FlashingCellDefinitionAdd,
            EditAction: FlashingCellRedux.FlashingCellDefinitionEdit,
        };
    }
    toView(flashingCell) {
        return {
            items: [
                {
                    name: 'Name',
                    values: [flashingCell.Name],
                },
                { ...getScopeViewItems(flashingCell.Scope, this.api), label: 'Trigger' },
                {
                    ...getRuleViewItems(flashingCell.Rule, this.api),
                    label: 'Rule',
                    name: 'Rule',
                },
                getFlashingCellDurationViewItems(flashingCell),
                getFlashingTargetViewItems(flashingCell),
                getFlashingCellStyleViewItems(),
                getObjectTagsViewItems(flashingCell, this.api),
            ],
            abObject: flashingCell,
        };
    }
    toViewAll() {
        return this.getModuleAdaptableObjects({
            includeLayoutNotExtendedObjects: this.showLayoutNonExtendedObjects(),
        }).map((flashingCell) => this.toView(flashingCell));
    }
    getViewProperties() {
        return {
            getEditAction: FlashingCellRedux.FlashingCellDefinitionEdit,
            getSuspendAction: FlashingCellRedux.FlashingCellDefinitionSuspend,
            getUnSuspendAction: FlashingCellRedux.FlashingCellDefinitionUnSuspend,
            getSuspendAllAction: FlashingCellRedux.FlashingCellDefinitionSuspendAll,
            getUnSuspendAllAction: FlashingCellRedux.FlashingCellDefinitionUnSuspendAll,
            getDeleteAction: FlashingCellRedux.FlashingCellDefinitionDelete,
            getEditWizard: () => FlashingCellWizard,
        };
    }
    containsLayoutExtensions() {
        return true;
    }
}
