import { SemanticType } from '../Semantics.js';
import { createEmailSemantic, DEFAULT_EMAIL_CONFIG } from '../definitions/Email.js';
import { createPasswordSemantic, DEFAULT_PASSWORD_CONFIG } from '../definitions/Password.js';
import { createPhoneSemantic } from '../definitions/Phone.js';
import { createPublicUniqueNameSemantic, DEFAULT_PUBLIC_UNIQUE_NAME_CONFIG } from '../definitions/PublicUniqueName.js';
import { createDescriptionSemantic } from '../definitions/Description.js';
import { CURRENCY_PRESETS } from '../definitions/Currency.js';
import { createStatusSemantic } from '../definitions/Status.js';
import { SKU_PRESETS } from '../definitions/SKU.js';
/**
 * Adds a field to the given entity that is automatically generated by the system.
 *
 * @param entity The entity to which the auto field will be added.
 * @param autoField The name of the auto field to add.
 * @returns The added DomainElement representing the auto field.
 * @throws Error if the auto field is not supported.
 */
export function addAutoField(entity, autoField) {
    switch (autoField) {
        case 'id':
            return addIdField(entity);
        case 'email':
            return addEmailField(entity);
        case 'name':
            return addNameField(entity);
        case 'description':
            return addDescriptionField(entity);
        case 'status':
            return addStatusField(entity);
        case 'version':
            return addVersionField(entity);
        case 'owner':
            return addOwnerField(entity);
        case 'created':
            return addCreatedAtField(entity);
        // case 'created-by':
        //   return addCreatedByField(entity)
        case 'updated':
            return addUpdatedAtField(entity);
        case 'updated-by':
            return addUpdatedByField(entity);
        case 'is-deleted':
            return addIsDeletedField(entity);
        case 'deleted':
            return addDeletedAtField(entity);
        case 'first-name':
            return addFirstNameField(entity);
        case 'last-name':
            return addLastNameField(entity);
        case 'phone':
            return addPhoneField(entity);
        case 'avatar-url':
            return addAvatarUrlField(entity);
        case 'public-unique-name':
            return addPublicUniqueNameField(entity);
        case 'price':
            return addPriceField(entity);
        case 'sku':
            return addSkuField(entity);
        case 'quantity':
            return addQuantityField(entity);
        case 'weight':
            return addWeightField(entity);
        case 'images':
            return addImagesField(entity);
        case 'session-id':
            return addSessionIdField(entity);
        case 'expires-at':
            return addExpiresAtField(entity);
        case 'unit-price':
            return addUnitPriceField(entity);
        default:
            throw new Error(`Unsupported auto field: ${autoField}`);
    }
}
/**
 * Adds the primary field (ID) to the given entity.
 * If the ID field already exists, it returns the existing field.
 * Otherwise, it creates a new ID field with a name based on the entity's name.
 *
 * Set properties:
 * - type: 'string'
 * - info.name: 'id'
 * - primary: true
 * - readOnly: true
 * - schema.defaultValue: { type: 'function', value: 'uuid-v4' }
 *
 * @param entity The entity to which the ID field will be added.
 * @returns The added primary field property.
 */
export function addIdField(entity, info = {}) {
    const existing = findIdField(entity, true);
    if (existing) {
        // If the ID field already exists, return it.
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: `id`, ...info },
        primary: true,
        readOnly: true,
        // I don't think we need to index the ID field..
        // index: true,
        type: 'string',
        // @TODO: We should experiment with the random ID generation using the nano library.
        schema: { defaultValue: { type: 'function', value: 'uuid-v4' } },
    });
    return prop;
}
/**
 * Adds an email field to the given entity.
 * If the email field already exists, it returns the existing field.
 * Otherwise, it creates a new email field with a semantic annotation.
 * @param entity The entity to which the email field will be added.
 * @returns The added email field property.
 */
export function addEmailField(entity, info = {}) {
    const existing = findEmailField(entity, true);
    if (existing) {
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: 'email', displayName: 'Email Address', ...info },
        type: 'string',
        required: true,
        index: true,
    });
    prop.addSemantic(createEmailSemantic(DEFAULT_EMAIL_CONFIG));
    return prop;
}
/**
 * Adds a password field to the given entity.
 * If the password field already exists, it returns the existing field.
 * Otherwise, it creates a new password field with a semantic annotation.
 * @param entity The entity to which the password field will be added.
 * @param info Additional information for the password field.
 * @returns The added password field property.
 */
