import * as InternalRedux from '../../Redux/ActionsReducers/InternalRedux';
import * as PopupRedux from '../../Redux/ActionsReducers/PopupRedux';
import * as AlertRedux from '../../Redux/ActionsReducers/AlertRedux';
import { ApiBase } from './ApiBase';
import * as ModuleConstants from '../../Utilities/Constants/ModuleConstants';
import ObjectFactory from '../../Utilities/ObjectFactory';
import { isAdaptableCellChangedAlert, isAdaptableRowChangedAlert, } from '../../AdaptableState/Common/AdaptableAlert';
import StringExtensions from '../../Utilities/Extensions/StringExtensions';
import { AlertInternalApi } from '../Internal/AlertInternalApi';
export class AlertApiImpl extends ApiBase {
    constructor(_adaptable) {
        super(_adaptable);
        this.internalApi = new AlertInternalApi(_adaptable);
    }
    getAlertState() {
        return this.getAdaptableState().Alert;
    }
    getAlertDefinitions(config) {
        return (this.handleLayoutAssociatedObjects(this.getAlertState().AlertDefinitions, 'Alert', config) ??
            []);
    }
    getSuspendedAlertDefinitions(config) {
        return this.getAlertDefinitions(config).filter((alertDefinition) => alertDefinition.IsSuspended);
    }
    getActiveAlertDefinitions(config) {
        return this.getAlertDefinitions(config).filter((alertDefinition) => !alertDefinition.IsSuspended);
    }
    deleteAlertDefinition(alertDefinition) {
        this.dispatchAction(AlertRedux.AlertDefinitionDelete(alertDefinition));
    }
    getAlertDefinitionById(id) {
        return this.getAlertDefinitions().find((alert) => alert?.Uuid === id);
    }
    addAlertDefinition(alertDefinition) {
        this.addUidToAdaptableObject(alertDefinition);
        this.dispatchAction(AlertRedux.AlertDefinitionAdd(alertDefinition));
        return this.getAlertDefinitionById(alertDefinition.Uuid);
    }
    async displayAdaptableAlert(alertToShow) {
        // There are 3 things we always do with an alert:
        // 1. Dispatch the Alert (so it appears in the toolbar)
        this.addUidToAdaptableObject(alertToShow);
        this.dispatchAction(InternalRedux.AdaptableAlertAdd(alertToShow, this.getAlertOptions().maxAlertsInStore));
        // 2. Publish the Alert Fired Event
        this.getEventApi().internalApi.fireAlertFiredEvent(alertToShow);
        // 3. Manage the many different possible Alert behaviours
        // Note:  alertProperties.PreventEdit is handled separately, in the Adaptable.setupColumnValueSetter() function
        if (alertToShow.alertDefinition && alertToShow.alertDefinition.AlertProperties != undefined) {
            const alertDefinition = alertToShow.alertDefinition;
            const alertProperties = alertDefinition.AlertProperties;
            // Show a Toast Popup
            if (alertProperties.DisplayNotification) {
                this.displayAdaptableAlertNotification(alertToShow);
            }
            // Log to console
            if (alertProperties.LogToConsole) {
                this.getAdatableLogger().consoleLogByMessageType(alertToShow.header + ': ' + alertToShow.message, alertDefinition.MessageType);
            }
            // Show Alert in a Div (if one has been set)
            if (alertProperties.ShowInDiv) {
                let alertDiv;
                let optionsDiv = this.getContainerOptions().alertContainer;
                if (optionsDiv) {
                    alertDiv =
                        typeof optionsDiv === 'string' ? document.getElementById(optionsDiv) : optionsDiv;
                }
                if (alertDiv) {
                    let alertString = alertToShow.header + ': ' + alertToShow.message;
                    alertDiv.innerHTML = alertString;
                }
            }
            // Jump to the Cell
            if (alertProperties.JumpToCell &&
                isAdaptableCellChangedAlert(alertToShow) &&
                alertToShow.cellDataChangedInfo) {
                this.getGridApi().jumpToCell(alertToShow.cellDataChangedInfo.primaryKeyValue, alertToShow.cellDataChangedInfo.column.columnId, alertToShow.cellDataChangedInfo.rowNode);
            }
            // Jump to the Row
            else if (alertProperties.JumpToRow &&
                isAdaptableRowChangedAlert(alertToShow) &&
                alertToShow.rowDataChangedInfo?.rowTrigger === 'Add') {
                const [firstRowNode] = alertToShow.rowDataChangedInfo.rowNodes;
                if (firstRowNode) {
                    this.getGridApi().jumpToRow(firstRowNode);
                }
            }
            // For CellChanged Alerts either Highlight the cell or row
            // for former - we just refresh the cells to trigger a re-evaluation of the Adaptable.setupColumn[Style/Class]
            if (isAdaptableCellChangedAlert(alertToShow) &&
                alertToShow.cellDataChangedInfo &&
                (alertProperties.HighlightCell || alertProperties.HighlightRow)) {
                let alertNode = alertToShow.cellDataChangedInfo.rowNode;
                if (!alertNode) {
                    alertNode = this.getGridApi().getRowNodeForPrimaryKey(alertToShow.cellDataChangedInfo.primaryKeyValue);
                }
                if (alertNode) {
                    if (alertProperties.HighlightRow) {
                        this.getGridApi().refreshRowNodes([alertNode]);
                        setTimeout(() => {
                            this.dispatchAction(InternalRedux.AdaptableAlertRemoveRowHighlight(alertToShow));
                        }, this.getAlertOptions().rowHighlightDuration);
                    }
                    if (alertProperties.HighlightCell) {
                        this.getGridApi().refreshCell(alertNode, alertToShow.cellDataChangedInfo.column.columnId);
                        setTimeout(() => {
                            this.dispatchAction(InternalRedux.AdaptableAlertRemoveCellHighlight(alertToShow));
                        }, this.getAlertOptions().cellHighlightDuration);
                    }
                }
            }
            // For RowChange Alerts only Highlight the row - we just refresh the rows to trigger a re-evaluation of the Adaptable.setupColumn[Style/Class]
            else if (alertProperties.HighlightRow &&
                isAdaptableRowChangedAlert(alertToShow) &&
                alertToShow.rowDataChangedInfo?.rowTrigger === 'Add') {
                this.getGridApi().refreshRowNodes(alertToShow.rowDataChangedInfo.rowNodes);
                setTimeout(() => {
                    this.dispatchAction(InternalRedux.AdaptableAlertRemoveRowHighlight(alertToShow));
                }, this.getAlertOptions().rowHighlightDuration);
            }
        }
    }
    displayAdaptableAlertNotification(alert) {
        this.dispatchAction(PopupRedux.PopupShowAlert(alert));
    }
    async showAlert(alertHeader, alertMessage, messageType, alertProperties) {
        const alertDefinition = ObjectFactory.CreateInternalAlertDefinitionForMessages(messageType, alertProperties);
        let alertToShow = ObjectFactory.CreateGenericAlert(alertHeader, alertMessage, alertDefinition);
        await this.displayAdaptableAlert(alertToShow);
    }
    async showAlertInfo(alertHeader, alertMessage, alertProperties) {
        await this.showAlert(alertHeader, alertMessage, 'Info', alertProperties);
    }
    async showAlertSuccess(alertHeader, alertMessage, alertProperties) {
        await this.showAlert(alertHeader, alertMessage, 'Success', alertProperties);
    }
    async showAlertWarning(alertHeader, alertMessage, alertProperties) {
        await this.showAlert(alertHeader, alertMessage, 'Warning', alertProperties);
    }
    async showAlertError(alertHeader, alertMessage, alertProperties) {
        await this.showAlert(alertHeader, alertMessage, 'Error', alertProperties);
    }
    openAlertSettingsPanel() {
        this.showModulePopup(ModuleConstants.AlertModuleId);
    }
    editAlertDefinition(alertDefinition) {
        this.dispatchAction(AlertRedux.AlertDefinitionEdit(alertDefinition));
        return this.getAlertDefinitionById(alertDefinition.Uuid);
    }
    suspendAlertDefinition(alertDefinition) {
        this.dispatchAction(AlertRedux.AlertDefinitionSuspend(alertDefinition));
        return this.getAlertDefinitionById(alertDefinition.Uuid);
    }
    suspendAllAlertDefinition() {
        this.dispatchAction(AlertRedux.AlertDefinitionSuspendAll());
    }
    unSuspendAllAlertDefinition() {
        this.dispatchAction(AlertRedux.AlertDefinitionUnSuspendAll());
    }
    unSuspendAlertDefinition(alertDefinition) {
        this.dispatchAction(AlertRedux.AlertDefinitionUnSuspend(alertDefinition));
        return this.getAlertDefinitionById(alertDefinition.Uuid);
    }
    evaluateAlertDefinitions(alertDefinitions) {
        const filterScopeAllDefinitions = (alertDefinition) => {
            if (this.getColumnScopeApi().scopeIsAll(alertDefinition.Scope) &&
                !this.getExpressionApi().getAdaptableQueryExpression(alertDefinition.Rule)) {
                // we don't support scope ALL without an expression
                // the predicates for scope ALL do NOT make any sense for a programmatic evaluation
                return false;
            }
            return true;
        };
        const relevantAlertDefinitions = alertDefinitions.filter((alertDefinition) => filterScopeAllDefinitions(alertDefinition));
        const relevantColumnIds = new Set();
        relevantAlertDefinitions.forEach((alertDefinition) => {
            this.getRelevantColumnIdsForAlertDefinition(alertDefinition).forEach((relevantColumnId) => relevantColumnIds.add(relevantColumnId));
        });
        if (!relevantColumnIds.size) {
            return;
        }
        const cellDataChangedInfos = this.createCellDataChangeInfoStubs(Array.from(relevantColumnIds));
        cellDataChangedInfos.forEach((cellDataChangedInfo) => {
            const alertDefinitions = this.internalApi
                .getAlertDefinitionsForCellDataChange(cellDataChangedInfo)
                .filter((alertDefinition) => filterScopeAllDefinitions(alertDefinition))
                // some alert definitions might NOT be relevant for the given locator
                .filter((alertDefinition) => relevantAlertDefinitions.some((relevantAlertDefinition) => relevantAlertDefinition.Uuid === alertDefinition.Uuid));
            this.getAlertApi().internalApi.showAlertForDefinitions(cellDataChangedInfo, alertDefinitions);
        });
    }
    findAlertDefinitions(criteria) {
        return this.getAdaptableInternalApi().findAdaptableObjectsByLookupCriteria(criteria, this.getAlertDefinitions({
            includeLayoutNotAssociatedObjects: true,
        }));
    }
    getRelevantColumnIdsForAlertDefinition(alertDefinition) {
        //  if there is an Expression defined, extract only the columns from there (in that case the Scope would be 'All' anyway)
        // columns referenced in rule expression
        const ruleExpression = this.getExpressionApi().getAdaptableQueryExpression(alertDefinition.Rule);
        if (this.getColumnScopeApi().scopeIsAll(alertDefinition.Scope) &&
            StringExtensions.IsNotNullOrEmpty(ruleExpression)) {
            // return only first referenced column (otherwise we would generate multiple alerts for the same row)
            const queryColumns = this.getAdaptableInternalApi()
                .getQueryLanguageService()
                .getColumnsFromExpression(ruleExpression);
            return queryColumns.length > 0 ? [queryColumns[0]] : [];
        }
        else {
            // otherwise return all columns for given scope
            return this.getColumnScopeApi()
                .getColumnsInScope(alertDefinition.Scope)
                .map((adaptableColumn) => adaptableColumn.columnId);
        }
    }
    createCellDataChangeInfoStubs(relevantColumnIds) {
        const cellDataChangeInfos = [];
        //const adaptable: IAdaptable = this.getAdaptableInternalApi().getAdaptableInstance();
        const relevantColumns = this.getColumnApi()
            .getColumnsWithColumnIds(relevantColumnIds)
            .filter(Boolean);
        const changedAt = Date.now();
        this.getAdaptableInternalApi().forAllRowNodesDo((rowNode) => {
            relevantColumns.forEach((column) => {
                const cellDataChangeInfoStub = {
                    changedAt,
                    rowNode,
                    column,
                    rowData: rowNode.data,
                    oldValue: null,
                    newValue: this.getGridApi().getRawValueFromRowNode(rowNode, column.columnId),
                    primaryKeyValue: this.getGridApi().getPrimaryKeyValueForRowNode(rowNode),
                };
                cellDataChangeInfos.push(cellDataChangeInfoStub);
            });
        });
        return cellDataChangeInfos;
    }
}
