/**
 * 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, computed, flowResult, } from 'mobx';
import { EditorState } from './EditorState.js';
import { assertErrorThrown, LogEvent, UnsupportedOperationError, isNonNullable, ActionState, assertTrue, } from '@finos/legend-shared';
import { LEGEND_STUDIO_APP_EVENT } from '../../../__lib__/LegendStudioEvent.js';
import { generateGAVCoordinates } from '@finos/legend-storage';
import { DEFAULT_TAB_SIZE } from '@finos/legend-application';
import { GraphEntities, LegendSDLC, SchemaSet, observe_SchemaSet, } from '@finos/legend-graph';
import { externalFormat_schemaSet_setFormat, externalFormat_schemaSet_setSchemas, } from '../../graph-modifier/DSL_ExternalFormat_GraphModifierHelper.js';
import { InnerSchemaSetEditorState } from './element-editor-state/external-format/DSL_ExternalFormat_SchemaSetEditorState.js';
import { ProjectDependency, UpdateProjectConfigurationCommand, } from '@finos/legend-server-sdlc';
import { generateEditorRoute } from '../../../__lib__/LegendStudioNavigation.js';
export var MODEL_IMPORT_NATIVE_INPUT_TYPE;
(function (MODEL_IMPORT_NATIVE_INPUT_TYPE) {
    MODEL_IMPORT_NATIVE_INPUT_TYPE["ENTITIES"] = "ENTITIES";
    MODEL_IMPORT_NATIVE_INPUT_TYPE["PURE_PROTOCOL"] = "PURE_PROTOCOL";
    MODEL_IMPORT_NATIVE_INPUT_TYPE["PURE_GRAMMAR"] = "PURE_GRAMMAR";
})(MODEL_IMPORT_NATIVE_INPUT_TYPE || (MODEL_IMPORT_NATIVE_INPUT_TYPE = {}));
export var MODEL_IMPORT_TYPE;
(function (MODEL_IMPORT_TYPE) {
    MODEL_IMPORT_TYPE["NATIVE"] = "NATIVE";
    MODEL_IMPORT_TYPE["EXTERNAL_FORMAT"] = "EXTERNAL_FORMAT";
    MODEL_IMPORT_TYPE["EXTENSIONS"] = "EXTENSIONS";
})(MODEL_IMPORT_TYPE || (MODEL_IMPORT_TYPE = {}));
export class ModelImporterEditorState {
    editorStore;
    modelImporterState;
    loadModelActionState = ActionState.create();
    constructor(modelImporterState) {
        this.editorStore = modelImporterState.editorStore;
        this.modelImporterState = modelImporterState;
    }
}
export class NativeModelImporterEditorState extends ModelImporterEditorState {
    nativeType = MODEL_IMPORT_NATIVE_INPUT_TYPE.ENTITIES;
    modelText;
    loadCurrentProjectActionState = ActionState.create();
    constructor(modelImporterState) {
        super(modelImporterState);
        makeObservable(this, {
            nativeType: observable,
            modelText: observable,
            loadModelActionState: observable,
            setModelText: action,
            setNativeImportType: action,
            isLoadingDisabled: computed,
            loadCurrentProjectEntities: flow,
        });
        this.modelText = this.getExampleEntitiesInputText();
    }
    get label() {
        return this.nativeType;
    }
    get allowHardReplace() {
        return true;
    }
    get isLoadingDisabled() {
        return this.loadCurrentProjectActionState.isInProgress;
    }
    setModelText(val) {
        this.modelText = val;
    }
    setNativeImportType(nativeImportType) {
        if (this.nativeType !== nativeImportType) {
            this.nativeType = nativeImportType;
            this.modelText = this.getNativeImportExampleText(nativeImportType);
        }
    }
    getNativeImportExampleText(inputType) {
        switch (inputType) {
            case MODEL_IMPORT_NATIVE_INPUT_TYPE.PURE_PROTOCOL:
                return this.getExamplePureProtocolInputText();
            case MODEL_IMPORT_NATIVE_INPUT_TYPE.ENTITIES:
                return this.getExampleEntitiesInputText();
            case MODEL_IMPORT_NATIVE_INPUT_TYPE.PURE_GRAMMAR:
                return this.getExamplePureGrammarText();
            default:
                return '';
        }
    }
    async loadEntites() {
        switch (this.nativeType) {
            case MODEL_IMPORT_NATIVE_INPUT_TYPE.PURE_PROTOCOL: {
                return this.editorStore.graphManagerState.graphManager.pureProtocolTextToEntities(this.modelText);
            }
            case MODEL_IMPORT_NATIVE_INPUT_TYPE.ENTITIES: {
                return JSON.parse(this.modelText);
            }
            case MODEL_IMPORT_NATIVE_INPUT_TYPE.PURE_GRAMMAR: {
                return this.editorStore.graphManagerState.graphManager.pureCodeToEntities(this.modelText);
            }
            default:
                throw new UnsupportedOperationError(`Can't load model for input of type '${this.nativeType}'`);
        }
    }
    *loadCurrentProjectEntities() {
        this.loadCurrentProjectActionState.inProgress();
        switch (this.nativeType) {
            case MODEL_IMPORT_NATIVE_INPUT_TYPE.PURE_PROTOCOL: {
                const graphEntities = this.editorStore.graphManagerState.graphBuildState
                    .hasSucceeded
                    ? this.editorStore.graphManagerState.graph.knownAllOwnElements.map((element) => this.editorStore.graphManagerState.graphManager.elementToEntity(element))
                    : this.editorStore.changeDetectionState
                        .workspaceLocalLatestRevisionState.entities;
                this.modelText =
                    (yield this.editorStore.graphManagerState.graphManager.entitiesToPureProtocolText(graphEntities));
                break;
            }
            case MODEL_IMPORT_NATIVE_INPUT_TYPE.ENTITIES: {
                const graphEntities = this.editorStore.graphManagerState.graphBuildState
                    .hasSucceeded
                    ? this.editorStore.graphManagerState.graph.knownAllOwnElements.map((element) => this.editorStore.graphManagerState.graphManager.elementToEntity(element))
                    : this.editorStore.changeDetectionState
                        .workspaceLocalLatestRevisionState.entities;
                this.modelText = JSON.stringify(graphEntities, undefined, DEFAULT_TAB_SIZE);
                break;
            }
            case MODEL_IMPORT_NATIVE_INPUT_TYPE.PURE_GRAMMAR: {
                this.modelText =
                    (yield this.editorStore.graphManagerState.graphManager.graphToPureCode(this.editorStore.graphManagerState.graph, { excludeUnknown: true }));
                break;
            }
            default:
                this.loadCurrentProjectActionState.fail();
                throw new UnsupportedOperationError(`Can't load current project entities for input type of type '${this.nativeType}'`);
        }
        this.loadCurrentProjectActionState.complete();
    }
    getExampleEntitiesInputText() {
        return `${JSON.stringify([
            {
                classifierPath: 'string',
                content: {},
                path: 'string',
            },
        ], undefined, DEFAULT_TAB_SIZE)}`;
    }
    getExamplePureProtocolInputText() {
        return `${this.editorStore.graphManagerState.graphManager.getExamplePureProtocolText()}`;
    }
    getExamplePureGrammarText() {
        return `###Pure\n Class model::A\n {\n\n}`;
    }
    async loadModel() {
        try {
            this.loadModelActionState.inProgress();
            this.editorStore.applicationStore.alertService.setBlockingAlert({
                message: 'Loading model...',
                prompt: 'Please do not close the application',
                showLoading: true,
            });
            const entities = await this.loadEntites();
            const message = `loading entities from ${this.editorStore.applicationStore.config.appName} [${this.modelImporterState.replace ? `potentially affected ` : ''} ${entities.length} entities]`;
            await this.editorStore.sdlcServerClient.updateEntities(this.editorStore.sdlcState.activeProject.projectId, this.editorStore.sdlcState.activeWorkspace, { replace: this.modelImporterState.replace, entities, message });
            this.editorStore.applicationStore.navigationService.navigator.reload({
                ignoreBlocking: true,
            });
        }
        catch (error) {
            assertErrorThrown(error);
            this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.MODEL_LOADER_FAILURE), error);
            this.editorStore.applicationStore.notificationService.notifyError(error);
        }
        finally {
            this.loadModelActionState.complete();
            this.editorStore.applicationStore.alertService.setBlockingAlert(undefined);
        }
    }
}
export class ExecuteInputDebugModelImporterEditorState extends ModelImporterEditorState {
    executeInput = '{}';
    executablePath = 'test::Debugger';
    loadState = ActionState.create();
    constructor(modelImporterState) {
        super(modelImporterState);
        makeObservable(this, {
            executeInput: observable,
            setExecuteInput: action,
            executablePath: observable,
            setExecutablePath: action,
        });
    }
    get label() {
        return `Execute Input [DEBUG]`;
    }
    get allowHardReplace() {
        return false;
    }
    get isLoadingDisabled() {
        return this.loadState.isInProgress;
    }
    setExecuteInput(val) {
        this.executeInput = val;
    }
    setExecutablePath(val) {
        this.executablePath = val;
    }
    async loadModel() {
        this.loadModelActionState.inProgress();
        try {
            this.editorStore.applicationStore.alertService.setBlockingAlert({
                message: 'Loading model...',
                prompt: 'Please do not close the application',
                showLoading: true,
            });
            const result = await this.editorStore.graphManagerState.graphManager.analyzeExecuteInput(JSON.parse(this.executeInput), this.executablePath);
            let entities = [];
            let dependencies = [];
            if (result.origin instanceof GraphEntities) {
                entities = [...result.origin.entities, ...result.entities];
            }
            else if (result.origin instanceof LegendSDLC) {
                entities = [...result.entities];
                dependencies = [
                    new ProjectDependency(generateGAVCoordinates(result.origin.groupId, result.origin.artifactId, undefined), result.origin.versionId),
                ];
            }
            const message = `loading entities from ${this.editorStore.applicationStore.config.appName} [${this.modelImporterState.replace ? `potentially affected ` : ''} ${entities.length} entities]`;
            await this.editorStore.sdlcServerClient.updateEntities(this.editorStore.sdlcState.activeProject.projectId, this.editorStore.sdlcState.activeWorkspace, { replace: this.modelImporterState.replace, entities, message });
            const currentProjectConfiguration = this.editorStore.projectConfigurationEditorState.originalConfig;
            const updateProjectConfigurationCommand = new UpdateProjectConfigurationCommand(currentProjectConfiguration.groupId, currentProjectConfiguration.artifactId, currentProjectConfiguration.projectStructureVersion, `update project configuration from ${this.editorStore.applicationStore.config.appName}`);
            updateProjectConfigurationCommand.projectDependenciesToAdd = dependencies;
            updateProjectConfigurationCommand.projectDependenciesToRemove =
                currentProjectConfiguration.projectDependencies;
            await flowResult(this.editorStore.projectConfigurationEditorState.updateProjectConfiguration(updateProjectConfigurationCommand));
            // open the debugger element
            this.editorStore.applicationStore.navigationService.navigator.goToLocation(generateEditorRoute(this.editorStore.sdlcState.activeProject.projectId, undefined, this.editorStore.sdlcState.activeWorkspace.workspaceId, this.editorStore.sdlcState.activeWorkspace.workspaceType, this.executablePath), {
                ignoreBlocking: true,
            });
        }
        catch (error) {
            assertErrorThrown(error);
            this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.MODEL_LOADER_FAILURE), error);
            this.editorStore.applicationStore.notificationService.notifyError(error);
        }
        finally {
            this.loadModelActionState.complete();
            this.editorStore.applicationStore.alertService.setBlockingAlert(undefined);
        }
    }
}
export class ExtensionModelImportRendererState {
    importerState;
    constructor(importerState) {
        this.importerState = importerState;
    }
}
export class ExtensionModelImporterEditorState extends ModelImporterEditorState {
    config;
    rendererState;
    constructor(config, rendererState, modelImporterState) {
        super(modelImporterState);
        makeObservable(this, {
            config: observable,
            modelImporterState: observable,
            loadModelActionState: observable,
            rendererState: observable,
            isLoadingDisabled: computed,
            setExtension: action,
            loadModel: flow,
        });
        this.config = config;
        this.rendererState = rendererState;
    }
    get label() {
        return this.config.label ?? this.config.key;
    }
    get allowHardReplace() {
        return Boolean(this.config.allowHardReplace);
    }
    get isLoadingDisabled() {
        return false;
    }
    setExtension(extensionConfiguration) {
        this.config = extensionConfiguration;
    }
    async loadModel() {
        await this.config.loadModel(this.rendererState);
    }
}
const DEFAULT_SCHEMA_PACKAGE = '__internal__';
const DEFAULT_SCHEMA_NAME = 'MyShemaSet';
export class ExternalFormatModelImporterState extends ModelImporterEditorState {
    schemaSet;
    schemaSetEditorState;
    description;
    isolatedSchemaGraph;
    constructor(description, modelImporterState) {
        super(modelImporterState);
        makeObservable(this, {
            schemaSet: observable,
            schemaSetEditorState: observable,
            loadModelActionState: observable,
            description: observable,
            isolatedSchemaGraph: observable,
            setExternalFormat: action,
            setDescription: action,
            isLoadingDisabled: computed,
        });
        this.description = description;
        this.schemaSet = new SchemaSet(DEFAULT_SCHEMA_NAME);
        this.schemaSet.format = description.name;
        observe_SchemaSet(this.schemaSet);
        const emptyGraph = this.editorStore.graphManagerState.createNewGraph();
        emptyGraph.addElement(this.schemaSet, DEFAULT_SCHEMA_PACKAGE);
        this.isolatedSchemaGraph = emptyGraph;
        this.schemaSetEditorState = new InnerSchemaSetEditorState(false, this.schemaSet, this.editorStore, this.isolatedSchemaGraph);
    }
    get allowHardReplace() {
        return true;
    }
    get label() {
        return this.schemaSet.format;
    }
    get isLoadingDisabled() {
        return (!this.schemaSetEditorState.schemaSetModelGenerationState.canGenerate ||
            this.schemaSetEditorState.schemaSetModelGenerationState
                .generatingModelsState.isInProgress ||
            this.schemaSetEditorState.schemaSetModelGenerationState
                .importGeneratedElementsState.isInProgress);
    }
    setDescription(val) {
        this.description = val;
    }
    setExternalFormat(description) {
        if (description !== this.description) {
            this.setDescription(description);
            externalFormat_schemaSet_setFormat(this.schemaSet, this.description.name);
            externalFormat_schemaSet_setSchemas(this.schemaSet, []);
            this.schemaSetEditorState.reset();
        }
    }
    async loadModel() {
        this.loadModelActionState.inProgress();
        try {
            this.loadModelActionState.inProgress();
            this.editorStore.applicationStore.alertService.setBlockingAlert({
                message: 'Loading model...',
                prompt: 'Please do not close the application',
                showLoading: true,
            });
            const modelgenerationstate = this.schemaSetEditorState.schemaSetModelGenerationState;
            const entities = await flowResult(modelgenerationstate.getImportEntities());
            if (modelgenerationstate.targetBinding) {
                const schemaEntity = this.editorStore.graphManagerState.graphManager.elementToEntity(this.schemaSet);
                entities.push(schemaEntity);
            }
            assertTrue(Boolean(entities.length), 'No entities to load');
            const message = `loading entities from ${this.editorStore.applicationStore.config.appName} [${this.modelImporterState.replace ? `potentially affected ` : ''} ${entities.length} entities]`;
            await this.editorStore.sdlcServerClient.updateEntities(this.editorStore.sdlcState.activeProject.projectId, this.editorStore.sdlcState.activeWorkspace, { replace: this.modelImporterState.replace, entities, message });
            this.editorStore.applicationStore.navigationService.navigator.reload({
                ignoreBlocking: true,
            });
        }
        catch (error) {
            assertErrorThrown(error);
            this.editorStore.applicationStore.notificationService.notifyError(error);
        }
        finally {
            this.loadModelActionState.complete();
            this.editorStore.applicationStore.alertService.setBlockingAlert(undefined);
        }
    }
}
export class ModelImporterState extends EditorState {
    replace = true;
    modelImportEditorState;
    extensionConfigs = [];
    constructor(editorStore) {
        super(editorStore);
        makeObservable(this, {
            replace: observable,
            modelImportEditorState: observable,
            extensionConfigs: observable,
            setReplaceFlag: action,
            setModelImporterExtension: action,
            setNativeImportType: action,
            setExternalFormatImportFormat: action,
            setImportEditorState: action,
        });
        this.modelImportEditorState = new NativeModelImporterEditorState(this);
        this.extensionConfigs = this.editorStore.pluginManager
            .getApplicationPlugins()
            .flatMap((plugin) => plugin.getExtraModelImporterExtensionConfigurations?.() ?? [])
            .filter(isNonNullable);
    }
    get label() {
        return 'Model Importer';
    }
    match(tab) {
        return tab instanceof ModelImporterState;
    }
    setReplaceFlag(val) {
        this.replace = val;
    }
    setImportEditorState(val) {
        this.modelImportEditorState = val;
    }
    setNativeImportType(nativeImportType) {
        const nativeEditorState = this.modelImportEditorState instanceof NativeModelImporterEditorState
            ? this.modelImportEditorState
            : new NativeModelImporterEditorState(this);
        nativeEditorState.setNativeImportType(nativeImportType);
        this.setImportEditorState(nativeEditorState);
        return nativeEditorState;
    }
    setExternalFormatImportFormat(externalFormat) {
        const extensionEditorState = this.modelImportEditorState instanceof ExternalFormatModelImporterState
            ? this.modelImportEditorState
            : new ExternalFormatModelImporterState(externalFormat, this);
        extensionEditorState.setExternalFormat(externalFormat);
        this.setImportEditorState(extensionEditorState);
        return extensionEditorState;
    }
    setModelImporterExtension(extension) {
        if (this.modelImportEditorState instanceof
            ExtensionModelImporterEditorState &&
            this.modelImportEditorState.config === extension) {
            return this.modelImportEditorState;
        }
        else {
            const modelImporterEditorState = new ExtensionModelImporterEditorState(extension, extension.getExtensionModelImportRendererStateCreator(this), this);
            this.setImportEditorState(modelImporterEditorState);
            return modelImporterEditorState;
        }
    }
    setExecuteInputDebugModelImporter() {
        const executeInputDebugModelImporterEditorState = this.modelImportEditorState instanceof
            ExecuteInputDebugModelImporterEditorState
            ? this.modelImportEditorState
            : new ExecuteInputDebugModelImporterEditorState(this);
        this.setImportEditorState(executeInputDebugModelImporterEditorState);
    }
}
//# sourceMappingURL=ModelImporterState.js.map