/**
 * 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 { UnsupportedOperationError, getClass, IllegalStateError, returnUndefOnError, promisify, filterByType, guaranteeNonNullable, guaranteeType, isNonNullable, isType, } from '@finos/legend-shared';
import { Package } from '../graph/metamodel/pure/packageableElements/domain/Package.js';
import { Type } from '../graph/metamodel/pure/packageableElements/domain/Type.js';
import { Association } from '../graph/metamodel/pure/packageableElements/domain/Association.js';
import { Mapping } from '../graph/metamodel/pure/packageableElements/mapping/Mapping.js';
import { Class } from '../graph/metamodel/pure/packageableElements/domain/Class.js';
import { Enumeration } from '../graph/metamodel/pure/packageableElements/domain/Enumeration.js';
import { PackageableElement } from '../graph/metamodel/pure/packageableElements/PackageableElement.js';
import { Profile } from '../graph/metamodel/pure/packageableElements/domain/Profile.js';
import { Service } from '../graph/metamodel/pure/packageableElements/service/Service.js';
import { ConcreteFunctionDefinition } from './metamodel/pure/packageableElements/function/ConcreteFunctionDefinition.js';
import { Store } from '../graph/metamodel/pure/packageableElements/store/Store.js';
import { FlatData } from '../graph/metamodel/pure/packageableElements/store/flatData/model/FlatData.js';
import { PackageableRuntime } from '../graph/metamodel/pure/packageableElements/runtime/PackageableRuntime.js';
import { PackageableConnection } from '../graph/metamodel/pure/packageableElements/connection/PackageableConnection.js';
import { FileGenerationSpecification } from '../graph/metamodel/pure/packageableElements/fileGeneration/FileGenerationSpecification.js';
import { GenerationSpecification } from '../graph/metamodel/pure/packageableElements/generationSpecification/GenerationSpecification.js';
import { Unit, Measure, } from '../graph/metamodel/pure/packageableElements/domain/Measure.js';
import { Database } from '../graph/metamodel/pure/packageableElements/store/relational/model/Database.js';
import { SectionIndex } from '../graph/metamodel/pure/packageableElements/section/SectionIndex.js';
import { PureGraphExtension } from './PureGraphExtension.js';
import { PrimitiveType } from '../graph/metamodel/pure/packageableElements/domain/PrimitiveType.js';
import { DataType } from '../graph/metamodel/pure/packageableElements/domain/DataType.js';
import { isValidFullPath, isValidPath, resolvePackagePathAndElementName, } from '../graph/MetaModelUtils.js';
import { addElementToPackage, deleteElementFromPackage, getFunctionName, getOrCreateGraphPackage, getOrCreatePackage, } from '../graph/helpers/DomainHelper.js';
import { DataElement } from '../graph/metamodel/pure/packageableElements/data/DataElement.js';
import { ExecutionEnvironmentInstance } from './metamodel/pure/packageableElements/service/ExecutionEnvironmentInstance.js';
import { INTERNAL__UnknownPackageableElement } from './metamodel/pure/packageableElements/INTERNAL__UnknownPackageableElement.js';
import { FunctionActivator } from './metamodel/pure/packageableElements/function/FunctionActivator.js';
import { INTERNAL__UnknownFunctionActivator } from './metamodel/pure/packageableElements/function/INTERNAL__UnknownFunctionActivator.js';
import { INTERNAL__UnknownStore } from './metamodel/pure/packageableElements/store/INTERNAL__UnknownStore.js';
import { INTERNAL__UnknownElement } from './metamodel/pure/packageableElements/INTERNAL__UnknownElement.js';
import { DataProduct } from './metamodel/pure/dataProduct/DataProduct.js';
import { IngestDefinition } from './metamodel/pure/packageableElements/ingest/IngestDefinition.js';
const FORBIDDEN_EXTENSION_ELEMENT_CLASS = new Set([
    PackageableElement,
    Type,
    DataType,
    PrimitiveType,
    Class,
    Association,
    Enumeration,
    ConcreteFunctionDefinition,
    Profile,
    Measure,
    Unit,
    DataElement,
    Store,
    Mapping,
    PackageableConnection,
    PackageableRuntime,
    GenerationSpecification,
    FileGenerationSpecification,
    Service, // to be modularized
    ExecutionEnvironmentInstance,
    SectionIndex,
    INTERNAL__UnknownPackageableElement,
    INTERNAL__UnknownStore,
]);
/**
 * Since this is the basis of the Pure graph itself, it shares many methods with the graph.
 * But the graph holds references to many basic graphs and expose those to outside consumers
 * as if it is one graph.
 *
 * As such, to avoid confusion, we add `Own` to methods in basic graph for methods that only
 * deal with elements belonging to the basic graph.
 */
