/**
 * 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, UnsupportedOperationError, guaranteeType, IllegalStateError, } from '@finos/legend-shared';
import { MappingElementDecorator } from './MappingElementDecorator.js';
import { LAMBDA_PIPE, GRAPH_MANAGER_EVENT, ParserError, FlatDataPropertyMapping, EmbeddedFlatDataPropertyMapping, Class, InferableMappingElementIdExplicitValue, PackageableElementExplicitReference, PropertyExplicitReference, buildSourceInformationSourceId, stub_RawLambda, isStubbed_RawLambda, SetImplementationExplicitReference, } from '@finos/legend-graph';
import { MAPPING_ELEMENT_TYPE } from './MappingEditorState.js';
export class FlatDataPropertyMappingState extends PropertyMappingState {
    editorStore;
    constructor(editorStore, instanceSetImplementationState, propertyMapping) {
        super(instanceSetImplementationState, propertyMapping, '', LAMBDA_PIPE);
        this.propertyMapping = propertyMapping;
        this.editorStore = editorStore;
    }
    get lambdaId() {
        return buildSourceInformationSourceId([
            this.propertyMapping._OWNER._PARENT.path,
            MAPPING_ELEMENT_TYPE.CLASS,
            this.propertyMapping._OWNER.id.value,
            this.propertyMapping.property.value.name,
            this.uuid, // in case of duplications
        ]);
    }
    *convertLambdaGrammarStringToObject() {
        const emptyLambda = stub_RawLambda();
        if (this.lambdaString) {
            try {
                const lambda = (yield this.editorStore.graphManagerState.graphManager.pureCodeToLambda(this.fullLambdaString, this.lambdaId));
                this.setParserError(undefined);
                if (this.propertyMapping instanceof FlatDataPropertyMapping) {
                    this.propertyMapping.transform = 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();
            if (this.propertyMapping instanceof FlatDataPropertyMapping) {
                this.propertyMapping.transform = emptyLambda;
            }
        }
    }
    *convertLambdaObjectToGrammarString(options) {
        if (this.propertyMapping instanceof FlatDataPropertyMapping) {
            if (!isStubbed_RawLambda(this.propertyMapping.transform)) {
                try {
                    const lambdas = new Map();
                    lambdas.set(this.lambdaId, this.propertyMapping.transform);
                    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 FlatDataInstanceSetImplementationState extends InstanceSetImplementationState {
    isConvertingTransformLambdaObjects = false;
    constructor(editorStore, setImplementation) {
        super(editorStore, setImplementation);
        makeObservable(this, {
            isConvertingTransformLambdaObjects: observable,
            hasParserError: computed,
            setPropertyMappingStates: action,
            addEmbeddedPropertyMapping: action,
        });
        this.mappingElement = setImplementation;
    }
    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 lambdas = new Map();
        const propertyMappingStates = new Map();
        this.propertyMappingStates.forEach((pm) => {
            if (pm.propertyMapping instanceof FlatDataPropertyMapping &&
                !isStubbed_RawLambda(pm.propertyMapping.transform)) {
                lambdas.set(pm.lambdaId, pm.propertyMapping.transform);
                propertyMappingStates.set(pm.lambdaId, pm);
            }
            // we don't have to do anything for embedded. they don't have a transform and do not require converting back and form.
        });
        if (lambdas.size) {
            this.isConvertingTransformLambdaObjects = true;
            try {
                const isolatedLambdas = (yield this.editorStore.graphManagerState.graphManager.lambdasToPureCode(lambdas));
                isolatedLambdas.forEach((grammarText, key) => {
                    const flatDataPropertyMappingState = propertyMappingStates.get(key);
                    flatDataPropertyMappingState?.setLambdaString(flatDataPropertyMappingState.extractLambdaString(grammarText));
                });
            }
            catch (error) {
                assertErrorThrown(error);
                this.editorStore.applicationStore.logService.error(LogEvent.create(GRAPH_MANAGER_EVENT.PARSING_FAILURE), error);
            }
            finally {
                this.isConvertingTransformLambdaObjects = false;
            }
        }
    }
    addEmbeddedPropertyMapping(property) {
        const rootInstanceSetImplementation = this.mappingElement instanceof EmbeddedFlatDataPropertyMapping
            ? this.mappingElement.rootInstanceSetImplementation
            : this.mappingElement;
        const _class = guaranteeType(property.genericType.value.rawType, Class);
        const embeddedPropertyMapping = new EmbeddedFlatDataPropertyMapping(this.mappingElement, PropertyExplicitReference.create(property), rootInstanceSetImplementation, SetImplementationExplicitReference.create(this.mappingElement), PackageableElementExplicitReference.create(_class), InferableMappingElementIdExplicitValue.create(`${this.mappingElement.id.value}.${property.name}`, ''), undefined);
        embeddedPropertyMapping.targetSetImplementation =
            SetImplementationExplicitReference.create(embeddedPropertyMapping);
        this.mappingElement.propertyMappings.push(embeddedPropertyMapping);
        return embeddedPropertyMapping;
    }
}
export class EmbeddedFlatDataInstanceSetImplementationState extends FlatDataInstanceSetImplementationState {
    constructor(editorStore, instanceSetImplementationState, setImplementation) {
        super(editorStore, setImplementation);
        this.instanceSetImplementationState = instanceSetImplementationState;
        this.mappingElement = setImplementation;
        this.propertyMapping = setImplementation;
        this.propertyMappingStates = this.getPropertyMappingStates(setImplementation.propertyMappings);
    }
    typeAheadEnabled = false;
    getCodeComplete(input) {
        throw new Error('Method not implemented.');
    }
    setTypeAhead(val) {
        this.typeAheadEnabled = val;
    }
    get lambdaId() {
        throw new IllegalStateError(`Can't build lambda ID for embedded flat data instance set implementation state`);
    }
    getPropertyMappingStates(propertyMappings) {
        return propertyMappings.map((pm) => {
            if (pm instanceof FlatDataPropertyMapping) {
                return new FlatDataPropertyMappingState(this.editorStore, this.instanceSetImplementationState, pm);
            }
            else if (pm instanceof EmbeddedFlatDataPropertyMapping) {
                return new EmbeddedFlatDataInstanceSetImplementationState(this.editorStore, this.instanceSetImplementationState, pm);
            }
            throw new UnsupportedOperationError();
        });
    }
    // dummy lambda editor states needed because embedded flat-data should be seen as `PropertyMappingState`
    lambdaPrefix = '';
    lambdaString = '';
    parserError;
    compilationError;
    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();
    }
}
export class RootFlatDataInstanceSetImplementationState extends FlatDataInstanceSetImplementationState {
    constructor(editorStore, setImplementation) {
        super(editorStore, setImplementation);
        this.mappingElement = setImplementation;
        this.propertyMappingStates = this.getPropertyMappingStates(setImplementation.propertyMappings);
    }
    getPropertyMappingStates(propertyMappings) {
        return propertyMappings.map((propertyMapping) => {
            if (propertyMapping instanceof FlatDataPropertyMapping) {
                return new FlatDataPropertyMappingState(this.editorStore, this, propertyMapping);
            }
            else if (propertyMapping instanceof EmbeddedFlatDataPropertyMapping) {
                return new EmbeddedFlatDataInstanceSetImplementationState(this.editorStore, this, propertyMapping);
            }
            throw new UnsupportedOperationError();
        });
    }
}
//# sourceMappingURL=FlatDataInstanceSetImplementationState.js.map