export function addPasswordField(entity, info = {}) {
    const existing = findPasswordField(entity, true);
    if (existing) {
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: 'password', displayName: 'Password', ...info },
        type: 'string',
        required: true,
    });
    prop.addSemantic(createPasswordSemantic(DEFAULT_PASSWORD_CONFIG));
    return prop;
}
/**
 * Adds a version field to the given entity.
 * @param entity The entity to which the version field will be added.
 * The version field is used to track the version of the entity.
 * @returns The added version field property.
 */
export function addVersionField(entity, info = {}) {
    const existing = findVersionField(entity, true);
    if (existing) {
        // If the version field already exists, return it.
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: 'version', displayName: 'Version', ...info },
        readOnly: true,
        index: true,
        type: 'number',
        schema: { defaultValue: { type: 'function', value: 'incremental' }, minimum: 0 },
    });
    prop.addSemantic({
        id: SemanticType.Version,
    });
    return prop;
}
export function addNameField(entity, info = {}) {
    const existing = findNameField(entity, true);
    if (existing) {
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: 'name', displayName: 'Name', ...info },
        type: 'string',
    });
    prop.addSemantic({
        id: SemanticType.Title,
    });
    return prop;
}
/**
 * Adds a description field to the specified entity.
 * If a description field already exists, it returns the existing field.
 * Otherwise, it creates a new description field with the specified information.
 *
 * Set properties:
 * - type: 'string'
 * - info.name: 'description'
 * - info.displayName: 'Description'
 *
 * Set semantics:
 * - `SemanticType.Description`
 *
 * @param entity The entity to which the description field will be added.
 * @returns The added description field property.
 */
export function addDescriptionField(entity, info = {}) {
    const existing = findDescriptionField(entity, true);
    if (existing) {
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: 'description', displayName: 'Description', ...info },
        type: 'string',
    });
    prop.addSemantic(createDescriptionSemantic());
    return prop;
}
/**
 * Adds a status field to the specified entity.
 * If a status field already exists, it returns the existing field.
 * Otherwise, it creates a new status field with the specified information.
 *
 * Set properties:
 * - type: 'string'
 * - info.name: 'status'
 * - info.displayName: 'Status'
 * - schema.enum: ['Active', 'Draft', 'Archived']
 * - schema.defaultValue: { type: 'literal', value: 'Draft' }
 *
 * Set semantics:
 * - `SemanticType.Status`
 *
 * @param entity The entity to which the status field will be added.
 * @param info Additional information about the field.
 * @returns The added status field property.
 */
export function addStatusField(entity, info = {}) {
    const existing = findStatusField(entity, true);
    if (existing) {
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: 'status', displayName: 'Status', ...info },
        type: 'string',
        schema: {
            enum: ['Active', 'Draft', 'Archived'],
            defaultValue: { type: 'literal', value: 'Draft' },
        },
    });
    prop.addSemantic({
        id: SemanticType.Status,
    });
    return prop;
}
/**
 * Adds an owner field to the specified entity.
 * If an owner field already exists, it returns the existing field.
 * Otherwise, it creates a new owner field with the specified information.
 *
 * Set properties:
 * - type: 'string'
 * - info.name: 'owner'
 * - info.displayName: 'Owner'
 * - required: true
 *
 * Set semantics:
 * - `SemanticType.ResourceOwnerIdentifier`
 *
 * @param entity The entity to which the owner field will be added.
 * @param info Additional information about the field.
 * @returns The added owner field property.
 */
export function addOwnerField(entity, info = {}) {
    const existing = findOwnerField(entity, true);
    if (existing) {
        return existing;
    }
    const prop = entity.addAssociation({}, {
        info: { name: 'owner', displayName: 'Owner', ...info },
        required: true,
    });
    prop.addSemantic({
        id: SemanticType.ResourceOwnerIdentifier,
    });
    return prop;
}
// This is owner with different name. Use that one instead.
// function addCreatedByField(entity: DomainEntity): DomainElement {
//   const existing = findCreatedByField(entity, true)
//   if (existing) {
//     return existing
//   }
//   const prop = entity.addProperty({
//     info: { name: 'created_by', displayName: 'Created By' },
//     type: 'string',
//     readOnly: true,
//     index: true,
//   })
//   prop.addSemantic({
//     id: SemanticType.CreatedTimestamp,
//   })
//   return prop
// }
/**
 * Adds an updated by field to the specified entity as an association to the User entity.
 * If an updated by field already exists, it returns the existing field.
 * Otherwise, it creates a new updated by field with the specified information.
 *
 * Set properties:
 * - info.name: 'updated_by'
 * - info.displayName: 'Updated By'
 * - required: true
 *
 * Set semantics:
 * - `SemanticType.ResourceOwnerIdentifier`
 *
 * @param entity The entity to which the updated by field will be added.
 * @param info Additional information about the field.
 * @returns The added updated by field property.
 */
