import { __esDecorate, __runInitializers } from "tslib";
import { DomainAssociationKind, DomainEntityKind, DomainPropertyKind } from '../models/kinds.js';
import { DomainElement } from './DomainElement.js';
import { nanoid } from '../nanoid.js';
import { Thing } from '../models/Thing.js';
import { observed, retargetChange, toRaw } from '../decorators/observed.js';
import { DomainProperty } from './DomainProperty.js';
import { RemovePropertyException } from '../exceptions/remove_property_exception.js';
import { DomainAssociation } from './DomainAssociation.js';
import { ValidationError } from '../exceptions/validation_error.js';
import { ShapeGenerator } from './amf/ShapeGenerator.js';
import { ApiSchemaGenerator } from '../amf/ApiSchemaGenerator.js';
import { removeGraphNode } from './GraphUtils.js';
import { DataSemantics, isEntitySemantic } from './Semantics.js';
/**
 * Represents an entity within a data domain model.
 *
 * A `DomainEntity` defines a structured data type,
 * containing properties and associations. It's a
 * fundamental building block for modeling complex data
 * structures.
 *
 * **Key Features:**
 *
 * - **Properties:** Defines the data elements (fields) within the entity.
 * - **Associations:** Defines relationships to other entities.
 * - **Inheritance:** Supports inheriting properties and associations from parent entities.
 * - **Metadata:** Supports tags, semantics, and deprecation status.
 * - **AMF Shape Generation:** Can be serialized to an AMF (API Modeling Framework) shape.
 * - **Example Generation:** Can generate example databased on its schema.
 *
 * **Usage:**
 *
 * The preferred way to create a `DomainEntity` is through
 * the `DomainModel.addEntity()` method.
 *
 * **Example:**
 *
 * ```typescript
 * const dataDomain = new DataDomain();
 * const userModel = dataDomain.addModel({ key: 'userModel' });
 * const userEntity = userModel.addEntity({ key: 'user' });
 * ```
 *
 * Entity Validation Rules:
 *
 * - **Primary Key:** An entity must have a primary key selected. This can come from a parent
 *   entity or be defined in the current entity.
 * - **Parent Entity:** The parent entity must exist in the graph.
 * - **Circular Dependency:** Adding a parent entity that creates a circular dependency is not allowed.
 * - **Unique Parent:** An entity cannot have the same parent more than once.
 * - **Property Shadowing:** A property can shadow a parent property. This means that if a property
 *   with the same key exists in both the parent and child entity, the child property will take precedence.
 * - **Association Shadowing:** An association can shadow a parent association. This means that if an
 *   association with the same key exists in both the parent and child entity,
 *   the child association will take precedence.
 * - **Property Resolution:** When the same property exists more than one parent and the child entity,
 *   doesn't explicitly define the property, then:
 *   - The last parent entity (iterating from the first entity to the last) that has the property
 *     will be used as the source of the property.
 * - **Association Resolution:** When the same association exists more than one parent and the child entity,
 *   doesn't explicitly define the association, then:
 *   - The last parent entity (iterating from the first entity to the last) that has the association
 *     will be used as the source of the association.
 * - **Minimum Required Properties:** An entity must have at least one property defined. Entities without properties
 *   are ignored in the system.
 * - **Entity Name**:
 *   - An entity must have a name defined.
 *   - The name has to follow the same rules as the names in a SQL database. This means that the name must be unique
 *     within the data domain and cannot contain special characters.
 * - **Association Targets:** An association must have a target entity defined.
 *   This means that the association must point to at least one entity.
 */
