/**
 * 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, computed, makeObservable } from 'mobx';
import { InstanceSetImplementationState, PropertyMappingState, } from '../MappingElementState.js';
import { assertErrorThrown, LogEvent, IllegalStateError, isNonNullable, UnsupportedOperationError, } from '@finos/legend-shared';
import { MappingElementDecorator } from '../MappingElementDecorator.js';
import { MAPPING_ELEMENT_TYPE } from '../MappingEditorState.js';
import { RelationalPropertyMapping, ParserError, GRAPH_MANAGER_EVENT, EmbeddedRelationalInstanceSetImplementation, buildSourceInformationSourceId, isStubbed_RawRelationalOperationElement, stub_RawRelationalOperationElement, } from '@finos/legend-graph';
export class RelationalPropertyMappingState extends PropertyMappingState {
    editorStore;
    constructor(editorStore, instanceSetImplementationState, propertyMapping) {
        super(instanceSetImplementationState, propertyMapping, '', '');
        this.propertyMapping = propertyMapping;
        this.editorStore = editorStore;
    }
    // NOTE: `operationId` is properly the more appropriate term to use, but we are just following what we
    // do for other property mapping for consistency
    get lambdaId() {
        // NOTE: Added the index here just in case but the order needs to be checked carefully as bugs may result from inaccurate orderings
        return buildSourceInformationSourceId([
            this.propertyMapping._OWNER._PARENT.path,
            MAPPING_ELEMENT_TYPE.CLASS,
            this.propertyMapping._OWNER.id.value,
            this.propertyMapping.property.value.name,
            this.propertyMapping.targetSetImplementation?.value.id.value,
            this.uuid, // in case of duplications
        ].filter(isNonNullable));
    }
    *convertLambdaGrammarStringToObject() {
        const stubOperation = stub_RawRelationalOperationElement();
        if (this.lambdaString) {
            try {
                const operation = (yield this.editorStore.graphManagerState.graphManager.pureCodeToRelationalOperationElement(this.fullLambdaString, this.lambdaId));
                this.setParserError(undefined);
                if (this.propertyMapping instanceof RelationalPropertyMapping) {
                    this.propertyMapping.relationalOperation = operation;
                }
            }
            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();
            if (this.propertyMapping instanceof RelationalPropertyMapping) {
                this.propertyMapping.relationalOperation = stubOperation;
            }
        }
    }
    *convertLambdaObjectToGrammarString(options) {
        if (this.propertyMapping instanceof RelationalPropertyMapping) {
            if (!isStubbed_RawRelationalOperationElement(this.propertyMapping)) {
                try {
                    const operations = new Map();
                    operations.set(this.lambdaId, this.propertyMapping.relationalOperation);
                    const operationsInText = (yield this.editorStore.graphManagerState.graphManager.relationalOperationElementToPureCode(operations));
                    const grammarText = operationsInText.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 RelationalInstanceSetImplementationState extends InstanceSetImplementationState {
}
export class EmbeddedRelationalInstanceSetImplementationState extends RelationalInstanceSetImplementationState {
    typeAheadEnabled = false;
    constructor(editorStore, instanceSetImplementationState, setImplementation) {
        super(editorStore, setImplementation);
        this.instanceSetImplementationState = instanceSetImplementationState;
        this.mappingElement = setImplementation;
        this.propertyMapping = setImplementation;
        this.propertyMappingStates = this.getPropertyMappingStates(setImplementation.propertyMappings);
    }
    setTypeAhead(val) {
        this.typeAheadEnabled = val;
    }
    get lambdaId() {
        throw new IllegalStateError(`Can't build lambda ID for embedded relational instance set implementation state`);
    }
    getPropertyMappingStates(propertyMappings) {
        // TODO
        return [];
    }
    // dummy lambda editor states needed because embedded flat-data should be seen as `PropertyMappingState`
    lambdaPrefix = '';
    lambdaString = '';
    parserError;
    compilationError;
    decorate() {
        return;
    }
    *convertPropertyMappingTransformObjects() {
        throw new UnsupportedOperationError();
    }
    setLambdaString(val) {
        return;
    }
    setParserError(error) {
        return;
    }
    setCompilationError(error) {
        // TODO
        return;
    }
    get fullLambdaString() {
        throw new UnsupportedOperationError();
    }
    processSourceInformation(sourceInformation) {
        throw new UnsupportedOperationError();
    }
    extractLambdaString(fullLambdaString) {
        throw new UnsupportedOperationError();
    }
    clearErrors(options) {
        // TODO
        return;
    }
    *convertLambdaGrammarStringToObject() {
        throw new UnsupportedOperationError();
    }
    *convertLambdaObjectToGrammarString(options) {
        throw new UnsupportedOperationError();
    }
    getCodeComplete(input) {
        throw new Error('Method not implemented.');
    }
}
export class RootRelationalInstanceSetImplementationState extends RelationalInstanceSetImplementationState {
    isConvertingTransformLambdaObjects = false;
    constructor(editorStore, setImplementation) {
        super(editorStore, setImplementation);
        makeObservable(this, {
            isConvertingTransformLambdaObjects: observable,
            hasParserError: computed,
            setPropertyMappingStates: action,
        });
        this.mappingElement = setImplementation;
        this.propertyMappingStates = this.getPropertyMappingStates(setImplementation.propertyMappings);
    }
    getPropertyMappingStates(propertyMappings) {
        return propertyMappings
            .map((pm) => {
            if (pm instanceof RelationalPropertyMapping) {
                return new RelationalPropertyMappingState(this.editorStore, this, pm);
            }
            else if (pm instanceof EmbeddedRelationalInstanceSetImplementation) {
                return new EmbeddedRelationalInstanceSetImplementationState(this.editorStore, this, pm);
            }
            return undefined;
        })
            .filter(isNonNullable);
    }
    get hasParserError() {
        return this.propertyMappingStates.some((propertyMappingState) => propertyMappingState.parserError);
    }
    setPropertyMappingStates(propertyMappingState) {
        this.propertyMappingStates = propertyMappingState;
    }
    /**
     * When we decorate, we might lose the error (parser/compiler) on each of the property mapping state
     * so here we make sure that we reuse existing state and only add new decorated ones
     */
    decorate() {
        this.mappingElement.accept_SetImplementationVisitor(new MappingElementDecorator(this.editorStore));
        const newPropertyMappingStates = [];
        const propertyMappingstatesAfterDecoration = this.getPropertyMappingStates(this.mappingElement.propertyMappings);
        propertyMappingstatesAfterDecoration.forEach((propertyMappingState) => {
            const existingPropertyMappingState = this.propertyMappingStates.find((p) => p.propertyMapping === propertyMappingState.propertyMapping);
            newPropertyMappingStates.push(existingPropertyMappingState ?? propertyMappingState);
        });
        this.setPropertyMappingStates(newPropertyMappingStates);
    }
    *convertPropertyMappingTransformObjects() {
        const operations = new Map();
        const propertyMappingStates = new Map();
        this.propertyMappingStates.forEach((pmState) => {
            if (pmState.propertyMapping instanceof RelationalPropertyMapping &&
                !isStubbed_RawRelationalOperationElement(pmState.propertyMapping.relationalOperation)) {
                operations.set(pmState.lambdaId, pmState.propertyMapping.relationalOperation);
                propertyMappingStates.set(pmState.lambdaId, pmState);
            }
            // we don't have to do anything for embedded. they don't have a transform and do not require converting back and form.
        });
        if (operations.size) {
            this.isConvertingTransformLambdaObjects = true;
            try {
                const operationsInText = (yield this.editorStore.graphManagerState.graphManager.relationalOperationElementToPureCode(operations));
                operationsInText.forEach((grammarText, key) => {
                    const relationalPropertyMappingState = propertyMappingStates.get(key);
                    relationalPropertyMappingState?.setLambdaString(relationalPropertyMappingState.extractLambdaString(grammarText));
                });
            }
            catch (error) {
                assertErrorThrown(error);
                this.editorStore.applicationStore.logService.error(LogEvent.create(GRAPH_MANAGER_EVENT.PARSING_FAILURE), error);
            }
            finally {
                this.isConvertingTransformLambdaObjects = false;
            }
        }
    }
}
//# sourceMappingURL=RelationalInstanceSetImplementationState.js.map