export function addUpdatedByField(entity, info = {}) {
    const existing = findUpdatedByField(entity, true);
    if (existing) {
        return existing;
    }
    const prop = entity.addAssociation({}, {
        info: { name: 'updated_by', displayName: 'Updated By', ...info },
        required: true,
    });
    prop.addSemantic({
        id: SemanticType.ResourceOwnerIdentifier,
    });
    return prop;
}
/**
 * Adds a created at field to the specified entity.
 * If a created at field already exists, it returns the existing field.
 * Otherwise, it creates a new created at field with the specified information.
 *
 * Set properties:
 * - type: 'datetime'
 * - info.name: 'created_at'
 * - info.displayName: 'Created At'
 * - readOnly: true
 * - index: true
 * - schema.defaultValue: { type: 'function', value: 'now' }
 *
 * Set semantics:
 * - `CreatedTimestamp`
 *
 * @param entity The entity to which the created at field will be added.
 * @param info Additional information about the field.
 * @returns The created created at field.
 */
export function addCreatedAtField(entity, info = {}) {
    const existing = findCreatedAtField(entity, true);
    if (existing) {
        // If the created_at field already exists, return it.
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: 'created_at', displayName: 'Created At', ...info },
        readOnly: true,
        index: true,
        type: 'datetime',
        schema: { defaultValue: { type: 'function', value: 'now' } },
    });
    prop.addSemantic({
        id: SemanticType.CreatedTimestamp,
    });
    return prop;
}
/**
 * Adds an updated at field to the specified entity.
 * If an updated at field already exists, it returns the existing field.
 * Otherwise, it creates a new updated at field with the specified information.
 *
 * Set properties:
 * - type: 'datetime'
 * - info.name: 'updated_at'
 * - info.displayName: 'Updated At'
 * - readOnly: true
 * - index: true
 * - schema.defaultValue: { type: 'function', value: 'now' }
 *
 * Set semantics:
 * - `UpdatedTimestamp`
 *
 * @param entity The entity to which the updated at field will be added.
 * @param info Additional information about the field.
 * @returns The created updated at field.
 */
export function addUpdatedAtField(entity, info = {}) {
    const existing = findUpdatedAtField(entity, true);
    if (existing) {
        // If the updated_at field already exists, return it.
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: 'updated_at', displayName: 'Updated At', ...info },
        readOnly: true,
        index: true,
        type: 'datetime',
        schema: { defaultValue: { type: 'function', value: 'now' } },
    });
    prop.addSemantic({
        id: SemanticType.UpdatedTimestamp,
    });
    return prop;
}
/**
 * Adds a deleted at field to the specified entity.
 * If a deleted at field already exists, it returns the existing field.
 * Otherwise, it creates a new deleted at field with the specified information.
 *
 * Set properties:
 * - required: true
 * - type: 'datetime'
 * - info.name: 'deleted_at'
 * - info.displayName: 'Deleted At'
 * - readOnly: true
 * - index: true
 * - schema.defaultValue: { type: 'function', value: 'now' }
 *
 * Set semantics:
 * - `SemanticType.DeletedTimestamp`
 *
 * @param entity The entity to which the deleted_at field will be added.
 * @param info Additional information about the field.
 * @returns The added deleted at field property.
 */
