import { __esDecorate, __runInitializers } from "tslib";
import { 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 { BinaryFormats, DomainPropertyAttributes, DomainPropertyList, DomainPropertyTypes, NumberFormats, } from './DataFormat.js';
import { ShapeGenerator } from './amf/ShapeGenerator.js';
import { DataSemantics, isPropertySemantic } from './Semantics.js';
import { ApiSchemaGenerator } from '../amf/ApiSchemaGenerator.js';
/**
 * Represents a property within a data domain entity.
 *
 * A `DomainProperty` defines a single data element within a
 * `DomainEntity`. It specifies the data type, constraints,
 * and other metadata for a specific piece of information.
 *
 * **Key Features:**
 *
 * -   **Data Type:** Defines the type of data the property
 *     holds (e.g., string, number, boolean, date).
 * -   **Constraints:** Supports defining constraints such as
 *     minimum/maximum values, enum values, and default values.
 * -   **Bindings:** Allows defining how the property is
 *     represented in different formats (e.g., web APIs,
 *     protocol buffers).
 * -   **Metadata:** Supports metadata such as tags, semantics,
 *     read-only/write-only status, and deprecation.
 * -   **Schema:** Allows defining a general schema for the
 *     property, which is propagated to all bindings.
 *
 * **Usage:**
 *
 * Use the `DomainEntity.addProperty` method to add a new property to an
 * entity. The property can be created using the constructor, but it
 * won't be added to the graph until you call the `addProperty` method.
 *
 * **Example:**
 *
 * ```typescript
 * const entity = domainModel.addEntity({ key: 'user' });
 * const property = entity.addProperty({
 *   key: 'name',
 *   type: 'string',
 *   required: true,
 *   schema: {
 *     minimum: 3,
 *     maximum: 50,
 *   },
 * });
 * ```
 */
let DomainProperty = (() => {
    let _classSuper = DomainElement;
    let _info_decorators;
    let _info_initializers = [];
    let _info_extraInitializers = [];
    let _required_decorators;
    let _required_initializers = [];
    let _required_extraInitializers = [];
    let _multiple_decorators;
    let _multiple_initializers = [];
    let _multiple_extraInitializers = [];
    let _primary_decorators;
    let _primary_initializers = [];
    let _primary_extraInitializers = [];
    let _unique_decorators;
    let _unique_initializers = [];
    let _unique_extraInitializers = [];
    let _index_decorators;
    let _index_initializers = [];
    let _index_extraInitializers = [];
    let _readOnly_decorators;
    let _readOnly_initializers = [];
    let _readOnly_extraInitializers = [];
    let _writeOnly_decorators;
    let _writeOnly_initializers = [];
    let _writeOnly_extraInitializers = [];
    let _deprecated_decorators;
    let _deprecated_initializers = [];
    let _deprecated_extraInitializers = [];
    let _tags_decorators;
    let _tags_initializers = [];
    let _tags_extraInitializers = [];
    let _semantics_decorators;
    let _semantics_initializers = [];
    let _semantics_extraInitializers = [];
    let _type_decorators;
    let _type_initializers = [];
    let _type_extraInitializers = [];
    let _schema_decorators;
    let _schema_initializers = [];
    let _schema_extraInitializers = [];
    let _bindings_decorators;
    let _bindings_initializers = [];
    let _bindings_extraInitializers = [];
    return class DomainProperty extends _classSuper {
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            _info_decorators = [retargetChange()];
            _required_decorators = [observed()];
            _multiple_decorators = [observed()];
            _primary_decorators = [observed()];
            _unique_decorators = [observed()];
            _index_decorators = [observed()];
            _readOnly_decorators = [observed()];
            _writeOnly_decorators = [observed()];
            _deprecated_decorators = [observed()];
            _tags_decorators = [observed({ deep: true })];
            _semantics_decorators = [observed({ deep: true })];
            _type_decorators = [observed()];
            _schema_decorators = [observed({ deep: true })];
            _bindings_decorators = [observed({ deep: true })];
            __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, _required_decorators, { kind: "accessor", name: "required", static: false, private: false, access: { has: obj => "required" in obj, get: obj => obj.required, set: (obj, value) => { obj.required = value; } }, metadata: _metadata }, _required_initializers, _required_extraInitializers);
            __esDecorate(this, null, _multiple_decorators, { kind: "accessor", name: "multiple", static: false, private: false, access: { has: obj => "multiple" in obj, get: obj => obj.multiple, set: (obj, value) => { obj.multiple = value; } }, metadata: _metadata }, _multiple_initializers, _multiple_extraInitializers);
            __esDecorate(this, null, _primary_decorators, { kind: "accessor", name: "primary", static: false, private: false, access: { has: obj => "primary" in obj, get: obj => obj.primary, set: (obj, value) => { obj.primary = value; } }, metadata: _metadata }, _primary_initializers, _primary_extraInitializers);
            __esDecorate(this, null, _unique_decorators, { kind: "accessor", name: "unique", static: false, private: false, access: { has: obj => "unique" in obj, get: obj => obj.unique, set: (obj, value) => { obj.unique = value; } }, metadata: _metadata }, _unique_initializers, _unique_extraInitializers);
            __esDecorate(this, null, _index_decorators, { kind: "accessor", name: "index", static: false, private: false, access: { has: obj => "index" in obj, get: obj => obj.index, set: (obj, value) => { obj.index = value; } }, metadata: _metadata }, _index_initializers, _index_extraInitializers);
            __esDecorate(this, null, _readOnly_decorators, { kind: "accessor", name: "readOnly", static: false, private: false, access: { has: obj => "readOnly" in obj, get: obj => obj.readOnly, set: (obj, value) => { obj.readOnly = value; } }, metadata: _metadata }, _readOnly_initializers, _readOnly_extraInitializers);
            __esDecorate(this, null, _writeOnly_decorators, { kind: "accessor", name: "writeOnly", static: false, private: false, access: { has: obj => "writeOnly" in obj, get: obj => obj.writeOnly, set: (obj, value) => { obj.writeOnly = value; } }, metadata: _metadata }, _writeOnly_initializers, _writeOnly_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);
            __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, _type_decorators, { kind: "accessor", name: "type", static: false, private: false, access: { has: obj => "type" in obj, get: obj => obj.type, set: (obj, value) => { obj.type = value; } }, metadata: _metadata }, _type_initializers, _type_extraInitializers);
            __esDecorate(this, null, _schema_decorators, { kind: "accessor", name: "schema", static: false, private: false, access: { has: obj => "schema" in obj, get: obj => obj.schema, set: (obj, value) => { obj.schema = value; } }, metadata: _metadata }, _schema_initializers, _schema_extraInitializers);
            __esDecorate(this, null, _bindings_decorators, { kind: "accessor", name: "bindings", static: false, private: false, access: { has: obj => "bindings" in obj, get: obj => obj.bindings, set: (obj, value) => { obj.bindings = value; } }, metadata: _metadata }, _bindings_initializers, _bindings_extraInitializers);
            if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
        }
        kind;
        #info_accessor_storage = __runInitializers(this, _info_initializers, void 0);
        /**
         * The description of the domain property.
         */
        get info() { return this.#info_accessor_storage; }
        set info(value) { this.#info_accessor_storage = value; }
        #required_accessor_storage = (__runInitializers(this, _info_extraInitializers), __runInitializers(this, _required_initializers, void 0));
        /**
         * Wether the data property is required.
         */
        get required() { return this.#required_accessor_storage; }
        set required(value) { this.#required_accessor_storage = value; }
        #multiple_accessor_storage = (__runInitializers(this, _required_extraInitializers), __runInitializers(this, _multiple_initializers, void 0));
        /**
         * Whether the data property allows multiple items.
         */
        get multiple() { return this.#multiple_accessor_storage; }
        set multiple(value) { this.#multiple_accessor_storage = value; }
        #primary_accessor_storage = (__runInitializers(this, _multiple_extraInitializers), __runInitializers(this, _primary_initializers, void 0));
        /**
         * Whether this property describes a primary key of the entity.
         */
        get primary() { return this.#primary_accessor_storage; }
        set primary(value) { this.#primary_accessor_storage = value; }
        #unique_accessor_storage = (__runInitializers(this, _primary_extraInitializers), __runInitializers(this, _unique_initializers, void 0));
        /**
         * Whether this property describes a unique property of the entity.
         * This is used to generate unique constraints in the database.
         */
        get unique() { return this.#unique_accessor_storage; }
        set unique(value) { this.#unique_accessor_storage = value; }
        #index_accessor_storage = (__runInitializers(this, _unique_extraInitializers), __runInitializers(this, _index_initializers, void 0));
        /**
         * Whether this property describes an indexed property of the entity.
         */
        get index() { return this.#index_accessor_storage; }
        set index(value) { this.#index_accessor_storage = value; }
        #readOnly_accessor_storage = (__runInitializers(this, _index_extraInitializers), __runInitializers(this, _readOnly_initializers, void 0));
        /**
         * Whether the property is read only in the schema.
         */
        get readOnly() { return this.#readOnly_accessor_storage; }
        set readOnly(value) { this.#readOnly_accessor_storage = value; }
        #writeOnly_accessor_storage = (__runInitializers(this, _readOnly_extraInitializers), __runInitializers(this, _writeOnly_initializers, void 0));
        /**
         * Whether the property is write only in the schema.
         */
        get writeOnly() { return this.#writeOnly_accessor_storage; }
        set writeOnly(value) { this.#writeOnly_accessor_storage = value; }
        #deprecated_accessor_storage = (__runInitializers(this, _writeOnly_extraInitializers), __runInitializers(this, _deprecated_initializers, void 0));
        /**
         * Whether this property is deprecated.
         */
        get deprecated() { return this.#deprecated_accessor_storage; }
        set deprecated(value) { this.#deprecated_accessor_storage = value; }
        #tags_accessor_storage = (__runInitializers(this, _deprecated_extraInitializers), __runInitializers(this, _tags_initializers, []
        /**
         * Semantics applied to this property.
         */
        ));
        /**
         * Optional general purpose tags for the UI.
         *
         * Note to implementations, use the `addTag()` method as it propagates the "tag" value in the namespace.
         * Also, the tags property is excluded from the observer properties.
         * Changes to this property will not notify the observers.
         */
        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 data type for this property.
         * Note, not all schemas support the same type. For example, defining `sint32`
         * works for protocol buffers but does not for JSON. In such case we use default
         * translation `sint32` -> `number`. Another way it to create
         * a derivative entity for specific schema to describe specific schema case.
         */
        ));
        /**
         * Semantics applied to this property.
         */
        get semantics() { return this.#semantics_accessor_storage; }
        set semantics(value) { this.#semantics_accessor_storage = value; }
        #type_accessor_storage = (__runInitializers(this, _semantics_extraInitializers), __runInitializers(this, _type_initializers, 'string'
        /**
         * The general schema definition of this property.
         * This is propagated to all bindings (when they support these properties).
         */
        ));
        /**
         * The data type for this property.
         * Note, not all schemas support the same type. For example, defining `sint32`
         * works for protocol buffers but does not for JSON. In such case we use default
         * translation `sint32` -> `number`. Another way it to create
         * a derivative entity for specific schema to describe specific schema case.
         */
        get type() { return this.#type_accessor_storage; }
        set type(value) { this.#type_accessor_storage = value; }
        #schema_accessor_storage = (__runInitializers(this, _type_extraInitializers), __runInitializers(this, _schema_initializers, void 0));
        /**
         * The general schema definition of this property.
         * This is propagated to all bindings (when they support these properties).
         */
        get schema() { return this.#schema_accessor_storage; }
        set schema(value) { this.#schema_accessor_storage = value; }
        #bindings_accessor_storage = (__runInitializers(this, _schema_extraInitializers), __runInitializers(this, _bindings_initializers, []
        /**
         * Creates a full data model schema with defaults.
         *
         * @param input The partial data model schema.
         * @returns The data model schema.
         */
        ));
        /**
         * The list of bindings for this property.
         *
         * A binding defines a translation from a data model to a specific format.
         * For example allows to define properties required to generate AMF shape and therefore RAML/OAS shapes for web APIs
         * or a protocol buffer schema.
         */
        get bindings() { return this.#bindings_accessor_storage; }
        set bindings(value) { this.#bindings_accessor_storage = value; }
        /**
         * Creates a full data model schema with defaults.
         *
         * @param input The partial data model schema.
         * @returns The data model schema.
         */
        static createSchema(input = {}) {
            const { key = nanoid(), multiple, required, type = DomainPropertyList.string, index, primary, unique, readOnly, writeOnly, tags, semantics, deprecated, schema, bindings, } = input;
            if (typeof type === 'string') {
                if (!DomainPropertyTypes.includes(type)) {
                    throw new Error(`Invalid data property type ${type}`);
                }
            }
            const info = Thing.fromJSON(input.info, { name: 'new_property' }).toJSON();
            const result = {
                kind: DomainPropertyKind,
                key,
                info,
                type,
            };
            if (typeof multiple === 'boolean') {
                result.multiple = multiple;
            }
            if (typeof required === 'boolean') {
                result.required = required;
            }
            if (typeof index === 'boolean') {
                result.index = index;
            }
            if (typeof primary === 'boolean') {
                result.primary = primary;
            }
            if (typeof unique === 'boolean') {
                result.unique = unique;
            }
            if (typeof readOnly === 'boolean') {
                result.readOnly = readOnly;
            }
            if (typeof writeOnly === 'boolean') {
                result.writeOnly = writeOnly;
            }
            if (typeof deprecated === 'boolean') {
                result.deprecated = deprecated;
            }
            if (Array.isArray(tags)) {
                result.tags = [...tags];
            }
            if (Array.isArray(semantics)) {
                result.semantics = [...semantics];
            }
            if (schema) {
                result.schema = structuredClone(schema);
            }
            if (Array.isArray(bindings)) {
                result.bindings = bindings.map((i) => structuredClone(i));
            }
            else {
                result.bindings = [];
            }
            return result;
        }
        /**
         * The key of the parent entity.
         */
        parent = __runInitializers(this, _bindings_extraInitializers);
        /**
         * Creates a new domain property instance.
         * It does not add it to the graph.
         * You need to call the `addProperty` method of the graph or a data model to add it.
         *
         * Domain objects constructors shouldn't be used directly. Use methods
         * provided on the DataDomain class to create new objects.
         *
         * @param root A reference to the root DataDomain instance.
         * @param parent The key of the parent entity.
         * @param input The partial domain property schema.
         * @returns Created domain property instance.
         */
        constructor(root, parent, input = {}) {
            const init = DomainProperty.createSchema(input);
            super(root, init.key);
            this.parent = parent;
            this.kind = DomainPropertyKind;
            this.info = new Thing(init.info);
            this.type = init.type;
            if (typeof init.multiple === 'boolean') {
                this.multiple = init.multiple;
            }
            else {
                this.multiple = undefined;
            }
            if (typeof init.required === 'boolean') {
                this.required = init.required;
            }
            else {
                this.required = undefined;
            }
            if (typeof init.index === 'boolean') {
                this.index = init.index;
            }
            else {
                this.index = undefined;
            }
            if (typeof init.deprecated === 'boolean') {
                this.deprecated = init.deprecated;
            }
            else {
                this.deprecated = undefined;
            }
            if (typeof init.primary === 'boolean') {
                this.primary = init.primary;
            }
            else {
                this.primary = undefined;
            }
            if (typeof init.unique === 'boolean') {
                this.unique = init.unique;
            }
            else {
                this.unique = undefined;
            }
            if (typeof init.readOnly === 'boolean') {
                this.readOnly = init.readOnly;
            }
            else {
                this.readOnly = undefined;
            }
            if (typeof init.writeOnly === 'boolean') {
                this.writeOnly = init.writeOnly;
            }
            else {
                this.writeOnly = undefined;
            }
            if (Array.isArray(init.tags)) {
                this.tags = [...init.tags];
            }
            else {
                this.tags = [];
            }
            if (Array.isArray(init.semantics)) {
                this.semantics = [...init.semantics];
            }
            else {
                this.semantics = [];
            }
            if (init.schema) {
                this.schema = structuredClone(init.schema);
            }
            else {
                this.schema = undefined;
            }
            if (Array.isArray(init.bindings)) {
                this.bindings = init.bindings.map((i) => structuredClone(i));
            }
            else {
                this.bindings = [];
            }
        }
        /**
         * Checks whether the input is a valid definition of a domain property.
         *
         * @param input The input to validate.
         * @param options Validation options.
         * @returns true when the passed input is a valid definition of a domain property.
         */
        static validate(input, options = {}) {
            if (!input || (typeof input !== 'object' && input !== null)) {
                return false;
            }
            const typed = input;
            if (options.strict && typed.kind !== DomainPropertyKind) {
                return false;
            }
            return true;
        }
        toJSON() {
            const result = {
                kind: this.kind,
                key: this.key,
                info: this.info.toJSON(),
                type: this.type || DomainPropertyList.string,
            };
            if (typeof this.index === 'boolean') {
                result.index = this.index;
            }
            if (typeof this.deprecated === 'boolean') {
                result.deprecated = this.deprecated;
            }
            if (typeof this.primary === 'boolean') {
                result.primary = this.primary;
            }
            if (typeof this.unique === 'boolean') {
                result.unique = this.unique;
            }
            if (typeof this.multiple === 'boolean') {
                result.multiple = this.multiple;
            }
            if (typeof this.required === 'boolean') {
                result.required = this.required;
            }
            if (typeof this.readOnly === 'boolean') {
                result.readOnly = this.readOnly;
            }
            if (typeof this.writeOnly === 'boolean') {
                result.writeOnly = this.writeOnly;
            }
            if (Array.isArray(this.tags) && this.tags.length) {
                result.tags = [...this.tags];
            }
            if (Array.isArray(this.semantics) && this.semantics.length) {
                result.semantics = toRaw(this, this.semantics)?.map((i) => structuredClone(i));
            }
            if (this.schema) {
                result.schema = structuredClone(toRaw(this, this.schema));
            }
            if (Array.isArray(this.bindings) && this.bindings.length) {
                result.bindings = toRaw(this, this.bindings)?.map((i) => structuredClone(i));
            }
            return result;
        }
        /**
         * Removes this property from the parent entity.
         * It calls the `removeProperty()` method of the parent entity.
         */
        remove() {
            const parent = this.getParentInstance();
            parent?.removeProperty(this.key);
        }
        /**
         * Returns a parent data entity where this data property exist.
         */
        getParentInstance() {
            return this.root.graph.node(this.parent);
        }
        /**
         * Checks whether the passed value is one of the supported data types.
         * @param value The value to test
         * @returns True when the passed value is one of the supported data types.
         */
        static isValidType(value) {
            if (typeof value !== 'string') {
                return false;
            }
            return DomainPropertyTypes.includes(value);
        }
        /**
         * Checks whether the passed value is one of the supported data property attributes.
         * @param value The value to test
         * @returns True when the passed value is one of the supported data property attributes.
         */
        static isValidAttribute(value) {
            if (typeof value !== 'string') {
                return false;
            }
            return DomainPropertyAttributes.includes(value);
        }
        /**
         * Checks whether the passed value of one of the supported `format` values.
         *
         * @param value The value to test
         * @returns `true` when the value is one of the supported `format` values.
         */
        static isValidFormat(value) {
            if (value === undefined) {
                // It is the valid value.
                return true;
            }
            if (typeof value !== 'string') {
                return false;
            }
            if (NumberFormats.includes(value)) {
                return true;
            }
            if (BinaryFormats.includes(value)) {
                return true;
            }
            return false;
        }
        /**
         * Creates the `schema` object if missing and returns it.
         * @returns The schema object.
         */
        ensureSchema() {
            if (!this.schema) {
                this.schema = {};
            }
            return this.schema;
        }
        /**
         * Creates if not existing and returns web bindings definition.
         * @returns The web binding definition
         */
        getWebBinding() {
            let object = this.bindings.find((i) => i.type === 'web');
            if (!object) {
                object = { type: 'web', schema: {} };
                this.bindings.push(object);
            }
            return object.schema;
        }
        /**
         * Returns the schema value of the binding, if any was created.
         * @param type The type of the binding to read.
         * @returns The binding schema, if any
         */
        readBinding(type) {
            const item = this.bindings.find((i) => i.type === type);
            return item?.schema || undefined;
        }
        /**
         * Creates a Property Shape of AMF.
         *
         * 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.property(this);
        }
        /**
         * Generates an example value for this property based on its schema and type.
         *
         * This method converts the property to an AMF shape and then generates
         * an example value using the ApiSchemaGenerator. The generated example
         * respects the property's type, constraints, and any defined schema.
         *
         * @param mime The mime type of the example (e.g., 'application/json', 'application/xml').
         * @param opts Optional configuration for example generation.
         * @returns The generated example value.
         * @example
         * ```typescript
         * const example = property.toExample('application/json');
         * console.log(example);
         * ```
         */
        toExample(mime, opts = {}) {
            // TODO: add support for semantic extensions and generate example values based on them.
            const shape = this.toApiShape();
            if (!shape.range) {
                return undefined;
            }
            const generator = new ApiSchemaGenerator(mime, {
                renderExamples: opts.renderExamples ?? true,
                renderMocked: opts.renderMocked ?? true,
                renderOptional: opts.renderOptional ?? true,
                selectedUnions: opts.selectedUnions,
            });
            return generator.generate(shape);
        }
        /**
         * Adds or updates a semantic to the property.
         * @param semantic The semantic to add to the property.
         * @throws Error if the semantic is not an property semantic.
         */
        addSemantic(semantic) {
            const sem = DataSemantics[semantic.id];
            if (!isPropertySemantic(sem)) {
                throw new Error(`Invalid semantic type: ${semantic.id}. Expected a property 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 property.
         * @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 property 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);
        }
        /**
         * Creates a duplicate of this domain property on the parent entity.
         * It places the duplicate on the parent entity right after the original property.
         */
        duplicate() {
            const parent = this.getParentInstance();
            if (!parent) {
                throw new Error(`Cannot duplicate property ${this.key} as it has no parent entity.`);
            }
            const originalIndex = parent.fields.findIndex((f) => f.key === this.key);
            if (originalIndex === -1) {
                throw new Error(`Cannot duplicate property ${this.key} as it does not exist on the parent entity fields list.`);
            }
            const baseName = this.info.name || 'field';
            const newName = parent.generateUniqueName(baseName);
            // Making a copy and restoring it through the `addProperty()` method
            // scales better than copying it manually.
            const copy = this.toJSON();
            // Delete properties that should not be copied
            delete copy.key;
            delete copy.primary; // Don't duplicate the primary key
            // Set the new name for the duplicated property
            if (copy.info) {
                copy.info.name = newName;
            }
            else {
                copy.info = { name: newName };
            }
            const result = parent.addProperty(copy);
            // Move the duplicate to be right after the original
            const fromIndex = parent.fields.length - 1;
            const duplicateField = parent.fields[fromIndex];
            parent.fields.splice(fromIndex, 1);
            parent.fields.splice(originalIndex + 1, 0, duplicateField);
            this.domain.notifyChange();
            return result;
        }
    };
})();
export { DomainProperty };
//# sourceMappingURL=DomainProperty.js.map