/**
 * 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 { action, observable, makeObservable } from 'mobx';
import { LogEvent, IllegalStateError, isNonNullable, UnsupportedOperationError, guaranteeNonNullable, ActionState, } from '@finos/legend-shared';
import { getDependenciesPackableElementTreeData, getPackableElementTreeData, openNode, openNodeById, openNodes, populatePackageTreeNodeChildren, } from './utils/PackageTreeUtils.js';
import { LEGEND_STUDIO_APP_EVENT } from '../../__lib__/LegendStudioEvent.js';
import { getFileSystemTreeData, } from './utils/FileSystemTreeUtils.js';
import { ROOT_PACKAGE_NAME, Package, Unit, PrimitiveType, getElementRootPackage, isDependencyElement, } from '@finos/legend-graph';
import { APPLICATION_EVENT } from '@finos/legend-application';
import { DatabaseBuilderWizardState } from './editor-state/element-editor-state/connection/DatabaseBuilderWizardState.js';
import { DatabaseModelBuilderState } from './editor-state/element-editor-state/connection/DatabaseModelBuilderState.js';
export var ExplorerTreeRootPackageLabel;
(function (ExplorerTreeRootPackageLabel) {
    ExplorerTreeRootPackageLabel["FILE_GENERATION"] = "generated-files";
    ExplorerTreeRootPackageLabel["MODEL_GENERATION"] = "generated-models";
    ExplorerTreeRootPackageLabel["SYSTEM"] = "system";
    ExplorerTreeRootPackageLabel["PROJECT_DEPENDENCY"] = "dependencies";
})(ExplorerTreeRootPackageLabel || (ExplorerTreeRootPackageLabel = {}));
export class ExplorerTreeState {
    editorStore;
    buildState = ActionState.create();
    treeData;
    generationTreeData;
    systemTreeData;
    legalTreeData;
    dependencyTreeData;
    selectedNode;
    artifactsGenerationTreeData;
    elementToRename;
    classToGenerateSampleData;
    databaseBuilderState;
    databaseModelBuilderState;
    constructor(editorStore) {
        makeObservable(this, {
            treeData: observable.ref,
            generationTreeData: observable.ref,
            systemTreeData: observable.ref,
            legalTreeData: observable.ref,
            dependencyTreeData: observable.ref,
            artifactsGenerationTreeData: observable.ref,
            selectedNode: observable.ref,
            elementToRename: observable,
            classToGenerateSampleData: observable,
            databaseBuilderState: observable,
            databaseModelBuilderState: observable,
            setTreeData: action,
            setGenerationTreeData: action,
            setSystemTreeData: action,
            setLegalTreeData: action,
            setDependencyTreeData: action,
            setArtifactsGenerationTreeData: action,
            setSelectedNode: action,
            setElementToRename: action,
            setClassToGenerateSampleData: action,
            build: action,
            buildImmutableModelTrees: action,
            buildTreeInTextMode: action,
            buildDatabaseModels: action,
            openExplorerTreeNodes: action,
            reprocess: action,
            buildDatabase: action,
            setDatabaseBuilderState: action,
            onTreeNodeSelect: action,
            openNode: action,
        });
        this.editorStore = editorStore;
    }
    getTreeData(rootPackageName = ROOT_PACKAGE_NAME.MAIN) {
        let treeData;
        switch (rootPackageName) {
            case ROOT_PACKAGE_NAME.MODEL_GENERATION:
                treeData = this.generationTreeData;
                break;
            case ROOT_PACKAGE_NAME.SYSTEM:
                treeData = this.systemTreeData;
                break;
            case ROOT_PACKAGE_NAME.PROJECT_DEPENDENCY_ROOT:
                treeData = this.dependencyTreeData;
                break;
            default:
                treeData = this.treeData;
        }
        if (!treeData || !this.buildState.hasCompleted) {
            this.editorStore.applicationStore.logService.error(LogEvent.create(APPLICATION_EVENT.ILLEGAL_APPLICATION_STATE_OCCURRED), `Can't get explorer tree data for root package '${rootPackageName}' as it hasn't been initialized`);
            throw new IllegalStateError(`Can't get explorer tree data for root package '${rootPackageName}' as it hasn't been initialized`);
        }
        return treeData;
    }
    getSelectedNodePackage() {
        if (!this.selectedNode) {
            return this.editorStore.graphManagerState.graph.root;
        }
        return this.selectedNode.packageableElement instanceof Package
            ? this.selectedNode.packageableElement
            : (this.selectedNode.packageableElement.package ??
                this.editorStore.graphManagerState.graph.root);
    }
    setTreeData(data) {
        this.treeData = data;
    }
    setGenerationTreeData(data) {
        this.generationTreeData = data;
    }
    setSystemTreeData(data) {
        this.systemTreeData = data;
    }
    setLegalTreeData(data) {
        this.legalTreeData = data;
    }
    setDependencyTreeData(data) {
        this.dependencyTreeData = data;
    }
    setArtifactsGenerationTreeData(data) {
        this.artifactsGenerationTreeData = data;
    }
    setElementToRename(val) {
        this.elementToRename = val;
    }
    setClassToGenerateSampleData(val) {
        this.classToGenerateSampleData = val;
    }
    setDatabaseBuilderState(val) {
        this.databaseBuilderState = val;
    }
    buildDatabase(val, isReadOnly) {
        const dbBuilderState = new DatabaseBuilderWizardState(this.editorStore, val, isReadOnly);
        dbBuilderState.setShowModal(true);
        this.setDatabaseBuilderState(dbBuilderState);
    }
    setDatabaseModelBuilderState(val) {
        this.databaseModelBuilderState = val;
    }
    buildDatabaseModels(val, isReadOnly) {
        const dbBuilderState = new DatabaseModelBuilderState(this.editorStore, val, isReadOnly, this.editorStore.graphManagerState.graph);
        dbBuilderState.setShowModal(true);
        this.setDatabaseModelBuilderState(dbBuilderState);
    }
    setSelectedNode(node) {
        if (this.selectedNode) {
            this.selectedNode.isSelected = false;
        }
        if (node) {
            node.isSelected = true;
        }
        this.selectedNode = node;
    }
    build() {
        this.buildState.reset();
        this.treeData = getPackableElementTreeData(this.editorStore, this.editorStore.graphManagerState.graph.root, '');
        this.generationTreeData = getPackableElementTreeData(this.editorStore, this.editorStore.graphManagerState.graph.generationModel.root, ExplorerTreeRootPackageLabel.MODEL_GENERATION);
        this.artifactsGenerationTreeData = getFileSystemTreeData(this.editorStore.graphState.graphGenerationState.rootFileDirectory, ExplorerTreeRootPackageLabel.FILE_GENERATION);
        this.setSelectedNode(undefined);
        this.buildState.complete();
    }
    buildTreeInTextMode() {
        this.buildState.reset();
        if (!this.systemTreeData) {
            this.systemTreeData = getPackableElementTreeData(this.editorStore, this.editorStore.graphManagerState.systemModel.root, ExplorerTreeRootPackageLabel.SYSTEM);
        }
        if (!this.dependencyTreeData) {
            this.dependencyTreeData = getDependenciesPackableElementTreeData(this.editorStore, this.editorStore.graphManagerState.graph.dependencyManager.roots, ExplorerTreeRootPackageLabel.PROJECT_DEPENDENCY);
        }
        this.treeData = getPackableElementTreeData(this.editorStore, this.editorStore.graphManagerState.graph.root, '');
        this.generationTreeData = getPackableElementTreeData(this.editorStore, this.editorStore.graphManagerState.graph.generationModel.root, ExplorerTreeRootPackageLabel.MODEL_GENERATION);
        this.artifactsGenerationTreeData = getFileSystemTreeData(this.editorStore.graphState.graphGenerationState.rootFileDirectory, ExplorerTreeRootPackageLabel.FILE_GENERATION);
        this.setSelectedNode(undefined);
        this.buildState.complete();
    }
    buildImmutableModelTrees() {
        this.systemTreeData = getPackableElementTreeData(this.editorStore, this.editorStore.graphManagerState.systemModel.root, ExplorerTreeRootPackageLabel.SYSTEM);
        this.dependencyTreeData = getDependenciesPackableElementTreeData(this.editorStore, this.editorStore.graphManagerState.graph.dependencyManager.roots, ExplorerTreeRootPackageLabel.PROJECT_DEPENDENCY);
    }
    openExplorerTreeNodes(mainTreeOpenedNodeIds, generationTreeOpenedNodeIds, selectedNodeId) {
        // Main tree
        {
            const openElements = new Set(mainTreeOpenedNodeIds
                .map((id) => this.editorStore.graphManagerState.graph.getNullableElement(id, true))
                .filter(isNonNullable));
            openNodes(this.editorStore, this.editorStore.graphManagerState.graph.root, openElements, guaranteeNonNullable(this.treeData));
        }
        // Generated tree
        {
            const openElements = new Set(generationTreeOpenedNodeIds
                .map((id) => this.editorStore.graphManagerState.graph.generationModel.getOwnNullableElement(id, true))
                .filter(isNonNullable));
            openNodes(this.editorStore, this.editorStore.graphManagerState.graph.generationModel.root, openElements, guaranteeNonNullable(this.generationTreeData));
            if (generationTreeOpenedNodeIds.includes(ROOT_PACKAGE_NAME.MODEL_GENERATION)) {
                openNodeById(ROOT_PACKAGE_NAME.MODEL_GENERATION, this.generationTreeData);
            }
        }
        {
            if (selectedNodeId) {
                const element = this.editorStore.graphManagerState.graph.getNullableElement(selectedNodeId);
                if (element) {
                    const openingNode = openNode(this.editorStore, element, guaranteeNonNullable(this.treeData)) ??
                        openNode(this.editorStore, element, guaranteeNonNullable(this.generationTreeData));
                    // ?? openNode(element, this.systemTreeData)
                    // ?? openNode(element, this.legalTreeData)
                    // ?? openNode(element, this.dependencyTreeData);
                    this.setSelectedNode(openingNode);
                }
                else {
                    this.setSelectedNode(undefined);
                }
            }
        }
    }
    /**
     * FIXME: this method should be replaced altogether as this could potentially cause memory leak when we `replace` the graph
     * When we refresh the graph (after compilation in text mode for example), we want to reprocess the app to
     * preserve the status of the explorer tree (opening nodes, selected nodes, etc.)
     *
     * @risk memory-leak
     */
    reprocess() {
        this.buildState.reset();
        if (!this.systemTreeData) {
            this.systemTreeData = getPackableElementTreeData(this.editorStore, this.editorStore.graphManagerState.systemModel.root, ExplorerTreeRootPackageLabel.SYSTEM);
        }
        if (!this.dependencyTreeData) {
            this.dependencyTreeData = getDependenciesPackableElementTreeData(this.editorStore, this.editorStore.graphManagerState.graph.dependencyManager.roots, ExplorerTreeRootPackageLabel.PROJECT_DEPENDENCY);
        }
        // Main tree
        {
            const openedTreeNodeIds = this.treeData
                ? Array.from(this.treeData.nodes.values())
                    .filter((node) => node.isOpen)
                    .map((node) => node.id)
                : [];
            this.treeData = getPackableElementTreeData(this.editorStore, this.editorStore.graphManagerState.graph.root, '');
            const openElements = new Set(openedTreeNodeIds
                .map((id) => this.editorStore.graphManagerState.graph.getNullableElement(id, true))
                .filter(isNonNullable));
            openNodes(this.editorStore, this.editorStore.graphManagerState.graph.root, openElements, this.treeData);
        }
        // Generated tree
        {
            const openedTreeNodeIds = this.generationTreeData
                ? Array.from(this.generationTreeData.nodes.values())
                    .filter((node) => node.isOpen)
                    .map((node) => node.id)
                : [];
            this.generationTreeData = getPackableElementTreeData(this.editorStore, this.editorStore.graphManagerState.graph.generationModel.root, ExplorerTreeRootPackageLabel.MODEL_GENERATION);
            const openElements = new Set(openedTreeNodeIds
                .map((id) => this.editorStore.graphManagerState.graph.generationModel.getOwnNullableElement(id, true))
                .filter(isNonNullable));
            openNodes(this.editorStore, this.editorStore.graphManagerState.graph.generationModel.root, openElements, this.generationTreeData);
            if (openedTreeNodeIds.includes(ROOT_PACKAGE_NAME.MODEL_GENERATION)) {
                openNodeById(ROOT_PACKAGE_NAME.MODEL_GENERATION, this.generationTreeData);
            }
        }
        // File generation tree
        // TODO: fix this so it does proper reprocessing, right now it just rebuilds
        this.artifactsGenerationTreeData = getFileSystemTreeData(this.editorStore.graphState.graphGenerationState.rootFileDirectory, ExplorerTreeRootPackageLabel.FILE_GENERATION);
        if (this.selectedNode) {
            const element = this.editorStore.graphManagerState.graph.getNullableElement(this.selectedNode.id);
            if (element) {
                const openingNode = openNode(this.editorStore, element, this.treeData) ??
                    openNode(this.editorStore, element, this.generationTreeData);
                // ?? openNode(element, this.systemTreeData)
                // ?? openNode(element, this.legalTreeData)
                // ?? openNode(element, this.dependencyTreeData);
                this.setSelectedNode(openingNode);
            }
            else {
                this.setSelectedNode(undefined);
            }
        }
        this.setTreeData({ ...this.treeData });
        this.setGenerationTreeData({ ...this.generationTreeData });
        this.buildState.complete();
    }
    onTreeNodeSelect = (node, treeData, rootPackageName = ROOT_PACKAGE_NAME.MAIN) => {
        // Open non-package element
        if (!(node.packageableElement instanceof Package)) {
            this.editorStore.graphEditorMode.openElement(node.packageableElement);
        }
        // Expand package element
        if (node.childrenIds?.length) {
            node.isOpen = !node.isOpen;
            if (node.packageableElement instanceof Package) {
                populatePackageTreeNodeChildren(this.editorStore, node, treeData, rootPackageName === ROOT_PACKAGE_NAME.PROJECT_DEPENDENCY_ROOT);
            }
        }
        this.setSelectedNode(node);
        switch (rootPackageName) {
            case ROOT_PACKAGE_NAME.MODEL_GENERATION:
                this.setGenerationTreeData({ ...treeData });
                break;
            case ROOT_PACKAGE_NAME.SYSTEM:
                this.setSystemTreeData({ ...treeData });
                break;
            case ROOT_PACKAGE_NAME.PROJECT_DEPENDENCY_ROOT:
                this.setDependencyTreeData({ ...treeData });
                break;
            default:
                this.setTreeData({ ...treeData });
        }
    };
    /**
     * Given an element we open the node depending on what package tree corresponds to it
     */
    openNode(element) {
        if (element instanceof PrimitiveType || element instanceof Unit) {
            throw new UnsupportedOperationError(`Can't open package tree node for element`, element);
        }
        const rootPackageName = getElementRootPackage(element).name;
        let opened = false;
        if (rootPackageName === ROOT_PACKAGE_NAME.MAIN && this.treeData) {
            const openingNode = openNode(this.editorStore, element, this.treeData);
            this.setSelectedNode(openingNode);
            opened = true;
        }
        else if (rootPackageName === ROOT_PACKAGE_NAME.MODEL_GENERATION &&
            this.generationTreeData) {
            const openingNode = openNode(this.editorStore, element, this.generationTreeData);
            this.setSelectedNode(openingNode);
            opened = true;
        }
        else if (rootPackageName === ROOT_PACKAGE_NAME.SYSTEM &&
            this.systemTreeData) {
            const openingNode = openNode(this.editorStore, element, this.systemTreeData);
            this.setSelectedNode(openingNode);
            opened = true;
        }
        else if (isDependencyElement(element) && this.dependencyTreeData) {
            const openingNode = openNode(this.editorStore, element, this.dependencyTreeData, undefined, true);
            this.setSelectedNode(openingNode);
            opened = true;
        }
        if (!opened) {
            this.editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.PACKAGE_TREE_BUILDER_FAILURE), `Can't open package tree node for element '${element.path}' with root package '${rootPackageName}'`);
        }
    }
    getArtifactsGenerationTreeData() {
        return guaranteeNonNullable(this.artifactsGenerationTreeData, 'File generation tree data has not been initialized');
    }
}
//# sourceMappingURL=ExplorerTreeState.js.map