export function addDeletedAtField(entity, info = {}) {
    const existing = findDeletedAtField(entity, true);
    if (existing) {
        // If the deleted_at field already exists, return it.
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: 'deleted_at', displayName: 'Deleted At', ...info },
        readOnly: true,
        index: true,
        type: 'datetime',
        schema: { defaultValue: { type: 'function', value: 'now' } },
    });
    prop.addSemantic({
        id: SemanticType.DeletedTimestamp,
    });
    return prop;
}
/**
 * Adds an is_deleted field to the specified entity.
 * If an is_deleted field already exists, it returns the existing field.
 * Otherwise, it creates a new is_deleted field with the specified information.
 *
 * Set properties:
 * - type: 'boolean'
 * - info.name: 'is_deleted'
 * - info.displayName: 'Is Deleted'
 * - readOnly: true
 * - index: true
 * - schema.defaultValue: { type: 'literal', value: 'false' }
 *
 * Set semantics:
 * - `SemanticType.DeletedFlag`
 *
 * @param entity The entity to which the is_deleted field will be added.
 * @param info Additional information about the field.
 * @returns The added is_deleted field property.
 */
export function addIsDeletedField(entity, info = {}) {
    const existing = findIsDeletedField(entity, true);
    if (existing) {
        // If the is_deleted field already exists, return it.
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: 'is_deleted', displayName: 'Is Deleted', ...info },
        readOnly: true,
        index: true,
        type: 'boolean',
        schema: { defaultValue: { type: 'literal', value: 'false' } },
    });
    prop.addSemantic({
        id: SemanticType.DeletedFlag,
    });
    return prop;
}
/**
 * Adds a first name field to the specified entity.
 * If a first name field already exists, it returns the existing field.
 * Otherwise, it creates a new first name field with the specified information.
 *
 * Set properties:
 * - type: 'string'
 * - info.name: 'first_name'
 * - info.displayName: 'First Name'
 *
 * @param entity The entity to which the first name field will be added.
 * @param info Additional information about the field.
 * @returns The created first name field.
 */
export function addFirstNameField(entity, info = {}) {
    const existing = findFirstNameField(entity);
    if (existing) {
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: 'first_name', displayName: 'First Name', ...info },
        type: 'string',
    });
    return prop;
}
/**
 * Adds a first name field to the specified entity.
 * If a first name field already exists, it returns the existing field.
 * Otherwise, it creates a new first name field with the specified information.
 *
 * Set properties:
 * - type: 'string'
 * - info.name: 'first_name'
 * - info.displayName: 'First Name'
 *
 * @param entity The entity to which the first name field will be added.
 * @param info Additional information about the field.
 * @returns The created first name field.
 */
export function addLastNameField(entity, info = {}) {
    const existing = findLastNameField(entity);
    if (existing) {
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: 'last_name', displayName: 'Last Name', ...info },
        type: 'string',
    });
    return prop;
}
/**
 * Adds a phone field to the specified entity.
 * If a phone field already exists, it returns the existing field.
 * Otherwise, it creates a new phone field with the specified information.
 *
 * Set properties:
 * - type: 'string'
 * - info.name: 'phone'
 * - info.displayName: 'Phone Number'
 * - index: true (to search by phone number)
 *
 * Set semantics:
 * - id: SemanticType.Phone
 *
 * @param entity The entity to which the phone field will be added.
 * @param info Additional information about the field.
 * @returns The created phone field.
 */
export function addPhoneField(entity, info = {}) {
    const existing = findPhoneField(entity, true);
    if (existing) {
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: 'phone', displayName: 'Phone Number', ...info },
        type: 'string',
        index: true,
    });
    prop.addSemantic(createPhoneSemantic({
        format: 'international',
        requireCountryCode: true,
        requireVerification: true,
        verificationMethod: 'sms',
    }));
    return prop;
}
/**
 * Adds an avatar URL field to the specified entity.
 * If an avatar URL field already exists, it returns the existing field.
 * Otherwise, it creates a new avatar URL field with the specified information.
 *
 * Set properties:
 * - type: 'string'
 * - info.name: 'avatar_url'
 * - info.displayName: 'Avatar URL'
 *
 * Set semantics:
 * - id: SemanticType.ImageURL
 *
 * @param entity The entity to which the avatar URL field will be added.
 * @param info Additional information about the field.
 * @returns The created avatar URL field.
 */
export function addAvatarUrlField(entity, info = {}) {
    const existing = findAvatarUrlField(entity, true);
    if (existing) {
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: 'avatar_url', displayName: 'Avatar URL', ...info },
        type: 'string',
    });
    prop.addSemantic({ id: SemanticType.ImageURL });
    return prop;
}
/**
 * Adds a public unique name (slug) field to the specified entity.
 * If a public unique name field already exists, it returns the existing field.
 * Otherwise, it creates a new public unique name field with the specified information.
 *
 * Set properties:
 * - type: 'string'
 * - info.name: 'slug'
 * - info.displayName: 'Public Unique Name'
 * - index: true
 * - required: true
 * - unique: true
 *
 * Set semantics:
 * - id: SemanticType.PublicUniqueName
 *
 * @param entity The entity to which the public unique name field will be added.
 * @param info Additional information about the field.
 * @returns The created public unique name field.
 */