let DomainEntity = (() => {
    let _classSuper = DomainElement;
    let _info_decorators;
    let _info_initializers = [];
    let _info_extraInitializers = [];
    let _tags_decorators;
    let _tags_initializers = [];
    let _tags_extraInitializers = [];
    let _semantics_decorators;
    let _semantics_initializers = [];
    let _semantics_extraInitializers = [];
    let _deprecated_decorators;
    let _deprecated_initializers = [];
    let _deprecated_extraInitializers = [];
    return class DomainEntity extends _classSuper {
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            _info_decorators = [retargetChange()];
            _tags_decorators = [observed({ deep: true })];
            _semantics_decorators = [observed({ deep: true })];
            _deprecated_decorators = [observed()];
            __esDecorate(this, null, _info_decorators, { kind: "accessor", name: "info", static: false, private: false, access: { has: obj => "info" in obj, get: obj => obj.info, set: (obj, value) => { obj.info = value; } }, metadata: _metadata }, _info_initializers, _info_extraInitializers);
            __esDecorate(this, null, _tags_decorators, { kind: "accessor", name: "tags", static: false, private: false, access: { has: obj => "tags" in obj, get: obj => obj.tags, set: (obj, value) => { obj.tags = value; } }, metadata: _metadata }, _tags_initializers, _tags_extraInitializers);
            __esDecorate(this, null, _semantics_decorators, { kind: "accessor", name: "semantics", static: false, private: false, access: { has: obj => "semantics" in obj, get: obj => obj.semantics, set: (obj, value) => { obj.semantics = value; } }, metadata: _metadata }, _semantics_initializers, _semantics_extraInitializers);
            __esDecorate(this, null, _deprecated_decorators, { kind: "accessor", name: "deprecated", static: false, private: false, access: { has: obj => "deprecated" in obj, get: obj => obj.deprecated, set: (obj, value) => { obj.deprecated = value; } }, metadata: _metadata }, _deprecated_initializers, _deprecated_extraInitializers);
            if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
        }
        /**
         * The kind of the domain element.
         */
        kind;
        #info_accessor_storage = __runInitializers(this, _info_initializers, void 0);
        /**
         * The description of the domain entity.
         */
        get info() { return this.#info_accessor_storage; }
        set info(value) { this.#info_accessor_storage = value; }
        #tags_accessor_storage = (__runInitializers(this, _info_extraInitializers), __runInitializers(this, _tags_initializers, void 0));
        /**
         * Optional general purpose tags for the UI.
         */
        get tags() { return this.#tags_accessor_storage; }
        set tags(value) { this.#tags_accessor_storage = value; }
        #semantics_accessor_storage = (__runInitializers(this, _tags_extraInitializers), __runInitializers(this, _semantics_initializers, []
        /**
         * The ordered list of fields (properties and associations) in the schema.
         * These only keep references to the properties and associations.
         * The order of the fields is important as it defines the order of the
         * properties in the schema.
         */
        ));
        /**
         * Semantics applied to this property.
         */
        get semantics() { return this.#semantics_accessor_storage; }
        set semantics(value) { this.#semantics_accessor_storage = value; }
        #fields_accessor_storage = __runInitializers(this, _semantics_extraInitializers);
        /**
         * The ordered list of fields (properties and associations) in the schema.
         * These only keep references to the properties and associations.
         * The order of the fields is important as it defines the order of the
         * properties in the schema.
         */
        get fields() { return this.#fields_accessor_storage; }
        set fields(value) { this.#fields_accessor_storage = value; }
        #deprecated_accessor_storage = __runInitializers(this, _deprecated_initializers, void 0);
        /**
         * Whether this entity is deprecated.
         */
        get deprecated() { return this.#deprecated_accessor_storage; }
        set deprecated(value) { this.#deprecated_accessor_storage = value; }
        /**
         * Creates a full data entity schema with defaults.
         *
         * @param input The partial data entity schema.
         * @returns The data entity schema.
         */
        static createSchema(input = {}) {
            const { key = nanoid(), tags, semantics, fields, deprecated } = input;
            const info = Thing.fromJSON(input.info, { name: 'new_entity' }).toJSON();
            const result = {
                kind: DomainEntityKind,
                key,
                info,
            };
            if (Array.isArray(tags)) {
                result.tags = [...tags];
            }
            if (Array.isArray(semantics)) {
                result.semantics = [...semantics];
            }
            if (Array.isArray(fields)) {
                result.fields = [...fields];
            }
            if (typeof deprecated === 'boolean') {
                result.deprecated = deprecated;
            }
            return result;
        }
        /**
         * Creates a new domain entity instance.
         *
         * You probably want to use the `DomainModel.addEntity()`
         * method instead.
         *
         * @param root A reference to the root DataDomain instance.
         * @param input The partial domain entity schema.
         */
        constructor(root, input) {
            const init = DomainEntity.createSchema(input);
            super(root, init.key);
            __runInitializers(this, _deprecated_extraInitializers);
            this.kind = DomainEntityKind;
            this.info = new Thing(init.info);
            if (Array.isArray(init.tags)) {
                this.tags = [...init.tags];
            }
            else {
                this.tags = [];
            }
            if (Array.isArray(init.semantics)) {
                this.semantics = init.semantics.map((item) => structuredClone(item));
            }
            else {
                this.semantics = [];
            }
            if (Array.isArray(init.fields)) {
                this.fields = [...init.fields];
            }
            else {
                this.fields = [];
            }
            if (typeof init.deprecated === 'boolean') {
                this.deprecated = init.deprecated;
            }
        }
        /**
         * Generates a JSON representation of the entity.
         *
         * @returns A plain object representation of the `DomainEntity`.
         */
        toJSON() {
            const result = {
                kind: this.kind,
                key: this.key,
                info: this.info.toJSON(),
            };
            if (Array.isArray(this.fields) && this.fields.length) {
                result.fields = [...this.fields];
            }
            if (Array.isArray(this.semantics) && this.semantics.length) {
                result.semantics = toRaw(this, this.semantics)?.map((i) => structuredClone(i));
            }
            if (Array.isArray(this.tags) && this.tags.length) {
                result.tags = [...toRaw(this, this.tags)];
            }
            if (typeof this.deprecated === 'boolean') {
                result.deprecated = this.deprecated;
            }
            return result;
        }
        /**
         * Returns a parent data entity where this data property
         * exist.
         *
         * @returns The parent `DomainModel` or undefined if not found.
         * @example
         * ```typescript
         * const parent = entity.getParentInstance();
         * if (parent) {
         *   console.log(parent.key);
         * }
         * ```
         */
        getParentInstance() {
            const parentKey = this.root.graph.parent(this.key);
            if (!parentKey) {
                return undefined;
            }
            return this.root.graph.node(parentKey);
        }
        /**
         * Adds a property to this entity.
         *
         * @param property The partial property schema.
         * @returns The created `DomainProperty` instance.
         * @example
         * ```typescript
         * const property = entity.addProperty({
         *   key: 'name',
         *   type: 'string',
         *   required: true,
         * })
         * ```
         */
        addProperty(property) {
            if (property && property.key && this.root.graph.hasNode(property.key)) {
                throw new Error(`Trying to add a property ${property.key}, but it already exists`);
            }
            const instance = new DomainProperty(this.root, this.key, property);
            this.root.graph.setNode(instance.key, instance);
            this.root.graph.setEdge(this.key, instance.key, { type: 'property' });
            this.fields.push({
                type: 'property',
                key: instance.key,
            });
            this.root.notifyChange();
            return instance;
        }
        /**
         * Removes a property from this entity.
         *
         * @param key The key of the property to remove.
         * @throws {RemovePropertyException} When the property does not exist.
         * @example
         * ```typescript
         * entity.removeProperty('name')
         * ```
         */
        removeProperty(key) {
            if (!this.root.graph.hasNode(key)) {
                throw new RemovePropertyException(`Trying to remove the ${key} property, but it doesn't exist`);
            }
            const prop = this.root.graph.node(key);
            if (prop.getParentInstance() !== this) {
                throw new RemovePropertyException(`Trying to remove the ${key} property, but it doesn't belong to this entity`);
            }
            removeGraphNode(this.root.graph, key);
            this.removeField(key);
            this.root.notifyChange();
        }
        /**
         * A shortcut to `listProperties()`.
         */
        get properties() {
            return this.listProperties();
        }
        /**
         * Lists all properties of this entity.
         *
         * Note, it accounts for the order of the properties as
         * defined in the `fields` array.
         *
         * @returns A generator that yields each `DomainProperty`.
         * @example
         * ```typescript
         * for (const property of entity.listProperties()) {
         *   console.log(property.key);
         * }
         * ```
         */
        *listProperties() {
            for (const { key, type } of this.fields) {
                if (type !== 'property') {
                    continue;
                }
                const node = this.root.graph.node(key);
                if (!node || node.kind !== DomainPropertyKind) {
                    continue;
                }
                yield node;
            }
        }
        /**
         * Checks if this entity has any properties.
         *
         * @returns True if the entity has properties.
         * @example
         * ```typescript
         * if (entity.hasProperties()) {
         *   // ...
         * }
         * ```
         */
        hasProperties() {
            return this.fields.some((item) => item.type === 'property');
        }
        /**
         * A shortcut to `listParents()`.
         */
        get parents() {
            return this.listParents();
        }
        /**
         * Lists all parent entities of this entity.
         *
         * @returns A generator that yields each parent `DomainEntity`.
         * @example
         * ```typescript
         * for (const parent of entity.listParents()) {
         *   console.log(parent.key);
         * }
         * ```
         */
        *listParents() {
            for (const edge of this.root.graph.outEdges(this.key)) {
                const value = this.root.graph.edge(edge);
                if (!value || value.type !== 'parent') {
                    continue;
                }
                const parent = this.root.graph.node(edge.w);
                if (parent) {
                    yield parent;
                }
            }
        }
        /**
         * Adds a parent to this entity.
         *
         * @param key The key of the parent entity.
         * @param domain The key of the foreign domain the parent belongs to.
         * @returns `this` for chaining.
         * @throws ValidationError When the parent does not exist or a circular dependency is detected.
         * @example
         * ```typescript
         * entity.addParent('baseEntity');
         * ```
         */
        addParent(key, domain) {
            const effectiveKey = this.domain.buildReferenceKey(key, domain);
            // Prevent adding self as parent
            if (effectiveKey === this.key) {
                const message = 'Entity cannot be a parent of itself';
                throw new ValidationError([
                    {
                        field: 'parents',
                        message,
                        rule: 'circular',
                    },
                ], { message });
            }
            const { graph } = this.root;
            if (!graph.hasNode(effectiveKey)) {
                let message = `Entity with key "${key}" not found`;
                if (domain) {
                    message += ` in domain "${domain}"`;
                }
                throw new ValidationError([
                    {
                        field: 'parents',
                        message,
                        rule: 'required',
                    },
                ], { message });
            }
            // Check for circular inheritance
            if (this.hasCircularParent(effectiveKey)) {
                const message = `Circular inheritance detected. Cannot add ${key} as a parent`;
                throw new ValidationError([
                    {
                        field: 'parents',
                        message,
                        rule: 'circular',
                    },
                ], { message });
            }
            if (graph.outEdges(this.key, effectiveKey).next().value) {
                const message = `Parent ${key} already exists`;
                throw new ValidationError([
                    {
                        field: 'parents',
                        message,
                        rule: 'unique',
                    },
                ], { message });
            }
            // Parents are associated through graph edges.
            // Otherwise it would collide with the parent model.
            if (domain) {
                graph.setEdge(this.key, effectiveKey, { type: 'parent', domain, foreign: true });
            }
            else {
                graph.setEdge(this.key, effectiveKey, { type: 'parent' });
            }
            this.root.notifyChange();
            return this;
        }
        /**
         * Removes a parent from this entity.
         *
         * @param key The key of the parent entity to remove.
         * @param domain Optional domain key if the parent belongs to a foreign domain.
         * @returns `this` for chaining.
         * @throws Error When the parent does not exist.
         * @example
         * ```typescript
         * // Remove local parent
         * entity.removeParent('baseEntity');
         *
         * // Remove foreign domain parent
         * entity.removeParent('baseEntity', 'externalDomain');
         * ```
         */
        removeParent(key, domain) {
            const { graph } = this.root;
            const effectiveKey = this.domain.buildReferenceKey(key, domain);
            if (!graph.hasEdge(this.key, effectiveKey)) {
                let message = `Trying to remove a parent ${key} from ${this.key}, but it doesn't exist`;
                if (domain) {
                    message = `Trying to remove a foreign parent ${key} from domain "${domain}" from ${this.key}, but it doesn't exist`;
                }
                throw new Error(message);
            }
            // Verify it's actually a parent edge
            const edge = graph.edge(this.key, effectiveKey);
            if (!edge || edge.type !== 'parent') {
                let message = `Edge between ${this.key} and ${key} exists but is not a parent relationship`;
                if (domain) {
                    message = `Edge between ${this.key} and ${key} in domain "${domain}" exists but is not a parent relationship`;
                }
                throw new Error(message);
            }
            graph.removeEdge(this.key, effectiveKey);
            this.root.notifyChange();
            return this;
        }
        /**
         * Checks if this entity has any parents.
         *
         * @returns True if the entity has parents.
         * @example
         * ```typescript
         * if (entity.hasParents()) {
         *   // ...
         * }
         * ```
         */
        hasParents() {
            for (const edge of this.root.graph.outEdges(this.key)) {
                if (this.root.graph.edge(edge)?.type === 'parent') {
                    return true;
                }
            }
            return false;
        }
        /**
         * Checks if the passed entity has a circular parent
         * relationship when attempting to add a new parent.
         *
         * @param key The key of the parent being added.
         * @returns True if adding this parent would create a
         *   circular relationship.
         */
        hasCircularParent(key) {
            const { graph } = this.root;
            // Check if new parent is one of the children.
            for (const edge of graph.outEdges(key, this.key)) {
                if (graph.edge(edge)?.type === 'parent') {
                    return true;
                }
            }
            // check if this entity is in parents chain.
            let current = graph.node(key);
            while (current) {
                if (current.key === this.key) {
                    return true;
                }
                const parents = [...current.listParents()];
                if (parents.length === 0) {
                    break;
                }
                // Assuming single inheritance
                // TODO: Update this to check for multiple parents.
                current = parents[0];
            }
            return false;
        }
        /**
         * Creates breadcrumbs from this entity to the root
         * namespace.
         *
         * @returns An array of `FileBreadcrumb` instances.
         * @example
         * ```typescript
         * const breadcrumbs = entity.breadcrumbs();
         * console.log(breadcrumbs);
         * ```
         */
        breadcrumbs() {
            const result = [];
            result.push({
                key: this.key,
                name: this.info.getLabel('Unnamed entity'),
                kind: DomainEntityKind,
            });
            const model = this.getParentInstance();
            if (model) {
                result.push({
                    key: model.key,
                    kind: model.kind,
                    name: model.info.getLabel('Unnamed model'),
                });
                let parent = model.getParentInstance();
                while (parent && parent !== this.root) {
                    result.push({
                        key: parent.key,
                        kind: parent.kind,
                        name: parent.info.getLabel('Unnamed namespace'),
                    });
                    const typed = parent;
                    parent = typed.getParentInstance();
                }
            }
            result.push({
                key: this.root.key,
                name: this.root.info.getLabel('Unnamed data domain'),
                kind: this.root.kind,
            });
            return result.reverse();
        }
        /**
         * Adds an association to this entity.
         *
         * @param init The association options.
         * @param input The partial association schema.
         * @returns The created `DomainAssociation` instance.
         * @throws Error When the target entity does not exist.
         * @example
         * ```typescript
         * const association = entity.addAssociation({
         *   key: 'address',
         * });
         * ```
         */
        addAssociation(init = {}, input) {
            const { graph } = this.root;
            if (init.domain && init.key) {
                // target is foreign
                const foreignKey = `${init.domain}:${init.key}`;
                if (!graph.hasNode(foreignKey)) {
                    throw new Error(`Foreign entity ${foreignKey} not found`);
                }
            }
            else if (init.key) {
                if (!graph.hasNode(init.key)) {
                    throw new Error(`Target entity ${init.key} not found`);
                }
            }
            const item = new DomainAssociation(this.root, this.key, input);
            graph.setNode(item.key, item);
            graph.setEdge(this.key, item.key, { type: 'association' });
            if (init.key) {
                item.addTarget(init.key, init.domain);
            }
            this.fields.push({ type: 'association', key: item.key });
            this.root.notifyChange();
            return item;
        }
        /**
         * Removes an association from this entity.
         *
         * @param key The key of the association to remove.
         * @throws Error When the association does not exist.
         * @example
         * ```typescript
         * entity.removeAssociation('address');
         * ```
         */
        removeAssociation(key) {
            if (!this.root.graph.hasEdge(this.key, key)) {
                throw new Error(`Unable to find a connection between this entity and the ${key} association`);
            }
            const edge = this.root.graph.edge(this.key, key);
            if (!edge || edge.type !== 'association') {
                throw new Error(`Association ${key} does not exist`);
            }
            removeGraphNode(this.root.graph, key);
            this.removeField(key);
            this.root.notifyChange();
        }
        /**
         * A shortcut to `listAssociations()`.
         */
        get associations() {
            return this.listAssociations();
        }
        /**
         * Lists all associations of this entity.
         *
         * The order of the associations is defined in the `fields` array.
         *
         * @returns A generator that yields each
         *   `DomainAssociation`.
         * @example
         * ```typescript
         * for (const association of entity.listAssociations()) {
         *   console.log(association.key);
         * }
         * ```
         */
        *listAssociations() {
            for (const { key, type } of this.fields) {
                if (type !== 'association') {
                    continue;
                }
                const node = this.root.graph.node(key);
                if (!node || node.kind !== DomainAssociationKind) {
                    continue;
                }
                yield node;
            }
        }
        /**
         * Checks if this entity has any associations.
         *
         * @returns True if the entity has associations.
         * @example
         * ```typescript
         * if (entity.hasAssociations()) {
         *   // ...
         * }
         * ```
         */
        hasAssociations() {
            return this.fields.some((item) => item.type === 'association');
        }
        /**
         * Lists all fields (properties and associations) of this entity.
         *
         * @returns A generator that yields each `DomainAssociation` or `DomainProperty`.
         * @example
         * ```typescript
         * for (const field of entity.listFields()) {
         *   console.log(field.key);
         * }
         * ```
         */
        *listFields() {
            for (const { key } of this.fields) {
                const node = this.root.graph.node(key);
                if (!node) {
                    continue;
                }
                if (node.kind === DomainAssociationKind || node.kind === DomainPropertyKind) {
                    yield node;
                }
            }
        }
        /**
         * Checks if this entity has any fields (properties or associations).
         *
         * @returns True if the entity has fields.
         * @example
         * ```typescript
         * if (entity.hasFields()) {
         *   // ...
         * }
         * ```
         */
        hasFields() {
            return this.fields.length > 0;
        }
        removeField(key) {
            this.fields = this.fields.filter((item) => item.key !== key);
        }
        /**
         * Creates an AMF's data Shape.
         *
         * The property itself is auto-generated. If the `schema` is defined then it is used
         * as the `range` of the property. Otherwise basic shape is generated for the range.
         *
         * This is a preferred way of reading the AMF shape as this synchronizes changed
         * data properties with the shape definition.
         *
         * @returns AMF property shape definition.
         */
        toApiShape() {
            const serializer = new ShapeGenerator();
            return serializer.entity(this);
        }
        /**
         * Reads the schema of the Entity and generates an example for it.
         *
         * @param mime The mime type of the example.
         * @param opts The example generation options.
         * @returns The generated example.
         * @example
         * ```typescript
         * const example = entity.toExample('application/json');
         * console.log(example);
         * ```
         */
        toExample(mime, opts = {}) {
            const shape = this.toApiShape();
            const generator = new ApiSchemaGenerator(mime, {
                renderExamples: opts.renderExamples ?? true,
                renderMocked: opts.renderMocked ?? true,
                renderOptional: opts.renderOptional ?? true,
                selectedUnions: opts.selectedUnions,
            });
            return generator.generate(shape);
        }
        /**
         * Checks whether the entity is a child of the given namespace or data model.
         * The relationship doesn't have to be direct, as long as the entity
         * is in the hierarchy it will return true.
         *
         * @param key The key of the parent to check.
         * @returns True if this entity is a child of the given namespace or data model.
         */
        isChildOf(key) {
            if (this.key === key) {
                return false;
            }
            const parent = this.getParentInstance();
            if (!parent) {
                return false;
            }
            if (parent.key === key) {
                return true;
            }
            return parent.isChildOf(key);
        }
        /**
         * Finds the primary key property of this entity.
         * @returns The primary key property of this entity or undefined if not found.
         */
        primaryKey() {
            for (const property of this.properties) {
                if (property.primary) {
                    return property;
                }
            }
            let property;
            for (const parent of this.listParents()) {
                const prop = parent.primaryKey();
                if (prop) {
                    // According to the validation rules, the last parent
                    // that has the property will be used as the source of
                    // the property.
                    // In the future, we may add a schema definition to the entity that
                    // will explicitly define which property to use.
                    property = prop;
                }
            }
            return property;
        }
        /**
         * Adds or updates a semantic to the entity.
         * @param semantic The semantic to add to the entity.
         * @throws Error if the semantic is not an entity semantic.
         */
        addSemantic(semantic) {
            const sem = DataSemantics[semantic.id];
            if (!isEntitySemantic(sem)) {
                throw new Error(`Invalid semantic type: ${semantic.id}. Expected an entity semantic.`);
            }
            const index = this.semantics.findIndex((s) => s.id === semantic.id);
            if (index >= 0) {
                this.semantics[index] = semantic;
            }
            else {
                this.semantics.push(semantic);
            }
            this.domain.notifyChange();
        }
        /**
         * Removes a semantic from the entity.
         * @param semanticId The ID of the semantic to remove.
         */
        removeSemantic(semanticId) {
            const index = this.semantics.findIndex((s) => s.id === semanticId);
            if (index >= 0) {
                this.semantics.splice(index, 1);
                this.domain.notifyChange();
            }
        }
        /**
         * Checks if the entity has a specific semantic.
         * @param semanticId The ID of the semantic to check.
         * @returns True if the semantic is present, false otherwise.
         */
        hasSemantic(semanticId) {
            return this.semantics.some((s) => s.id === semanticId);
        }
        /**
         * Generates a unique name by appending a number to the base name.
         * @param baseName The base name to make unique.
         * @returns A unique name within the current entity.
         */
        generateUniqueName(baseName) {
            const existingNames = new Set();
            for (const field of this.listFields()) {
                if (field.info.name) {
                    existingNames.add(field.info.name);
                }
            }
            let counter = 2;
            let newName = `${baseName}_copy`;
            while (existingNames.has(newName)) {
                newName = `${baseName}_copy_${counter}`;
                counter++;
            }
            return newName;
        }
    };
})();
export { DomainEntity };
//# sourceMappingURL=DomainEntity.js.map