/**
 * 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 { observable, action, flow, makeObservable } from 'mobx';
import { assertErrorThrown, LogEvent, guaranteeNonNullable, } from '@finos/legend-shared';
import { LAMBDA_PIPE, GRAPH_MANAGER_EVENT, ParserError, RawLambda, buildSourceInformationSourceId, stub_RawLambda, isStubbed_RawLambda, getAllClassConstraints, getAllClassDerivedProperties, } from '@finos/legend-graph';
import { constraint_setFunctionDefinition, derivedProperty_setBody, derivedProperty_setParameters, } from '../../../graph-modifier/DomainGraphModifierHelper.js';
import { LambdaEditorState } from '@finos/legend-query-builder';
export const CONSTRAINT_SOURCE_ID_LABEL = 'constraint';
export const DERIVED_PROPERTY_SOURCE_ID_LABEL = 'derivedProperty';
export class DerivedPropertyState extends LambdaEditorState {
    derivedProperty;
    editorStore;
    constructor(derivedProperty, editorStore) {
        super(`${LAMBDA_PIPE}''`, '');
        makeObservable(this, {
            derivedProperty: observable,
            editorStore: observable,
            setBodyAndParameters: action,
        });
        this.derivedProperty = derivedProperty;
        this.editorStore = editorStore;
    }
    get lambdaId() {
        return buildSourceInformationSourceId([
            this.derivedProperty._OWNER.path,
            DERIVED_PROPERTY_SOURCE_ID_LABEL,
            this.derivedProperty.name,
            this.uuid, // in case of duplications
        ]);
    }
    setBodyAndParameters(lambda) {
        derivedProperty_setBody(this.derivedProperty, lambda.body);
        derivedProperty_setParameters(this.derivedProperty, lambda.parameters);
    }
    *convertLambdaGrammarStringToObject() {
        const emptyLambda = stub_RawLambda();
        if (this.lambdaString) {
            try {
                const lambda = (yield this.editorStore.graphManagerState.graphManager.pureCodeToLambda(this.fullLambdaString, this.lambdaId));
                this.setParserError(undefined);
                this.setBodyAndParameters(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.setBodyAndParameters(emptyLambda);
        }
    }
    *convertLambdaObjectToGrammarString(options) {
        if (this.derivedProperty.body) {
            try {
                const lambdas = new Map();
                lambdas.set(this.lambdaId, new RawLambda(this.derivedProperty.parameters, this.derivedProperty.body));
                const isolatedLambdas = (yield this.editorStore.graphManagerState.graphManager.lambdasToPureCode(lambdas, options?.pretty));
                const grammarText = isolatedLambdas.get(this.lambdaId);
                this.setLambdaString(grammarText !== undefined
                    ? this.extractLambdaString(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 ConstraintState extends LambdaEditorState {
    constraint;
    editorStore;
    constructor(constraint, editorStore) {
        super('true', LAMBDA_PIPE);
        makeObservable(this, {
            constraint: observable,
            editorStore: observable,
        });
        this.constraint = constraint;
        this.editorStore = editorStore;
    }
    get lambdaId() {
        return buildSourceInformationSourceId([
            this.constraint._OWNER.path,
            CONSTRAINT_SOURCE_ID_LABEL,
            this.constraint.name,
            this.uuid, // in case of duplications
        ]);
    }
    *convertLambdaGrammarStringToObject() {
        const emptyFunctionDefinition = stub_RawLambda();
        if (this.lambdaString) {
            try {
                const lambda = (yield this.editorStore.graphManagerState.graphManager.pureCodeToLambda(this.fullLambdaString, this.lambdaId));
                this.setParserError(undefined);
                constraint_setFunctionDefinition(this.constraint, 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();
            constraint_setFunctionDefinition(this.constraint, emptyFunctionDefinition);
        }
    }
    *convertLambdaObjectToGrammarString(options) {
        if (!isStubbed_RawLambda(this.constraint.functionDefinition)) {
            try {
                const lambdas = new Map();
                lambdas.set(this.lambdaId, this.constraint.functionDefinition);
                const isolatedLambdas = (yield this.editorStore.graphManagerState.graphManager.lambdasToPureCode(lambdas, options?.pretty));
                const grammarText = isolatedLambdas.get(this.lambdaId);
                this.setLambdaString(grammarText !== undefined
                    ? this.extractLambdaString(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('');
        }
    }
}
// NOTE: We went through the trouble of maintaining Class state outside of metamodel class prototype because we don't want to pollute
// metamodel models with UI states, this requires more effort to maintain but it will help in the long run
export class ClassState {
    editorStore;
    class;
    derivedPropertyStates = [];
    constraintStates = [];
    isConvertingConstraintLambdaObjects = false;
    isConvertingDerivedPropertyLambdaObjects = false;
    constructor(editorStore, _class) {
        makeObservable(this, {
            class: observable,
            derivedPropertyStates: observable,
            constraintStates: observable,
            isConvertingConstraintLambdaObjects: observable,
            isConvertingDerivedPropertyLambdaObjects: observable,
            addConstraintState: action,
            deleteConstraintState: action,
            addDerivedPropertyState: action,
            deleteDerivedPropertyState: action,
            convertConstraintLambdaObjects: flow,
            convertDerivedPropertyLambdaObjects: flow,
            decorate: action,
        });
        this.editorStore = editorStore;
        this.class = _class;
        this.constraintStates = getAllClassConstraints(_class).map((constraint) => new ConstraintState(constraint, this.editorStore));
        this.derivedPropertyStates = getAllClassDerivedProperties(_class).map((derivedProperty) => new DerivedPropertyState(derivedProperty, this.editorStore));
    }
    getNullableConstraintState = (constraint) => this.constraintStates.find((constraintState) => constraintState.constraint === constraint);
    getNullableDerivedPropertyState = (dp) => this.derivedPropertyStates.find((state) => state.derivedProperty === dp);
    getConstraintState = (constraint) => guaranteeNonNullable(this.getNullableConstraintState(constraint), `Can't find constraint state for constraint ${constraint}`);
    getDerivedPropertyState = (dp) => guaranteeNonNullable(this.getNullableDerivedPropertyState(dp), `Can't find derived property state for derived property ${dp}`);
    addConstraintState(constraint) {
        if (!this.constraintStates.find((constraintState) => constraintState.constraint === constraint)) {
            this.constraintStates.push(new ConstraintState(constraint, this.editorStore));
        }
    }
    deleteConstraintState(constraint) {
        const idx = this.constraintStates.findIndex((constraintState) => constraintState.constraint === constraint);
        if (idx !== -1) {
            this.constraintStates.splice(idx, 1);
        }
    }
    addDerivedPropertyState(derivedProperty) {
        if (!this.derivedPropertyStates.find((state) => state.derivedProperty === derivedProperty)) {
            this.derivedPropertyStates.push(new DerivedPropertyState(derivedProperty, this.editorStore));
        }
    }
    deleteDerivedPropertyState(derivedProperty) {
        const idx = this.derivedPropertyStates.findIndex((state) => state.derivedProperty === derivedProperty);
        if (idx !== -1) {
            this.derivedPropertyStates.splice(idx, 1);
        }
    }
    *convertConstraintLambdaObjects() {
        const lambdas = new Map();
        const index = new Map();
        this.constraintStates.forEach((constraintState) => {
            if (!isStubbed_RawLambda(constraintState.constraint.functionDefinition)) {
                lambdas.set(constraintState.lambdaId, constraintState.constraint.functionDefinition);
                index.set(constraintState.lambdaId, constraintState);
            }
        });
        if (lambdas.size) {
            this.isConvertingConstraintLambdaObjects = true;
            try {
                const isolatedLambdas = (yield this.editorStore.graphManagerState.graphManager.lambdasToPureCode(lambdas));
                isolatedLambdas.forEach((grammarText, key) => {
                    const constraintState = index.get(key);
                    constraintState?.setLambdaString(constraintState.extractLambdaString(grammarText));
                });
            }
            catch (error) {
                assertErrorThrown(error);
                this.editorStore.applicationStore.logService.error(LogEvent.create(GRAPH_MANAGER_EVENT.PARSING_FAILURE), error);
            }
            finally {
                this.isConvertingConstraintLambdaObjects = false;
            }
        }
    }
    *convertDerivedPropertyLambdaObjects() {
        const lambdas = new Map();
        const index = new Map();
        this.derivedPropertyStates.forEach((state) => {
            const lambda = new RawLambda(state.derivedProperty.parameters, state.derivedProperty.body);
            if (!isStubbed_RawLambda(lambda)) {
                lambdas.set(state.lambdaId, lambda);
                index.set(state.lambdaId, state);
            }
        });
        if (lambdas.size) {
            this.isConvertingDerivedPropertyLambdaObjects = true;
            try {
                const isolatedLambdas = (yield this.editorStore.graphManagerState.graphManager.lambdasToPureCode(lambdas));
                isolatedLambdas.forEach((grammarText, key) => {
                    const derivedPropertyState = index.get(key);
                    derivedPropertyState?.setLambdaString(derivedPropertyState.extractLambdaString(grammarText));
                });
            }
            catch (error) {
                assertErrorThrown(error);
                this.editorStore.applicationStore.logService.error(LogEvent.create(GRAPH_MANAGER_EVENT.PARSING_FAILURE), error);
            }
            finally {
                this.isConvertingDerivedPropertyLambdaObjects = false;
            }
        }
    }
    decorate() {
        this.constraintStates = getAllClassConstraints(this.class).map((constraint) => this.constraintStates.find((constraintState) => constraintState.constraint === constraint) ?? new ConstraintState(constraint, this.editorStore));
        this.derivedPropertyStates = getAllClassDerivedProperties(this.class).map((dp) => this.derivedPropertyStates.find((state) => state.derivedProperty === dp) ?? new DerivedPropertyState(dp, this.editorStore));
    }
}
//# sourceMappingURL=ClassState.js.map