export function addPublicUniqueNameField(entity, info = {}) {
    const existing = findPublicUniqueNameField(entity, true);
    if (existing) {
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: 'slug', displayName: 'Public Unique Name', ...info },
        type: 'string',
        index: true,
        required: true,
        unique: true,
    });
    prop.addSemantic(createPublicUniqueNameSemantic(DEFAULT_PUBLIC_UNIQUE_NAME_CONFIG));
    return prop;
}
/**
 * Adds a SKU field to the specified entity.
 * If a SKU field already exists, it returns the existing field.
 * Otherwise, it creates a new SKU field with semantic annotation.
 *
 * Set properties:
 * - type: 'string'
 * - info.name: 'sku'
 * - info.displayName: 'SKU'
 * - required: true
 * - unique: true
 *
 * @param entity The entity to which the SKU field will be added.
 * @param info Additional information about the field.
 * @returns The created SKU field.
 */
export function addSkuField(entity, info = {}) {
    const existing = findSkuField(entity, true);
    if (existing) {
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: 'sku', displayName: 'SKU', ...info },
        type: 'string',
        required: true,
        unique: true,
    });
    prop.addSemantic({ ...SKU_PRESETS.PRODUCT_STANDARD });
    return prop;
}
/**
 * Adds a price field to the specified entity with currency semantic.
 * If a price field already exists, it returns the existing field.
 * Otherwise, it creates a new price field with proper currency handling.
 *
 * Set properties:
 * - type: 'number'
 * - info.name: 'price'
 * - info.displayName: 'Price'
 * - required: true
 * - schema.maximum: 0
 *
 * @param entity The entity to which the price field will be added.
 * @param info Additional information about the field.
 * @returns The created price field.
 */
export function addPriceField(entity, info = {}) {
    const existing = findPriceField(entity, true);
    if (existing) {
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: 'price', displayName: 'Price', ...info },
        type: 'number',
        required: true,
        schema: {
            maximum: 0,
        },
        bindings: [
            {
                type: 'web',
                schema: {
                    dataType: 'float',
                },
            },
        ],
    });
    prop.addSemantic({ ...CURRENCY_PRESETS.USD_CENTS });
    return prop;
}
/**
 * Adds a quantity field to the specified entity.
 * If a quantity field already exists, it returns the existing field.
 * Otherwise, it creates a new quantity field with validation.
 *
 * Set properties:
 * - type: 'number'
 * - info.name: 'quantity'
 * - info.displayName: 'Quantity'
 * - required: true
 * - schema.minimum: 0
 *
 * @param entity The entity to which the quantity field will be added.
 * @param info Additional information about the field.
 * @returns The created quantity field.
 */
export function addQuantityField(entity, info = {}) {
    const existing = findQuantityField(entity, true);
    if (existing) {
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: 'quantity', displayName: 'Quantity', ...info },
        type: 'number',
        required: true,
        schema: {
            minimum: 0,
        },
    });
    return prop;
}
/**
 * Adds a weight field to the specified entity.
 * If a weight field already exists, it returns the existing field.
 * Otherwise, it creates a new weight field.
 *
 * Set properties:
 * - type: 'number'
 * - info.name: 'weight'
 * - info.displayName: 'Weight'
 *
 * @param entity The entity to which the weight field will be added.
 * @param info Additional information about the field.
 * @returns The created weight field.
 */
export function addWeightField(entity, info = {}) {
    const existing = findWeightField(entity, true);
    if (existing) {
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: 'weight', displayName: 'Weight', ...info },
        type: 'number',
    });
    return prop;
}
/**
 * Adds an images field to the specified entity for multiple image URLs.
 * If an images field already exists, it returns the existing field.
 * Otherwise, it creates a new images field with ImageURL semantic.
 *
 * Set properties:
 * - type: 'string'
 * - info.name: 'images'
 * - info.displayName: 'Images'
 * - multiple: true
 *
 * @param entity The entity to which the images field will be added.
 * @param info Additional information about the field.
 * @returns The created images field.
 */
