/**
 * 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 { GRAPH_MANAGER_EVENT, LAMBDA_PIPE, ParserError, PostValidation, PostValidationAssertion, RawLambda, isStubbed_RawLambda, stub_RawLambda, } from '@finos/legend-graph';
import { serviceValidation_addAssertion, serviceValidation_addParam, serviceValidation_deleteAssertion, serviceValidation_deleteParam, service_addValidation, service_deleteValidation, } from '../../../../graph-modifier/DSL_Service_GraphModifierHelper.js';
import { action, flow, flowResult, makeObservable, observable } from 'mobx';
import { LambdaEditorState, buildDefaultEmptyStringRawLambda, } from '@finos/legend-query-builder';
import { assertErrorThrown, LogEvent, generateEnumerableNameFromToken, ActionState, guaranteeNonNullable, } from '@finos/legend-shared';
export class PostValidationAssertionState extends LambdaEditorState {
    editorStore;
    postValidationState;
    assertion;
    constructor(assertion, validation, editorStore) {
        super('', LAMBDA_PIPE);
        makeObservable(this, {
            assertion: observable,
        });
        this.postValidationState = validation;
        this.editorStore = editorStore;
        this.assertion = assertion;
    }
    // TODO add lookup logic
    get lambdaId() {
        return this.uuid;
    }
    *convertLambdaGrammarStringToObject() {
        const emptyLambda = stub_RawLambda();
        if (this.lambdaString) {
            try {
                const lambda = (yield this.editorStore.graphManagerState.graphManager.pureCodeToLambda(this.lambdaString, this.lambdaId));
                this.setParserError(undefined);
                this.assertion.assertion = lambda;
            }
            catch (error) {
                assertErrorThrown(error);
                if (error instanceof ParserError) {
                    this.setParserError(error);
                }
                this.editorStore.applicationStore.logService.error(LogEvent.create(GRAPH_MANAGER_EVENT.PARSING_FAILURE), error);
            }
        }
        else {
            this.clearErrors();
            this.assertion.assertion = emptyLambda;
        }
    }
    *convertLambdaObjectToGrammarString(options) {
        if (!isStubbed_RawLambda(this.assertion.assertion)) {
            try {
                const lambdas = new Map();
                lambdas.set(this.lambdaId, this.assertion.assertion);
                const isolatedLambdas = (yield this.editorStore.graphManagerState.graphManager.lambdasToPureCode(lambdas, options?.pretty));
                const grammarText = isolatedLambdas.get(this.lambdaId);
                this.setLambdaString(grammarText ?? '');
                this.clearErrors({
                    preserveCompilationError: options?.preserveCompilationError,
                });
            }
            catch (error) {
                assertErrorThrown(error);
                this.editorStore.applicationStore.logService.error(LogEvent.create(GRAPH_MANAGER_EVENT.PARSING_FAILURE), error);
            }
        }
        else {
            this.clearErrors();
            this.setLambdaString('');
        }
    }
}
export class PostValidationParameterState extends LambdaEditorState {
    editorStore;
    postValidationState;
    parameters;
    idx;
    constructor(validationState, idx, editorStore) {
        super('', LAMBDA_PIPE);
        makeObservable(this, {
            setLambda: action,
            parameters: observable,
            reProcess: observable,
        });
        this.postValidationState = validationState;
        this.editorStore = editorStore;
        this.idx = idx;
        this.parameters = validationState.validation.parameters;
    }
    get validation() {
        return this.postValidationState.validation;
    }
    reProcess(parameters, idx) {
        this.parameters = parameters;
        this.idx = idx;
    }
    setLambda(val) {
        this.parameters[this.idx] = val;
    }
    get lambda() {
        return this.validation.parameters[this.idx] ?? stub_RawLambda();
    }
    get lambdaId() {
        return this.uuid;
    }
    *convertLambdaGrammarStringToObject() {
        const emptyLambda = stub_RawLambda();
        if (this.lambdaString) {
            try {
                const lambda = (yield this.editorStore.graphManagerState.graphManager.pureCodeToLambda(this.lambdaString, this.lambdaId));
                this.setParserError(undefined);
                this.setLambda(lambda);
            }
            catch (error) {
                assertErrorThrown(error);
                if (error instanceof ParserError) {
                    this.setParserError(error);
                }
                this.editorStore.applicationStore.logService.error(LogEvent.create(GRAPH_MANAGER_EVENT.PARSING_FAILURE), error);
            }
        }
        else {
            this.clearErrors();
            this.setLambda(emptyLambda);
        }
    }
    *convertLambdaObjectToGrammarString(options) {
        if (this.lambda.body) {
            try {
                const lambdas = new Map();
                lambdas.set(this.lambdaId, new RawLambda(this.lambda.parameters, this.lambda.body));
                const isolatedLambdas = (yield this.editorStore.graphManagerState.graphManager.lambdasToPureCode(lambdas, options?.pretty));
                const grammarText = isolatedLambdas.get(this.lambdaId);
                this.setLambdaString(grammarText ?? '');
                this.clearErrors({
                    preserveCompilationError: options?.preserveCompilationError,
                });
            }
            catch (error) {
                assertErrorThrown(error);
                this.editorStore.applicationStore.logService.error(LogEvent.create(GRAPH_MANAGER_EVENT.PARSING_FAILURE), error);
            }
        }
        else {
            this.clearErrors();
            this.setLambdaString('');
        }
    }
}
export class PostValidationState {
    servicePostValidationState;
    validation;
    assertionStates = [];
    parametersState = [];
    isConvertingParameterLambdasState = ActionState.create();
    isConvertingAssertionLambdasState = ActionState.create();
    constructor(validation, servicePostValidationState) {
        makeObservable(this, {
            validation: observable,
            deleteAssertion: action,
            addParameter: flow,
            deleteParam: action,
            reprocessParamState: action,
            isConvertingParameterLambdasState: observable,
            isConvertingAssertionLambdasState: observable,
            convertAssertionsLambdas: flow,
            convertParameterLambdas: flow,
            addAssertion: flow,
        });
        this.validation = validation;
        this.servicePostValidationState = servicePostValidationState;
        this.assertionStates = validation.assertions.map((e) => new PostValidationAssertionState(e, this, this.servicePostValidationState.editorStore));
        this.reprocessParamState();
    }
    get hasParserError() {
        return this.assertionStates.some((aState) => aState.parserError);
    }
    get isRunningLambdaConversion() {
        return (this.isConvertingAssertionLambdasState.isInProgress ||
            this.isConvertingParameterLambdasState.isInProgress);
    }
    reprocessParamState() {
        this.parametersState = this.validation.parameters.map((e, idx) => new PostValidationParameterState(this, idx, this.servicePostValidationState.editorStore));
    }
    *convertAssertionsLambdas() {
        const assertionsLambdas = new Map();
        const assertionColumnStateMap = new Map();
        this.assertionStates.forEach((assertionState) => {
            if (!isStubbed_RawLambda(assertionState.assertion.assertion)) {
                assertionsLambdas.set(assertionState.lambdaId, assertionState.assertion.assertion);
                assertionColumnStateMap.set(assertionState.lambdaId, assertionState);
            }
        });
        if (assertionsLambdas.size) {
            this.isConvertingAssertionLambdasState.inProgress();
            try {
                const isolatedLambdas = (yield this.servicePostValidationState.editorStore.graphManagerState.graphManager.lambdasToPureCode(assertionsLambdas));
                isolatedLambdas.forEach((grammarText, key) => {
                    const assertionColState = assertionColumnStateMap.get(key);
                    assertionColState?.setLambdaString(grammarText);
                });
            }
            catch (error) {
                assertErrorThrown(error);
                this.servicePostValidationState.editorStore.applicationStore.logService.error(LogEvent.create(GRAPH_MANAGER_EVENT.PARSING_FAILURE), error);
            }
            finally {
                this.isConvertingAssertionLambdasState.complete();
            }
        }
    }
    *convertParameterLambdas() {
        const parameterLambas = new Map();
        const parameterLambasMap = new Map();
        this.parametersState.forEach((paramState) => {
            if (!isStubbed_RawLambda(paramState.lambda)) {
                parameterLambas.set(paramState.lambdaId, paramState.lambda);
                parameterLambasMap.set(paramState.lambdaId, paramState);
            }
        });
        if (parameterLambas.size) {
            this.isConvertingParameterLambdasState.inProgress();
            try {
                const isolatedParamsLambdas = (yield this.servicePostValidationState.editorStore.graphManagerState.graphManager.lambdasToPureCode(parameterLambas));
                isolatedParamsLambdas.forEach((grammarText, key) => {
                    const paramColState = parameterLambasMap.get(key);
                    paramColState?.setLambdaString(grammarText);
                });
            }
            catch (error) {
                assertErrorThrown(error);
                this.servicePostValidationState.editorStore.applicationStore.logService.error(LogEvent.create(GRAPH_MANAGER_EVENT.PARSING_FAILURE), error);
            }
            finally {
                this.isConvertingParameterLambdasState.complete();
            }
        }
    }
    *addParameter() {
        const _param = buildDefaultEmptyStringRawLambda(this.servicePostValidationState.editorStore.graphManagerState, this.servicePostValidationState.editorStore.changeDetectionState
            .observerContext);
        serviceValidation_addParam(this.validation, _param);
        const idx = this.validation.parameters.findIndex((x) => x === _param);
        const _paramState = new PostValidationParameterState(this, idx, this.servicePostValidationState.editorStore);
        this.parametersState.push(_paramState);
        yield flowResult(_paramState.convertLambdaObjectToGrammarString({ pretty: false }));
    }
    deleteParam(paramState) {
        const val = paramState.lambda;
        serviceValidation_deleteParam(this.validation, val);
        this.parametersState = this.parametersState.filter((e) => e !== paramState);
        this.parametersState.forEach((p, idx) => p.reProcess(this.validation.parameters, idx));
    }
    *addAssertion() {
        const _assertion = new PostValidationAssertion();
        _assertion.id = generateEnumerableNameFromToken(this.validation.assertions.map((e) => e.id), 'assertion_id');
        _assertion.assertion = buildDefaultEmptyStringRawLambda(this.servicePostValidationState.editorStore.graphManagerState, this.servicePostValidationState.editorStore.changeDetectionState
            .observerContext);
        serviceValidation_addAssertion(this.validation, _assertion);
        const aState = new PostValidationAssertionState(_assertion, this, this.servicePostValidationState.editorStore);
        this.assertionStates.push(aState);
        yield flowResult(aState.convertLambdaObjectToGrammarString({ pretty: false }));
    }
    deleteAssertion(assertion) {
        const _assertionState = this.assertionStates.find((e) => e.assertion === assertion);
        serviceValidation_deleteAssertion(this.validation, assertion);
        this.assertionStates = this.assertionStates.filter((e) => e !== _assertionState);
    }
}
export class ServicePostValidationsState {
    serviceEditorState;
    editorStore;
    selectedPostValidationState;
    runningPostValidationAction = ActionState.create();
    postValidationAssertionResults;
    constructor(serviceEditorState) {
        makeObservable(this, {
            selectedPostValidationState: observable,
            postValidationAssertionResults: observable,
            init: action,
            buildPostValidationState: action,
            addValidation: action,
            deleteValidation: action,
            changeValidation: action,
            runVal: flow,
        });
        this.serviceEditorState = serviceEditorState;
        this.editorStore = serviceEditorState.editorStore;
        // this.init();
    }
    get service() {
        return this.serviceEditorState.service;
    }
    get postValidations() {
        return this.serviceEditorState.service.postValidations;
    }
    init() {
        const currentVal = this.selectedPostValidationState?.validation;
        if (!currentVal || !this.postValidations.includes(currentVal)) {
            const validation = this.serviceEditorState.service.postValidations[0];
            if (validation) {
                this.selectedPostValidationState =
                    this.buildPostValidationState(validation);
            }
            else {
                this.selectedPostValidationState = undefined;
            }
        }
    }
    buildPostValidationState(validation) {
        return new PostValidationState(validation, this);
    }
    deleteValidation(validation) {
        service_deleteValidation(this.serviceEditorState.service, validation);
        this.init();
    }
    addValidation() {
        const val = new PostValidation();
        val.description = `Description for validation ${this.postValidations.length + 1}`;
        service_addValidation(this.serviceEditorState.service, val);
        this.selectedPostValidationState = this.buildPostValidationState(val);
    }
    changeValidation(validation) {
        this.selectedPostValidationState =
            this.buildPostValidationState(validation);
    }
    // Fetches validation results. Caller of validation should catch the error
    async fetchValidationResult() {
        const validation = guaranteeNonNullable(this.selectedPostValidationState?.validation);
        return Promise.all(validation.assertions.map(async (assertion) => {
            const assertionId = guaranteeNonNullable(assertion.id);
            const testResults = await this.editorStore.graphManagerState.graphManager
                .runServicePostValidations(this.service, this.editorStore.graphManagerState.graph, assertionId)
                .then((data) => data);
            return guaranteeNonNullable(testResults);
        }));
    }
    *runVal() {
        try {
            this.runningPostValidationAction.inProgress();
            this.postValidationAssertionResults = undefined;
            this.postValidationAssertionResults = (yield flowResult(this.fetchValidationResult()));
            this.runningPostValidationAction.complete();
        }
        catch (error) {
            assertErrorThrown(error);
            this.editorStore.applicationStore.notificationService.notifyError(`Error running validation: ${error.message}`);
            this.runningPostValidationAction.fail();
        }
    }
}
//# sourceMappingURL=ServicePostValidationState.js.map