export class BasicModel {
    root;
    _origin;
    extensions = [];
    graphPlugins = [];
    elementSectionIndex = new Map();
    sectionIndicesIndex = new Map();
    profilesIndex = new Map();
    typesIndex = new Map();
    associationsIndex = new Map();
    functionsIndex = new Map();
    functionActivatorsIndex = new Map();
    storesIndex = new Map();
    mappingsIndex = new Map();
    connectionsIndex = new Map();
    runtimesIndex = new Map();
    servicesIndex = new Map();
    generationSpecificationsIndex = new Map();
    fileGenerationsIndex = new Map();
    productsIndex = new Map();
    dataElementsIndex = new Map();
    executionEnvironmentsIndex = new Map();
    INTERNAL__unknownElementsIndex = new Map();
    INTERNAL__unknownIndex = new Map();
    constructor(rootPackageName, graphPlugins, origin) {
        this.root = new Package(rootPackageName);
        const extensionElementClasses = graphPlugins.flatMap((plugin) => plugin.getExtraPureGraphExtensionClasses?.() ?? []);
        this.extensions = this.createGraphExtensions(extensionElementClasses);
        this._origin = origin;
    }
    createGraphExtensions(extensionElementClasses) {
        return extensionElementClasses.map((extensionElementClass) => {
            if (FORBIDDEN_EXTENSION_ELEMENT_CLASS.has(extensionElementClass)) {
                throw new IllegalStateError(`Pure graph extension not allowed for the specified class. Consider removing this extension from plugins.`);
            }
            return new PureGraphExtension(extensionElementClass);
        });
    }
    get ownSectionIndices() {
        return Array.from(this.sectionIndicesIndex.values());
    }
    get ownProfiles() {
        return Array.from(this.profilesIndex.values());
    }
    get ownEnumerations() {
        return Array.from(this.typesIndex.values()).filter(filterByType(Enumeration));
    }
    get ownMeasures() {
        return Array.from(this.typesIndex.values()).filter(filterByType(Measure));
    }
    get ownClasses() {
        return Array.from(this.typesIndex.values()).filter(filterByType(Class));
    }
    get ownTypes() {
        return Array.from(this.typesIndex.values());
    }
    get ownAssociations() {
        return Array.from(this.associationsIndex.values());
    }
    get ownFunctions() {
        return Array.from(this.functionsIndex.values());
    }
    get ownFunctionActivators() {
        return Array.from(this.functionActivatorsIndex.values());
    }
    get ownStores() {
        return Array.from(this.storesIndex.values());
    }
    get ownFlatDatas() {
        return Array.from(this.storesIndex.values()).filter(filterByType(FlatData));
    }
    get ownDatabases() {
        return Array.from(this.storesIndex.values()).filter(filterByType(Database));
    }
    get ownMappings() {
        return Array.from(this.mappingsIndex.values());
    }
    get ownServices() {
        return Array.from(this.servicesIndex.values());
    }
    get ownRuntimes() {
        return Array.from(this.runtimesIndex.values());
    }
    get ownConnections() {
        return Array.from(this.connectionsIndex.values());
    }
    get ownFileGenerations() {
        return Array.from(this.fileGenerationsIndex.values());
    }
    get ownDataProducts() {
        return Array.from(this.productsIndex.values());
    }
    get ownDataElements() {
        return Array.from(this.dataElementsIndex.values());
    }
    get ownExecutionEnvironments() {
        return Array.from(this.executionEnvironmentsIndex.values());
    }
    get ownIngests() {
        return Array.from(this.INTERNAL__unknownElementsIndex.values()).filter(filterByType(IngestDefinition));
    }
    get ownGenerationSpecifications() {
        return Array.from(this.generationSpecificationsIndex.values());
    }
    get ownTestables() {
        const coreTestables = [
            ...this.ownServices,
            ...this.ownMappings,
            // TODO: re-add functions once function test runner has been completed in backend
            // ...this.ownFunctions,
        ];
        const filters = this.graphPlugins.flatMap((plugin) => plugin.getExtraTestablesCollectors?.() ?? []);
        const extraTestables = this.allOwnElements
            .map((element) => filters.find((elementFilter) => Boolean(elementFilter(element)))?.(element))
            .filter(isNonNullable);
        return [...coreTestables, ...extraTestables];
    }
    get origin() {
        return this._origin;
    }
    setOrigin(val) {
        if (this._origin) {
            throw new IllegalStateError(`Graph origin has already been set`);
        }
        else {
            this._origin = val;
        }
    }
    getExtensionElements(extensionElementClass) {
        return this.getExtensionForElementClass(extensionElementClass).elements;
    }
    getExtensionForElementClass(_class) {
        const extensions = this.extensions.filter((extension) => extension.getElementClass() === _class);
        if (extensions.length === 0) {
            throw new UnsupportedOperationError(`Can't find graph extension for the specified element class: no compatible graph extensions available from plugins`);
        }
        else if (extensions.length > 1) {
            throw new IllegalStateError(`Found multiple extensions for the specified element class`);
        }
        return extensions[0];
    }
    getOwnNullableSection = (path) => this.elementSectionIndex.get(path);
    getOwnNullableSectionIndex = (path) => this.sectionIndicesIndex.get(path);
    getOwnNullableProfile = (path) => this.profilesIndex.get(path);
    getOwnNullableType(path) {
        return this.typesIndex.get(path);
    }
    getOwnNullableClass = (path) => {
        const el = this.getOwnNullableType(path);
        return el instanceof Class ? el : undefined;
    };
    getOwnNullableEnumeration = (path) => {
        const el = this.getOwnNullableType(path);
        return el instanceof Enumeration ? el : undefined;
    };
    getOwnNullableMeasure = (path) => {
        const el = this.getOwnNullableType(path);
        return el instanceof Measure ? el : undefined;
    };
    getOwnNullableAssociation = (path) => this.associationsIndex.get(path);
    getOwnNullableFunction = (path) => this.functionsIndex.get(path);
    getOwnNullableFunctionActivator = (path) => this.functionActivatorsIndex.get(path);
    getOwnNullableStore = (path) => this.storesIndex.get(path);
    getOwnNullableMapping = (path) => this.mappingsIndex.get(path);
    getOwnNullableConnection = (path) => this.connectionsIndex.get(path);
    getOwnNullableRuntime = (path) => this.runtimesIndex.get(path);
    getOwnNullableService = (path) => this.servicesIndex.get(path);
    getOwnNullableGenerationSpecification = (path) => this.generationSpecificationsIndex.get(path);
    getOwnNullableFileGeneration = (path) => this.fileGenerationsIndex.get(path);
    getOwnNullableDataElement = (path) => this.dataElementsIndex.get(path);
    getOwnNullableExecutionEnviornment = (path) => this.executionEnvironmentsIndex.get(path);
    getOwnNullableDataProduct = (path) => this.productsIndex.get(path);
    getOwnNullableIngestDefinition = (path) => this.ownIngests.find((ingest) => ingest.path === path);
    getOwnSectionIndex = (path) => guaranteeNonNullable(this.getOwnNullableSectionIndex(path), `Can't find section index '${path}'`);
    getOwnProfile = (path) => guaranteeNonNullable(this.getOwnNullableProfile(path), `Can't find profile '${path}'`);
    getOwnType = (path) => guaranteeNonNullable(this.getOwnNullableType(path), `Can't find type '${path}'`);
    getOwnClass = (path) => guaranteeNonNullable(this.getOwnNullableClass(path), `Can't find class '${path}'`);
    getOwnEnumeration = (path) => guaranteeNonNullable(this.getOwnNullableEnumeration(path), `Can't find enumeration '${path}'`);
    getOwnMeasure = (path) => guaranteeNonNullable(this.getOwnNullableMeasure(path), `Can't find measure '${path}'`);
    getOwnAssociation = (path) => guaranteeNonNullable(this.getOwnNullableAssociation(path), `Can't find association '${path}'`);
    getOwnFunction = (path) => guaranteeNonNullable(this.getOwnNullableFunction(path), `Can't find function '${path}'`);
    getOwnFunctionActivator = (path) => guaranteeNonNullable(this.getOwnNullableFunctionActivator(path), `Can't find function activator '${path}'`);
    getOwnStore = (path) => guaranteeNonNullable(this.getOwnNullableStore(path), `Can't find store '${path}'`);
    getOwnDatabase = (path) => guaranteeType(this.getOwnNullableStore(path), Database, `Can't find database '${path}'`);
    getOwnFlatDataStore = (path) => guaranteeType(this.getOwnNullableStore(path), FlatData, `Can't find flat-data store '${path}'`);
    getOwnMapping = (path) => guaranteeNonNullable(this.getOwnNullableMapping(path), `Can't find mapping '${path}'`);
    getOwnConnection = (path) => guaranteeNonNullable(this.getOwnNullableConnection(path), `Can't find connection '${path}'`);
    getOwnRuntime = (path) => guaranteeNonNullable(this.getOwnNullableRuntime(path), `Can't find runtime '${path}'`);
    getOwnService = (path) => guaranteeNonNullable(this.getOwnNullableService(path), `Can't find service '${path}'`);
    getOwnGenerationSpecification = (path) => guaranteeNonNullable(this.getOwnNullableGenerationSpecification(path), `Can't find generation specification '${path}'`);
    getOwnFileGeneration = (path) => guaranteeNonNullable(this.getOwnNullableFileGeneration(path), `Can't find file generation '${path}'`);
    getOwnDataElement = (path) => guaranteeNonNullable(this.getOwnNullableDataElement(path), `Can't find data element '${path}'`);
    getOwnExecutionEnvironment = (path) => guaranteeNonNullable(this.getOwnNullableExecutionEnviornment(path), `Can't find execution environment element '${path}'`);
    getOwnDataProduct = (path) => guaranteeNonNullable(this.getOwnNullableDataProduct(path), `Can't find data product element '${path}'`);
    getOwnNullableExtensionElement(path, extensionElementClass) {
        const extension = this.getExtensionForElementClass(extensionElementClass);
        return extension.getElement(path);
    }
    setOwnSection(path, val) {
        this.elementSectionIndex.set(path, val);
    }
    setOwnSectionIndex(path, val) {
        this.sectionIndicesIndex.set(path, val);
    }
    setOwnProfile(path, val) {
        this.profilesIndex.set(path, val);
    }
    setOwnType(path, val) {
        this.typesIndex.set(path, val);
    }
    setOwnAssociation(path, val) {
        this.associationsIndex.set(path, val);
    }
    setOwnFunction(path, val) {
        this.functionsIndex.set(path, val);
    }
    setOwnFunctionActivator(path, val) {
        this.functionActivatorsIndex.set(path, val);
    }
    setOwnStore(path, val) {
        this.storesIndex.set(path, val);
    }
    setOwnMapping(path, val) {
        this.mappingsIndex.set(path, val);
    }
    setOwnConnection(path, val) {
        this.connectionsIndex.set(path, val);
    }
    setOwnRuntime(path, val) {
        this.runtimesIndex.set(path, val);
    }
    setOwnService(path, val) {
        this.servicesIndex.set(path, val);
    }
    setOwnGenerationSpecification(path, val) {
        this.generationSpecificationsIndex.set(path, val);
    }
    setOwnFileGeneration(path, val) {
        this.fileGenerationsIndex.set(path, val);
    }
    setOwnDataElement(path, val) {
        this.dataElementsIndex.set(path, val);
    }
    setOwnExecutionEnvironment(path, val) {
        this.executionEnvironmentsIndex.set(path, val);
    }
    setOwnDataProduct(path, val) {
        this.productsIndex.set(path, val);
    }
    INTERNAL__setOwnUnknown(path, val) {
        this.INTERNAL__unknownIndex.set(path, val);
    }
    INTERNAL__setOwnUnknownElement(path, val) {
        this.INTERNAL__unknownElementsIndex.set(path, val);
    }
    setOwnElementInExtension(path, val, extensionElementClass) {
        const extension = this.getExtensionForElementClass(extensionElementClass);
        extension.setElement(path, val);
    }
    get allOwnElements() {
        return [
            ...this.ownProfiles,
            ...this.ownEnumerations,
            ...this.ownMeasures,
            ...this.ownClasses,
            ...this.ownAssociations,
            ...this.ownFunctions,
            ...this.ownFunctionActivators,
            ...this.ownStores,
            ...this.ownMappings,
            ...this.ownServices,
            ...this.ownRuntimes,
            ...this.ownConnections,
            ...this.ownGenerationSpecifications,
            ...this.ownFileGenerations,
            ...this.ownDataProducts,
            ...this.ownDataElements,
            ...this.ownExecutionEnvironments,
            ...Array.from(this.INTERNAL__unknownElementsIndex.values()),
            ...Array.from(this.INTERNAL__unknownIndex.values()),
            ...this.extensions.flatMap((extension) => extension.elements),
        ];
    }
    get knownAllOwnElements() {
        return this.allOwnElements.filter((element) => !isType(element, INTERNAL__UnknownElement));
    }
    /**
     * Dispose the current graph and any potential reference from parts outside of the graph to the graph
     * This is a MUST to prevent memory-leak as we might have references between metamodels from this graph
     * and other graphs
     */
    async dispose() {
        if (this.allOwnElements.length) {
            await Promise.all(this.allOwnElements.map((element) => promisify(() => {
                element.dispose();
            })));
        }
    }
    getNullablePackage = (path) => !path
        ? this.root
        : returnUndefOnError(() => getOrCreatePackage(this.root, path, false, undefined));
    getOwnNullableElement(path, includePackage) {
        let element = this.sectionIndicesIndex.get(path) ??
            this.INTERNAL__unknownIndex.get(path) ??
            this.INTERNAL__unknownElementsIndex.get(path) ??
            this.typesIndex.get(path) ??
            this.profilesIndex.get(path) ??
            this.associationsIndex.get(path) ??
            this.functionsIndex.get(path) ??
            this.functionActivatorsIndex.get(path) ??
            this.storesIndex.get(path) ??
            this.mappingsIndex.get(path) ??
            this.servicesIndex.get(path) ??
            this.runtimesIndex.get(path) ??
            this.connectionsIndex.get(path) ??
            this.fileGenerationsIndex.get(path) ??
            this.productsIndex.get(path) ??
            this.dataElementsIndex.get(path) ??
            this.executionEnvironmentsIndex.get(path) ??
            this.generationSpecificationsIndex.get(path);
        if (!element) {
            for (const extension of this.extensions) {
                const extensionElement = extension.getElement(path);
                if (extensionElement) {
                    element = extensionElement;
                    break;
                }
            }
        }
        if (includePackage && !element) {
            return this.getNullablePackage(path);
        }
        return element;
    }
    addOwnElement(element, packagePath) {
        addElementToPackage(packagePath
            ? getOrCreateGraphPackage(this, packagePath, undefined)
            : this.root, element);
        if (element instanceof Mapping) {
            this.setOwnMapping(element.path, element);
        }
        else if (element instanceof Store) {
            this.setOwnStore(element.path, element);
        }
        else if (element instanceof Type) {
            this.setOwnType(element.path, element);
        }
        else if (element instanceof Association) {
            this.setOwnAssociation(element.path, element);
        }
        else if (element instanceof Profile) {
            this.setOwnProfile(element.path, element);
        }
        else if (element instanceof ConcreteFunctionDefinition) {
            this.setOwnFunction(element.path, element);
        }
        else if (element instanceof FunctionActivator) {
            this.setOwnFunctionActivator(element.path, element);
        }
        else if (element instanceof Service) {
            this.setOwnService(element.path, element);
        }
        else if (element instanceof PackageableConnection) {
            this.setOwnConnection(element.path, element);
        }
        else if (element instanceof PackageableRuntime) {
            this.setOwnRuntime(element.path, element);
        }
        else if (element instanceof FileGenerationSpecification) {
            this.setOwnFileGeneration(element.path, element);
        }
        else if (element instanceof GenerationSpecification) {
            this.setOwnGenerationSpecification(element.path, element);
        }
        else if (element instanceof DataElement) {
            this.setOwnDataElement(element.path, element);
        }
        else if (element instanceof ExecutionEnvironmentInstance) {
            this.setOwnExecutionEnvironment(element.path, element);
        }
        else if (element instanceof DataProduct) {
            this.setOwnDataProduct(element.path, element);
        }
        else if (element instanceof Package) {
            // do nothing
        }
        else {
            const extension = this.getExtensionForElementClass(getClass(element));
            extension.setElement(element.path, element);
        }
    }
    deleteOwnElement(element) {
        if (element.package) {
            deleteElementFromPackage(element.package, element);
        }
        if (element instanceof INTERNAL__UnknownElement) {
            this.INTERNAL__unknownIndex.delete(element.path);
        }
        else if (element instanceof INTERNAL__UnknownPackageableElement) {
            this.INTERNAL__unknownElementsIndex.delete(element.path);
        }
        else if (element instanceof Mapping) {
            this.mappingsIndex.delete(element.path);
        }
        else if (element instanceof Store) {
            this.storesIndex.delete(element.path);
        }
        else if (element instanceof Type) {
            this.typesIndex.delete(element.path);
            if (element instanceof Measure) {
                if (element.canonicalUnit) {
                    this.typesIndex.delete(element.canonicalUnit.path);
                }
                element.nonCanonicalUnits.forEach((unit) => this.typesIndex.delete(unit.path));
            }
        }
        else if (element instanceof Association) {
            this.associationsIndex.delete(element.path);
        }
        else if (element instanceof Profile) {
            this.profilesIndex.delete(element.path);
        }
        else if (element instanceof ConcreteFunctionDefinition) {
            this.functionsIndex.delete(element.path);
        }
        else if (element instanceof FunctionActivator) {
            this.functionActivatorsIndex.delete(element.path);
        }
        else if (element instanceof Service) {
            this.servicesIndex.delete(element.path);
        }
        else if (element instanceof PackageableRuntime) {
            this.runtimesIndex.delete(element.path);
        }
        else if (element instanceof PackageableConnection) {
            this.connectionsIndex.delete(element.path);
        }
        else if (element instanceof FileGenerationSpecification) {
            this.fileGenerationsIndex.delete(element.path);
        }
        else if (element instanceof GenerationSpecification) {
            this.generationSpecificationsIndex.delete(element.path);
        }
        else if (element instanceof DataElement) {
            this.dataElementsIndex.delete(element.path);
        }
        else if (element instanceof ExecutionEnvironmentInstance) {
            this.executionEnvironmentsIndex.delete(element.path);
        }
        else if (element instanceof DataProduct) {
            this.productsIndex.delete(element.path);
        }
        else if (element instanceof Package) {
            element.children.forEach((child) => this.deleteOwnElement(child));
        }
        else {
            const extension = this.getExtensionForElementClass(getClass(element));
            extension.deleteElement(element.path);
        }
    }
    /**
     * NOTE: this method has to do with graph modification
     * and as of the current set up, we should not allow it to be called
     * on any immutable graphs. As such, we protect it and let the main graph
     * expose it. The other benefit of exposing this in the main graph is that we could
     * do better duplicated path check
     */
    renameOwnElement(element, oldPath, newPath) {
        // validation before renaming
        if (oldPath === newPath) {
            return;
        }
        if (!newPath) {
            throw new UnsupportedOperationError(`Can't rename element '${oldPath} to '${newPath}': path is empty'`);
        }
        if ((element instanceof Package && !isValidPath(newPath)) ||
            (!(element instanceof Package) && !isValidFullPath(newPath))) {
            throw new UnsupportedOperationError(`Can't rename element '${oldPath} to '${newPath}': invalid path'`);
        }
        const existingElement = this.getOwnNullableElement(newPath, true);
        if (existingElement) {
            throw new UnsupportedOperationError(`Can't rename element '${oldPath} to '${newPath}': another element with the same path already existed'`);
        }
        const [newPackagePath, newElementName] = resolvePackagePathAndElementName(newPath);
        // if we're not renaming package, we can simply add new package
        // if the element new package does not exist. For renaming package
        // it's a little bit more complicated as we need to rename its children
        // we will handle this case later
        if (!(element instanceof Package)) {
            const newParentPackage = this.getNullablePackage(newPackagePath) ??
                (newPackagePath !== ''
                    ? getOrCreateGraphPackage(this, newPackagePath, undefined)
                    : this.root);
            // update element name
            element.name = newElementName;
            if (element instanceof ConcreteFunctionDefinition) {
                element.functionName = getFunctionName(element, element.name);
            }
            // update element package if needed
            if (element.package !== newParentPackage) {
                if (element.package) {
                    deleteElementFromPackage(element.package, element);
                }
                addElementToPackage(newParentPackage, element);
            }
        }
        // update index in the graph
        if (element instanceof INTERNAL__UnknownElement) {
            this.INTERNAL__unknownIndex.delete(oldPath);
            this.INTERNAL__unknownIndex.set(newPath, element);
            element.content = {
                ...element.content,
                name: element.name,
                package: element.package?.path,
            };
        }
        else if (element instanceof INTERNAL__UnknownPackageableElement) {
            this.INTERNAL__unknownElementsIndex.delete(oldPath);
            this.INTERNAL__unknownElementsIndex.set(newPath, element);
            element.content = {
                ...element.content,
                name: element.name,
                package: element.package?.path,
            };
        }
        else if (element instanceof INTERNAL__UnknownFunctionActivator) {
            this.functionActivatorsIndex.delete(oldPath);
            this.functionActivatorsIndex.set(newPath, element);
            element.content = {
                ...element.content,
                name: element.name,
                package: element.package?.path,
            };
        }
        else if (element instanceof INTERNAL__UnknownStore) {
            this.storesIndex.delete(oldPath);
            this.storesIndex.set(newPath, element);
            element.content = {
                ...element.content,
                name: element.name,
                package: element.package?.path,
            };
        }
        else if (element instanceof Mapping) {
            this.mappingsIndex.delete(oldPath);
            this.mappingsIndex.set(newPath, element);
        }
        else if (element instanceof Store) {
            this.storesIndex.delete(oldPath);
            this.storesIndex.set(newPath, element);
        }
        else if (element instanceof Type) {
            this.typesIndex.delete(oldPath);
            this.typesIndex.set(newPath, element);
            if (element instanceof Measure) {
                if (element.canonicalUnit) {
                    this.typesIndex.delete(element.canonicalUnit.path);
                    this.typesIndex.set(element.canonicalUnit.path.replace(oldPath, newPath), element.canonicalUnit);
                }
                element.nonCanonicalUnits.forEach((unit) => {
                    this.typesIndex.delete(unit.path);
                    this.typesIndex.set(unit.path.replace(oldPath, newPath), unit);
                });
            }
        }
        else if (element instanceof Association) {
            this.associationsIndex.delete(oldPath);
            this.associationsIndex.set(newPath, element);
        }
        else if (element instanceof Profile) {
            this.profilesIndex.delete(oldPath);
            this.profilesIndex.set(newPath, element);
        }
        else if (element instanceof ConcreteFunctionDefinition) {
            this.functionsIndex.delete(oldPath);
            this.functionsIndex.set(newPath, element);
        }
        else if (element instanceof FunctionActivator) {
            this.functionActivatorsIndex.delete(oldPath);
            this.functionActivatorsIndex.set(newPath, element);
        }
        else if (element instanceof Service) {
            this.servicesIndex.delete(oldPath);
            this.servicesIndex.set(newPath, element);
        }
        else if (element instanceof PackageableRuntime) {
            this.runtimesIndex.delete(oldPath);
            this.runtimesIndex.set(newPath, element);
        }
        else if (element instanceof PackageableConnection) {
            this.connectionsIndex.delete(oldPath);
            this.connectionsIndex.set(newPath, element);
        }
        else if (element instanceof FileGenerationSpecification) {
            this.fileGenerationsIndex.delete(oldPath);
            this.fileGenerationsIndex.set(newPath, element);
        }
        else if (element instanceof DataElement) {
            this.dataElementsIndex.delete(oldPath);
            this.dataElementsIndex.set(newPath, element);
        }
        else if (element instanceof ExecutionEnvironmentInstance) {
            this.executionEnvironmentsIndex.delete(oldPath);
            this.executionEnvironmentsIndex.set(newPath, element);
        }
        else if (element instanceof GenerationSpecification) {
            this.generationSpecificationsIndex.delete(oldPath);
            this.generationSpecificationsIndex.set(newPath, element);
        }
        else if (element instanceof Package) {
            // Since we will modify the package name, we need to first store the original
            // paths of all of its children
            const childElements = new Map();
            element.children.forEach((childElement) => {
                childElements.set(childElement.path, childElement);
            });
            // update element name
            element.name = newElementName;
            if (!element.package) {
                throw new IllegalStateError(`Can't rename root package`);
            }
            /**
             * Update element package if needed.
             *
             * NOTE: to be clean, first completely remove the package from its parent package
             * Only then would we find or create the new parent package. This way, if we rename
             * package `example::something` to `example::something::somethingElse`, we will not
             * end up in a loop. If we did not first remove the package from its parent package
             * we would end up having the `somethingElse` package containing itself as a child.
             */
            const currentParentPackage = element.package;
            if (currentParentPackage !== this.getNullablePackage(newPackagePath)) {
                deleteElementFromPackage(currentParentPackage, element);
                const newParentPackage = newPackagePath !== ''
                    ? getOrCreateGraphPackage(this, newPackagePath, undefined)
                    : this.root;
                addElementToPackage(newParentPackage, element);
            }
            childElements.forEach((childElement, childElementOriginalPath) => {
                // NOTE: since we already modified the parent package path, we need to pass in the child element's original path
                // here to be sure we're properly, if we rely on the `childElement.path` that would cause a bug
                this.renameOwnElement(childElement, childElementOriginalPath, childElementOriginalPath.replace(oldPath, newPath));
            });
        }
        else {
            const extension = this.getExtensionForElementClass(getClass(element));
            extension.renameElement(oldPath, newPath);
        }
    }
    /**
     * TODO: this will be removed once we have full support for section index
     * See https://github.com/finos/legend-studio/issues/1067
     */
    TEMPORARY__deleteOwnSectionIndex() {
        this.sectionIndicesIndex.forEach((sectionIndex) => {
            sectionIndex.setIsDeleted(true);
            this.sectionIndicesIndex.delete(sectionIndex.path);
        });
        // NOTE: we have to do this because right now we don't add section index to the package tree
        // as such `this.sectionIndicesIndex.delete(sectionIndex.path)` won't work because the path
        // is without the package
        this.sectionIndicesIndex = new Map();
        this.elementSectionIndex = new Map();
    }
}
//# sourceMappingURL=BasicModel.js.map