export function addImagesField(entity, info = {}) {
    const existing = findImagesField(entity, true);
    if (existing) {
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: 'images', displayName: 'Images', ...info },
        type: 'string',
        multiple: true,
    });
    prop.addSemantic({
        id: SemanticType.ImageURL,
    });
    return prop;
}
/**
 * Adds a status field with custom enum values to the specified entity.
 * This is not a regular enum field, it should represent a record status.
 * If a status field already exists, it returns the existing field.
 * Otherwise, it creates a new status field with provided enum values.
 *
 * Set properties:
 * - type: 'string'
 * - info.name: 'status'
 * - info.displayName: 'Status'
 * - required: true
 * - schema.enum: provided enum values
 * - schema.defaultValue: first enum value
 *
 * @param entity The entity to which the status field will be added.
 * @param enumValues Array of status values.
 * @param info Additional information about the field.
 * @returns The created status field.
 */
export function addCustomStatusField(entity, enumValues, info = {}) {
    const existing = findStatusField(entity, true);
    if (existing) {
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: 'status', displayName: 'Status', ...info },
        type: 'string',
        required: true,
        schema: {
            enum: enumValues,
            defaultValue: {
                type: 'literal',
                value: enumValues[0],
            },
        },
    });
    prop.addSemantic(createStatusSemantic());
    return prop;
}
/**
 * Adds a boolean field to the specified entity.
 * If the field already exists, it returns the existing field.
 * Otherwise, it creates a new boolean field with default value.
 *
 * Set properties:
 * - type: 'boolean'
 * - required: true
 * - schema.defaultValue: provided default or 'false'
 *
 * @param entity The entity to which the boolean field will be added.
 * @param fieldName Name of the boolean field.
 * @param defaultValue Default boolean value.
 * @param info Additional information about the field.
 * @returns The created boolean field.
 */
export function addBooleanField(entity, fieldName, defaultValue = 'false', info = {}) {
    const existing = findBooleanField(entity, fieldName, true);
    if (existing) {
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: fieldName, ...info },
        type: 'boolean',
        required: true,
        schema: {
            defaultValue: {
                type: 'literal',
                value: defaultValue,
            },
        },
    });
    return prop;
}
/**
 * Adds a session ID field to the specified entity.
 * If a session ID field already exists, it returns the existing field.
 * Otherwise, it creates a new session ID field.
 *
 * Set properties:
 * - type: 'string'
 * - info.name: 'sessionId'
 * - info.displayName: 'Session ID'
 *
 * @param entity The entity to which the session ID field will be added.
 * @param info Additional information about the field.
 * @returns The created session ID field.
 */
export function addSessionIdField(entity, info = {}) {
    const existing = findSessionIdField(entity, true);
    if (existing) {
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: 'sessionId', displayName: 'Session ID', ...info },
        type: 'string',
    });
    return prop;
}
/**
 * Adds an expiration datetime field to the specified entity.
 * If an expiration field already exists, it returns the existing field.
 * Otherwise, it creates a new expiration field.
 *
 * Set properties:
 * - type: 'datetime'
 * - info.name: 'expiresAt'
 * - info.displayName: 'Expires At'
 *
 * @param entity The entity to which the expiration field will be added.
 * @param info Additional information about the field.
 * @returns The created expiration field.
 */
export function addExpiresAtField(entity, info = {}) {
    const existing = findExpiresAtField(entity, true);
    if (existing) {
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: 'expiresAt', displayName: 'Expires At', ...info },
        type: 'datetime',
    });
    return prop;
}
/**
 * Adds a unit price field to the specified entity with currency semantic.
 * If a unit price field already exists, it returns the existing field.
 * Otherwise, it creates a new unit price field with proper currency handling.
 *
 * Set properties:
 * - type: 'number'
 * - info.name: 'unitPrice'
 * - info.displayName: 'Unit Price'
 * - required: true
 *
 * @param entity The entity to which the unit price field will be added.
 * @param info Additional information about the field.
 * @returns The created unit price field.
 */
export function addUnitPriceField(entity, info = {}) {
    const existing = findUnitPriceField(entity, true);
    if (existing) {
        return existing;
    }
    const prop = entity.addProperty({
        info: { name: 'unitPrice', displayName: 'Unit Price', ...info },
        type: 'number',
        required: true,
    });
    prop.addSemantic({ ...CURRENCY_PRESETS.USD_CENTS });
    return prop;
}
/**
 * Adds a currency amount field (subtotal, tax, shipping, etc.).
 */
