import { ArrayExtensions } from '../Extensions/ArrayExtensions';
import StringExtensions from '../Extensions/StringExtensions';
import { AlertModuleId } from '../Constants/ModuleConstants';
import { SERVER_VALIDATION_HEADER, SERVER_VALIDATION_MESSAGE_TYPE, } from '../Constants/GeneralConstants';
import { errorOnce } from '../../agGrid/AdaptableLogger';
export class ValidationService {
    adaptableApi;
    constructor(adaptableApi) {
        this.adaptableApi = adaptableApi;
        this.adaptableApi = adaptableApi;
    }
    // Not sure where to put this: was in the Module but might be better here until I can work out a way of having an event with a callback...
    getValidationRulesForDataChange(cellDataChangedInfo) {
        // if the new value is the same as the old value then we can get out as we dont see it as an edit?
        if (cellDataChangedInfo.oldValue == cellDataChangedInfo.newValue) {
            return [];
        }
        let editingRules = this.adaptableApi.alertApi.internalApi
            .getAlertDefinitionsWithPreventEdit()
            .filter((v) => this.adaptableApi.columnScopeApi.isColumnInScope(cellDataChangedInfo.column, v.Scope));
        let failedValidations = [];
        if (ArrayExtensions.IsNotEmpty(editingRules)) {
            editingRules.forEach((alertDefinition) => {
                if (alertDefinition.Rule.BooleanExpression) {
                    let isSatisfiedExpression = false;
                    try {
                        isSatisfiedExpression =
                            cellDataChangedInfo.rowNode != null &&
                                this.adaptableApi.internalApi
                                    .getQueryLanguageService()
                                    .evaluateBooleanExpression(alertDefinition.Rule.BooleanExpression, AlertModuleId, cellDataChangedInfo.rowNode);
                    }
                    catch (error) {
                        isSatisfiedExpression = false;
                        errorOnce(error.message);
                    }
                    if (isSatisfiedExpression) {
                        failedValidations.push(alertDefinition);
                    }
                }
                else {
                    if (this.IsAlertDefinitionTriggered(alertDefinition, cellDataChangedInfo)) {
                        failedValidations.push(alertDefinition);
                    }
                }
            });
        }
        return failedValidations;
    }
    performValidation(cellDataChangedInfo) {
        const failedRules = this.getValidationRulesForDataChange(cellDataChangedInfo);
        if (failedRules.length > 0) {
            let alert = {
                alertType: 'cellChanged',
                header: 'Alert',
                message: 'Perform Edit Alert Fired',
                alertDefinition: failedRules[0],
                cellDataChangedInfo: cellDataChangedInfo,
            };
            this.adaptableApi.eventApi.internalApi.fireAlertFiredEvent(alert);
            // This is not needed because the change event is triggered regardless
            // if the edit is succesful. This is because (I think) Adaptable uses a non-standard way ot detecting data changes.
            // https://github.com/AdaptableTools/adaptable/issues/2197
            // failedRules.forEach((alertDefinition) => {
            //   const alert: AdaptableAlert = ObjectFactory.CreateCellChangedAlert(
            //     cellDataChangedInfo.column.friendlyName,
            //     this.adaptableApi.alertApi.internalApi.getAlertDescription(
            //       alertDefinition,
            //       cellDataChangedInfo
            //     ),
            //     alertDefinition,
            //     cellDataChangedInfo
            //   );
            //   this.adaptableApi.alertApi.showAdaptableAlert(alert);
            // });
            return false;
        }
        return true;
    }
    IsAlertDefinitionTriggered(alertDefinition, dataChangedEvent) {
        const displayValue = this.adaptableApi.gridApi.getDisplayValueFromRowNode(dataChangedEvent.rowNode, dataChangedEvent.column.columnId);
        const predicateDefHandlerContext = {
            value: dataChangedEvent.newValue,
            oldValue: dataChangedEvent.oldValue,
            rawValue: dataChangedEvent.newValue,
            displayValue,
            node: dataChangedEvent.rowNode,
            column: dataChangedEvent.column,
            ...this.adaptableApi.internalApi.buildBaseContext(),
        };
        return this.adaptableApi.predicateApi.handlePredicates(alertDefinition.Rule?.Predicates, predicateDefHandlerContext, false);
    }
    performServerValidation(cellDataChangedInfo, config) {
        const serverValidationContext = {
            cellDataChangedInfo: cellDataChangedInfo,
            ...this.adaptableApi.internalApi.buildBaseContext(),
        };
        return () => {
            this.adaptableApi.optionsApi
                .getEditOptions()
                .validateOnServer(serverValidationContext)
                .then((validationResult) => {
                if (validationResult.newCellValue === undefined) {
                    validationResult.newCellValue = cellDataChangedInfo.newValue;
                }
                // If they have changed the return value then we should update the grid, log the function change
                // otherwise the value will persist
                if (validationResult.newCellValue !== cellDataChangedInfo.newValue) {
                    cellDataChangedInfo.newValue = validationResult.newCellValue;
                    // this.adaptable.setValue(cellDataChangedInfo, false);
                    const row = cellDataChangedInfo.rowNode?.data;
                    if (!row) {
                        return;
                    }
                    row[this.adaptableApi.columnApi.internalApi.getAgGridColumnFieldForAdaptableColumn(cellDataChangedInfo.column.columnId) ?? cellDataChangedInfo.column.columnId] = validationResult.newCellValue;
                    this.adaptableApi.gridApi.updateGridData([row]);
                    if (StringExtensions.IsNotNullOrEmpty(validationResult.validationMessage) &&
                        this.adaptableApi.optionsApi.getEditOptions().displayServerValidationMessages) {
                        const validationHeader = validationResult.validationHeader
                            ? validationResult.validationHeader
                            : SERVER_VALIDATION_HEADER;
                        const messageType = validationResult.messageType
                            ? validationResult.messageType
                            : SERVER_VALIDATION_MESSAGE_TYPE;
                        this.adaptableApi.alertApi.showAlert(validationHeader, validationResult.validationMessage, messageType);
                    }
                }
                config.onServerValidationCompleted();
            });
            return false;
        };
    }
    createValidationDescription(alertDefinition) {
        return (this.adaptableApi.columnScopeApi.getScopeDescription(alertDefinition.Scope) +
            ' ' +
            alertDefinition.Rule?.Predicates?.map((predicate) => this.adaptableApi.predicateApi.predicateToString(predicate)).join(' AND '));
    }
    createValidationMessage(alertDefinition) {
        let returnMessage = this.createValidationDescription(alertDefinition);
        if (alertDefinition.Rule.BooleanExpression) {
            returnMessage += ' when ' + alertDefinition.Rule.BooleanExpression;
        }
        return returnMessage;
    }
    destroy() {
        // TO DO
    }
}
