/**
 * 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 { computed, action, makeObservable, observable, flow, flowResult, } from 'mobx';
import { readFileAsText, assertErrorThrown, LogEvent, deepEqual, guaranteeType, isEmpty, ActionState, deleteEntry, assertTrue, generateEnumerableNameFromToken, } from '@finos/legend-shared';
import { GenerationPropertyItemType, CompilationError, ConfigurationProperty, ExternalFormatSchema as Schema, SchemaSet, isValidFullPath, resolvePackagePathAndElementName, Package, } from '@finos/legend-graph';
import { EntityChangeType } from '@finos/legend-server-sdlc';
import { ElementEditorState } from '../ElementEditorState.js';
import { LEGEND_STUDIO_APP_EVENT } from '../../../../../__lib__/LegendStudioEvent.js';
import { configurationProperty_addConfigurationProperty, configurationProperty_setValue, } from '../../../../graph-modifier/DSL_Generation_GraphModifierHelper.js';
export var SCHEMA_SET_TAB_TYPE;
(function (SCHEMA_SET_TAB_TYPE) {
    SCHEMA_SET_TAB_TYPE["SCHEMAS"] = "SCHEMAS";
    SCHEMA_SET_TAB_TYPE["GENERATE_MODEL"] = "GENERATE_MODEL";
})(SCHEMA_SET_TAB_TYPE || (SCHEMA_SET_TAB_TYPE = {}));
const DEFAULT_SCHEMA_NAME = 'MyShemaSet';
export class SchemaSetModelGenerationState {
    editorStore;
    schemaSet;
    generatingModelsState = ActionState.create();
    importGeneratedElementsState = ActionState.create();
    configurationProperties = [];
    generationValue = '';
    targetBinding = '';
    isolatedGraph;
    constructor(schemaSet, editorStore, graph) {
        this.schemaSet = schemaSet;
        this.editorStore = editorStore;
        makeObservable(this, {
            configurationProperties: observable,
            generatingModelsState: observable,
            importGeneratedElementsState: observable,
            generationValue: observable,
            targetBinding: observable,
            setConfigurationProperty: action,
            setGenerationValue: action,
            setTargetBindingPath: action,
            handleTargetBindingPathChange: action,
            updateGenerationParameters: action,
            canGenerate: computed,
            generate: flow,
            importGeneratedModelsIntoGraph: flow,
            getImportEntities: flow,
        });
        this.isolatedGraph = graph;
        assertTrue(this.isolatedGraph !== this.editorStore.graphManagerState.graph);
    }
    setConfigurationProperty(val) {
        this.configurationProperties = val;
    }
    get description() {
        return this.editorStore.graphState.graphGenerationState.externalFormatState.getTypeDescription(this.schemaSet.format);
    }
    get modelGenerationProperties() {
        return this.description?.modelGenerationProperties ?? [];
    }
    get canGenerate() {
        if (this.targetBinding === '') {
            return true;
        }
        return isValidFullPath(this.targetBinding);
    }
    setTargetBindingPath(val) {
        this.targetBinding = val;
    }
    getConfigValue(name) {
        return this.getConfig(name)?.value;
    }
    getConfig(name) {
        return this.configurationProperties.find((property) => name === property.name);
    }
    setGenerationValue(val) {
        this.generationValue = val;
    }
    handleTargetBindingPathChange() {
        const isolatedGraph = this.isolatedGraph;
        if (isolatedGraph && isValidFullPath(this.targetBinding)) {
            try {
                const [packagePath, bindingName] = resolvePackagePathAndElementName(this.targetBinding);
                const schemaName = `${DEFAULT_SCHEMA_NAME}For${bindingName}`;
                const _package = this.editorStore.graphManagerState.graph.getNullableElement(packagePath, true);
                let reservedNames = [];
                if (_package instanceof Package) {
                    reservedNames = _package.children.map((e) => e.name);
                }
                const newSchemaName = reservedNames.includes(schemaName)
                    ? generateEnumerableNameFromToken(reservedNames, schemaName)
                    : schemaName;
                const newPath = `${packagePath}::${newSchemaName}`;
                if (newPath !== this.schemaSet.path) {
                    isolatedGraph.renameElement(this.schemaSet, `${packagePath}::${newSchemaName}`);
                }
            }
            catch (error) {
                assertErrorThrown(error);
            }
        }
    }
    updateGenerationParameters(generationProperty, newValue) {
        if (generationProperty.type === GenerationPropertyItemType.MAP) {
            if (!newValue ||
                isEmpty(newValue) ||
                deepEqual(newValue, generationProperty.defaultValue)) {
                this.configurationProperties = this.configurationProperties.filter((e) => e.name !== generationProperty.name);
            }
            else {
                const configProperty = this.getConfig(generationProperty.name);
                if (configProperty) {
                    configurationProperty_setValue(configProperty, {
                        ...newValue,
                    });
                }
                else {
                    const newItem = new ConfigurationProperty(generationProperty.name, newValue);
                    configurationProperty_addConfigurationProperty(this.configurationProperties, newItem);
                }
            }
        }
        else {
            const configProperty = this.getConfig(generationProperty.name);
            let useDefaultValue = generationProperty.defaultValue === newValue;
            if (generationProperty.type === GenerationPropertyItemType.BOOLEAN) {
                useDefaultValue =
                    (generationProperty.defaultValue === 'true') ===
                        newValue;
            }
            const newConfigValue = useDefaultValue && !generationProperty.required ? undefined : newValue;
            if (newConfigValue !== undefined) {
                if (configProperty) {
                    configurationProperty_setValue(configProperty, newConfigValue);
                }
                else {
                    const newItem = new ConfigurationProperty(generationProperty.name, newConfigValue);
                    configurationProperty_addConfigurationProperty(this.configurationProperties, newItem);
                }
            }
            else {
                this.configurationProperties = this.configurationProperties.filter((e) => e.name !== generationProperty.name);
            }
        }
    }
    addInferredConfigurationProperties(properties) {
        const modelGenerationProperties = this.modelGenerationProperties;
        const SCHEMA_FORMAT_PROPERTY_NAME = 'format';
        if (!properties.find((e) => e.name === SCHEMA_FORMAT_PROPERTY_NAME)) {
            const genProperty = new ConfigurationProperty(SCHEMA_FORMAT_PROPERTY_NAME, this.schemaSet.format);
            properties.push(genProperty);
        }
        modelGenerationProperties
            .filter((property) => property.required &&
            property.defaultValue &&
            !properties.find((pv) => pv.name === property.name))
            .forEach((toAdd) => {
            const value = new ConfigurationProperty(toAdd.name, toAdd.defaultValue);
            configurationProperty_addConfigurationProperty(properties, value);
        });
    }
    *generate() {
        this.generatingModelsState.inProgress();
        this.editorStore.applicationStore.notificationService.setNotification(undefined);
        try {
            const properties = [...this.configurationProperties];
            this.addInferredConfigurationProperties(properties);
            const val = (yield this.editorStore.graphManagerState.graphManager.generateModelFromExternalFormat(this.schemaSet, this.targetBinding, properties, this.isolatedGraph ?? this.editorStore.graphManagerState.graph));
            this.setGenerationValue(val);
            return true;
        }
        catch (error) {
            assertErrorThrown(error);
            this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.EXTERNAL_FORMAT_FAILURE), error);
            this.editorStore.applicationStore.notificationService.notifyError(error);
            this.setGenerationValue('');
            return false;
        }
        finally {
            this.generatingModelsState.complete();
        }
    }
    *importGeneratedModelsIntoGraph() {
        try {
            this.importGeneratedElementsState.inProgress();
            const entities = (yield flowResult(this.getImportEntities()));
            const newEntities = entities.map((e) => ({
                type: EntityChangeType.CREATE,
                entityPath: e.path,
                content: e.content,
            }));
            yield flowResult(this.editorStore.graphState.loadEntityChangesToGraph(newEntities, undefined));
            this.editorStore.applicationStore.notificationService.notifySuccess('Generated elements imported into project');
        }
        catch (error) {
            assertErrorThrown(error);
            this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.EXTERNAL_FORMAT_FAILURE), error);
            this.editorStore.applicationStore.notificationService.notifyError(error);
        }
        finally {
            this.importGeneratedElementsState.complete();
        }
    }
    *getImportEntities() {
        try {
            this.importGeneratedElementsState.inProgress();
            const entities = (yield this.editorStore.graphManagerState.graphManager.pureCodeToEntities(this.generationValue));
            return entities;
        }
        catch (error) {
            assertErrorThrown(error);
            this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.EXTERNAL_FORMAT_FAILURE), error);
            this.editorStore.applicationStore.notificationService.notifyError(error);
            throw error;
        }
        finally {
            this.importGeneratedElementsState.complete();
        }
    }
}
export class ImportSchemaContentState {
    editorStore;
    schemaSetEditorState;
    loadingSchemaContentState = ActionState.create();
    files;
    importSchemaModal = false;
    constructor(schemaSetEditorState, editorStore) {
        this.editorStore = editorStore;
        this.schemaSetEditorState = schemaSetEditorState;
        makeObservable(this, {
            schemaSetEditorState: false,
            editorStore: false,
            files: observable,
            importSchemaModal: observable,
            loadingSchemaContentState: observable,
            removeFile: action,
            closeModal: action,
            importSchemas: flow,
        });
    }
    setImportSchemaModal(val) {
        this.importSchemaModal = val;
    }
    setFiles(files) {
        this.files = files;
    }
    removeFile(file) {
        if (this.files) {
            deleteEntry(this.files, file);
        }
    }
    closeModal() {
        this.files = undefined;
        this.setImportSchemaModal(false);
    }
    *importSchemas(files) {
        try {
            this.loadingSchemaContentState.inProgress();
            const schemaSet = this.schemaSetEditorState.schemaSet;
            yield Promise.all(files.map((file) => readFileAsText(file).then((content) => {
                const _schema = new Schema();
                _schema.location = file.name;
                _schema.content = content;
                // https://stackoverflow.com/questions/4250364
                _schema.id = file.name.replace(/\.[^/.]+$/, '');
                schemaSet.schemas.push(_schema);
            })));
            if (schemaSet.schemas.length) {
                this.schemaSetEditorState.setCurrentSchema(schemaSet.schemas[0]);
            }
            this.loadingSchemaContentState.complete();
            this.closeModal();
        }
        catch (error) {
            assertErrorThrown(error);
            this.editorStore.applicationStore.notificationService.notifyError(`Can't load patch: Error: ${error.message}`);
            this.loadingSchemaContentState.fail();
        }
    }
}
export class InnerSchemaSetEditorState {
    editorStore;
    isReadOnly;
    schemaSet;
    currentSchema;
    selectedTab = SCHEMA_SET_TAB_TYPE.SCHEMAS;
    schemaValidationError;
    importSchemaContentState;
    schemaSetModelGenerationState;
    constructor(isReadOnly, schemaSet, editorStore, graph) {
        this.editorStore = editorStore;
        makeObservable(this, {
            schemaValidationError: observable,
            currentSchema: observable,
            selectedTab: observable,
            schemaSet: observable,
            schemaSetModelGenerationState: observable,
            setSelectedTab: action,
            setCurrentSchema: action,
            setSchemaValidationerror: action,
            reset: action,
        });
        this.schemaSet = schemaSet;
        this.isReadOnly = isReadOnly;
        if (this.schemaSet.schemas.length !== 0) {
            this.currentSchema =
                this.schemaSet.schemas[this.schemaSet.schemas.length - 1];
        }
        this.schemaSetModelGenerationState = new SchemaSetModelGenerationState(this.schemaSet, this.editorStore, graph);
        this.importSchemaContentState = new ImportSchemaContentState(this, this.editorStore);
    }
    reset() {
        this.currentSchema = undefined;
        this.selectedTab = SCHEMA_SET_TAB_TYPE.SCHEMAS;
        this.schemaSetModelGenerationState = new SchemaSetModelGenerationState(this.schemaSet, this.editorStore, this.schemaSetModelGenerationState.isolatedGraph);
        this.importSchemaContentState = new ImportSchemaContentState(this, this.editorStore);
    }
    setCurrentSchema(value) {
        this.currentSchema = value;
    }
    setSelectedTab(tab) {
        this.selectedTab = tab;
    }
    setSchemaValidationerror(error) {
        this.schemaValidationError = error;
    }
}
export class SchemaSetEditorState extends ElementEditorState {
    currentSchema;
    selectedTab = SCHEMA_SET_TAB_TYPE.SCHEMAS;
    schemaValidationError;
    importSchemaContentState;
    schemaSetModelGenerationState;
    constructor(editorStore, element) {
        super(editorStore, element);
        makeObservable(this, {
            schemaValidationError: observable,
            currentSchema: observable,
            selectedTab: observable,
            schemaSet: computed,
            setSelectedTab: action,
            setCurrentSchema: action,
            reprocess: action,
            setSchemaValidationerror: action,
            validateSchema: flow,
        });
        if (this.schemaSet.schemas.length !== 0) {
            this.currentSchema =
                this.schemaSet.schemas[this.schemaSet.schemas.length - 1];
        }
        this.schemaSetModelGenerationState = new SchemaSetModelGenerationState(this.schemaSet, this.editorStore);
        this.importSchemaContentState = new ImportSchemaContentState(this, this.editorStore);
    }
    get schemaSet() {
        return guaranteeType(this.element, SchemaSet, 'Element inside schema set element editor state must be a SchemaSet');
    }
    setCurrentSchema(value) {
        this.currentSchema = value;
    }
    setSelectedTab(tab) {
        this.selectedTab = tab;
    }
    setSchemaValidationerror(error) {
        this.schemaValidationError = error;
    }
    reprocess(newElement, editorStore) {
        const schemaSetEditorState = new SchemaSetEditorState(editorStore, newElement);
        return schemaSetEditorState;
    }
    *validateSchema(currentSchema) {
        try {
            const _schemaSet = new SchemaSet(this.schemaSet.name);
            _schemaSet.format = this.schemaSet.format;
            _schemaSet.schemas = [currentSchema];
            const newGraph = this.editorStore.graphManagerState.createNewGraph();
            newGraph.addElement(_schemaSet, this.schemaSet.package?.path);
            yield this.editorStore.graphManagerState.graphManager.compileGraph(newGraph, {
                keepSourceInformation: true,
            });
        }
        catch (error) {
            assertErrorThrown(error);
            this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.EXTERNAL_FORMAT_FAILURE), error);
            if (error instanceof CompilationError) {
                this.setSchemaValidationerror(error);
            }
        }
    }
}
//# sourceMappingURL=DSL_ExternalFormat_SchemaSetEditorState.js.map