export function addCurrencyAmountField(entity, fieldName, displayName, info = {}) {
    const prop = entity.addProperty({
        info: {
            name: fieldName,
            displayName,
            ...info,
        },
        type: 'number',
        required: true,
    });
    prop.addSemantic({ ...CURRENCY_PRESETS.USD_CENTS });
    return prop;
}
/**
 * Finds a field in the given entity that matches the specified predicate.
 * @param entity The entity to search within.
 * @param predicate A function that defines the search criteria.
 * @param direct Whether to search only the direct properties of the entity.
 * @returns The found field property or undefined.
 */
function findPropertyField(entity, predicate, direct = false) {
    for (const property of entity.properties) {
        if (predicate(property)) {
            return property;
        }
    }
    if (direct) {
        return undefined;
    }
    for (const parent of entity.listParents()) {
        // we can't call a recursive function here because it would scan vertically through the hierarchy
        // and not horizontally through all parents. We need to check each **direct** parent separately,
        // and then move up.
        for (const property of parent.properties) {
            if (predicate(property)) {
                return property;
            }
        }
    }
    return undefined;
}
/**
 * Finds a field in the given entity that matches the specified predicate.
 * @param entity The entity to search within.
 * @param predicate A function that defines the search criteria.
 * @param direct Whether to search only the direct properties of the entity.
 * @returns The found field association or undefined.
 */
function findAssociationField(entity, predicate, direct = false) {
    for (const assoc of entity.associations) {
        if (predicate(assoc)) {
            return assoc;
        }
    }
    if (direct) {
        return undefined;
    }
    for (const parent of entity.listParents()) {
        // we can't call a recursive function here because it would scan vertically through the hierarchy
        // and not horizontally through all parents. We need to check each **direct** parent separately,
        // and then move up.
        for (const assoc of parent.associations) {
            if (predicate(assoc)) {
                return assoc;
            }
        }
    }
    return undefined;
}
/**
 * Searches for the ID field in the given entity.
 * @param entity The entity to search for the ID field.
 * @param direct Whether to search only the direct properties of the entity.
 * @returns The found ID field property or undefined.
 */
function findIdField(entity, direct = false) {
    if (!direct) {
        // Find the primary key property in the entity, which can be inherited or defined on the entity itself.
        return entity.primaryKey();
    }
    for (const property of entity.properties) {
        if (property.primary) {
            return property;
        }
    }
}
/**
 * Searches for the email field in the given entity.
 * @param entity The entity to search for the email field.
 * @param direct Whether to search only the direct properties of the entity.
 * @returns The found email field property or undefined.
 */
function findEmailField(entity, direct = false) {
    return findPropertyField(entity, (property) => property.hasSemantic(SemanticType.Email), direct);
}
/**
 * Searches for the password field in the given entity.
 * @param entity The entity to search for the password field.
 * @param direct Whether to search only the direct properties of the entity.
 * @returns The found password field property or undefined.
 */
function findPasswordField(entity, direct = false) {
    return findPropertyField(entity, (property) => property.hasSemantic(SemanticType.Password), direct);
}
function findVersionField(entity, direct = false) {
    return findPropertyField(entity, (property) => {
        return property.hasSemantic(SemanticType.Version);
    }, direct);
}
function findNameField(entity, direct = false) {
    return findPropertyField(entity, (property) => property.hasSemantic(SemanticType.Title), direct);
}
function findDescriptionField(entity, direct = false) {
    return findPropertyField(entity, (property) => property.hasSemantic(SemanticType.Description), direct);
}
function findStatusField(entity, direct = false) {
    return findPropertyField(entity, (property) => property.hasSemantic(SemanticType.Status), direct);
}
function findOwnerField(entity, direct = false) {
    return findAssociationField(entity, (association) => association.hasSemantic(SemanticType.ResourceOwnerIdentifier) &&
        ['owner', 'author'].includes(association.info.name || ''), direct);
}
// function findCreatedByField(entity: DomainEntity, direct = false): DomainProperty | undefined {
//   return findField(entity, (property) => property.hasSemantic(SemanticType.CreatedTimestamp), direct)
// }
function findUpdatedByField(entity, direct = false) {
    return findAssociationField(entity, (association) => association.hasSemantic(SemanticType.ResourceOwnerIdentifier) &&
        ['updated_by', 'updatedBy'].includes(association.info.name || ''), direct);
}
function findCreatedAtField(entity, direct = false) {
    return findPropertyField(entity, (property) => {
        return property.hasSemantic(SemanticType.CreatedTimestamp);
    }, direct);
}
function findUpdatedAtField(entity, direct = false) {
    return findPropertyField(entity, (property) => {
        return property.hasSemantic(SemanticType.UpdatedTimestamp);
    }, direct);
}
function findDeletedAtField(entity, direct = false) {
    return findPropertyField(entity, (property) => {
        return property.hasSemantic(SemanticType.DeletedTimestamp);
    }, direct);
}
function findIsDeletedField(entity, direct = false) {
    return findPropertyField(entity, (property) => property.hasSemantic(SemanticType.DeletedFlag), direct);
}
export function addRecommendedFields(entity) {
    const fields = [];
    if (!findIdField(entity)) {
        fields.push(addIdField(entity));
    }
    // The version field is not always recommended, so we don't add it by default.
    // if (!findVersionField(entity)) {
    //   fields.push(addVersionField(entity))
    // }
    if (!findCreatedAtField(entity)) {
        fields.push(addCreatedAtField(entity));
    }
    // if (!findCreatedByField(entity)) {
    //   fields.push(addCreatedByField(entity))
    // }
    if (!findUpdatedAtField(entity)) {
        fields.push(addUpdatedAtField(entity));
    }
    if (!findUpdatedByField(entity)) {
        fields.push(addUpdatedByField(entity));
    }
    if (!findDeletedAtField(entity)) {
        fields.push(addDeletedAtField(entity));
    }
    if (!findIsDeletedField(entity)) {
        fields.push(addIsDeletedField(entity));
    }
    return fields;
}
function findFirstNameField(entity, direct = false) {
    return findPropertyField(entity, (property) => ['first_name', 'firstName'].includes(property.info.name || ''), direct);
}
function findLastNameField(entity, direct = false) {
    return findPropertyField(entity, (property) => ['last_name', 'lastName'].includes(property.info.name || ''), direct);
}
// Avatar URL has the `SemanticType.ImageURL` semantic, but it can be used for any image URL.
// It is not a specific semantic, but it is a common field in many entities.
function findAvatarUrlField(entity, direct = false) {
    return findPropertyField(entity, (property) => ['avatar_url', 'avatarUrl'].includes(property.info.name || ''), direct);
}
function findPhoneField(entity, direct = false) {
    return findPropertyField(entity, (property) => property.hasSemantic(SemanticType.Phone), direct);
}
function findPublicUniqueNameField(entity, direct = false) {
    return findPropertyField(entity, (property) => property.hasSemantic(SemanticType.PublicUniqueName), direct);
}
function findSkuField(entity, direct = false) {
    return findPropertyField(entity, (property) => property.hasSemantic(SemanticType.SKU), direct);
}
function findPriceField(entity, direct = false) {
    return findPropertyField(entity, (property) => ['price', 'unitPrice', 'totalPrice'].includes(property.info.name || '') &&
        property.hasSemantic(SemanticType.Currency), direct);
}
function findQuantityField(entity, direct = false) {
    return findPropertyField(entity, (property) => ['quantity', 'stock', 'amount'].includes(property.info.name || '') && property.type === 'number', direct);
}
function findWeightField(entity, direct = false) {
    return findPropertyField(entity, (property) => property.info.name === 'weight' && property.type === 'number', direct);
}
function findImagesField(entity, direct = false) {
    return findPropertyField(entity, (property) => property.info.name === 'images' && property.hasSemantic(SemanticType.ImageURL), direct);
}
function findBooleanField(entity, fieldName, direct = false) {
    return findPropertyField(entity, (property) => property.info.name === fieldName && property.type === 'boolean', direct);
}
function findSessionIdField(entity, direct = false) {
    return findPropertyField(entity, (property) => property.info.name === 'sessionId', direct);
}
function findUnitPriceField(entity, direct = false) {
    return findPropertyField(entity, (property) => property.info.name === 'unitPrice' && property.hasSemantic(SemanticType.Currency), direct);
}
function findExpiresAtField(entity, direct = false) {
    return findPropertyField(entity, (property) => property.info.name === 'expiresAt' && property.type === 'datetime', direct);
}
//# sourceMappingURL=Intelisense.js.map