import { BuilderRegistry } from "../utils/registry";
import { ModelErrorDefinition } from "./ModelErrorDefinition";
import { Comparable, Constructor, Hashable, ModelArg, ModelBuilderFunction, ModelConstructor, Serializable, Validatable } from "./types";
/**
 * @description Registry type for storing and retrieving model constructors
 * @summary The ModelRegistry type defines a registry for model constructors that extends
 * the BuilderRegistry interface. It provides a standardized way to register, retrieve,
 * and build model instances, enabling the model system to work with different types of models.
 *
 * @interface ModelRegistry
 * @template T Type of model that can be registered, must extend Model
 * @extends BuilderRegistry<T>
 * @memberOf module:decorator-validation
 * @category Model
 */
export type ModelRegistry<T extends Model> = BuilderRegistry<T>;
/**
 * @description Registry manager for model constructors that enables serialization and rebuilding
 * @summary The ModelRegistryManager implements the ModelRegistry interface and provides
 * functionality for registering, retrieving, and building model instances. It maintains
 * a cache of model constructors indexed by name, allowing for efficient lookup and instantiation.
 * This class is essential for the serialization and deserialization of model objects.
 *
 * @param {function(Record<string, any>): boolean} [testFunction] - Function to test if an object is a model, defaults to {@link Model#isModel}
 *
 * @class ModelRegistryManager
 * @template M Type of model that can be registered, must extend Model
 * @implements ModelRegistry<M>
 * @category Model
 *
 * @example
 * ```typescript
 * // Create a model registry
 * const registry = new ModelRegistryManager();
 *
 * // Register a model class
 * registry.register(User);
 *
 * // Retrieve a model constructor by name
 * const UserClass = registry.get("User");
 *
 * // Build a model instance from a plain object
 * const userData = { name: "John", age: 30 };
 * const user = registry.build(userData, "User");
 * ```
 *
 * @mermaid
 * sequenceDiagram
 *   participant C as Client
 *   participant R as ModelRegistryManager
 *   participant M as Model Class
 *
 *   C->>R: new ModelRegistryManager(testFunction)
 *   C->>R: register(ModelClass)
 *   R->>R: Store in cache
 *   C->>R: get("ModelName")
 *   R-->>C: ModelClass constructor
 *   C->>R: build(data, "ModelName")
 *   R->>R: Get constructor from cache
 *   R->>M: new ModelClass(data)
 *   M-->>R: Model instance
 *   R-->>C: Model instance
 */
export declare class ModelRegistryManager<M extends Model> implements ModelRegistry<M> {
    private cache;
    private readonly testFunction;
    constructor(testFunction?: (obj: Record<string, any>) => boolean);
    /**
     * @description Registers a model constructor with the registry
     * @summary Adds a model constructor to the registry cache, making it available for
     * later retrieval and instantiation. If no name is provided, the constructor's name
     * property is used as the key in the registry.
     *
     * @param {ModelConstructor<M>} constructor - The model constructor to register
     * @param {string} [name] - Optional name to register the constructor under, defaults to constructor.name
     * @return {void}
     * @throws {Error} If the constructor is not a function
     */
    register(constructor: ModelConstructor<M>, name?: string): void;
    /**
     * @summary Gets a registered Model {@link ModelConstructor}
     * @param {string} name
     */
    get(name: string): ModelConstructor<M> | undefined;
    /**
     * @param {Record<string, any>} obj
     * @param {string} [clazz] when provided, it will attempt to find the matching constructor
     *
     * @throws Error If clazz is not found, or obj is not a {@link Model} meaning it has no {@link ModelKeys.ANCHOR} property
     */
    build(obj?: Record<string, any>, clazz?: string): M;
}
/**
 * @summary Bulk Registers Models
 * @description Useful when using bundlers that might not evaluate all the code at once
 *
 * @template M extends Model
 * @param {Array<Constructor<M>> | Array<{name: string, constructor: Constructor<M>}>} [models]
 *
 * @memberOf module:decorator-validation
 * @category Model
 */
export declare function bulkModelRegister<M extends Model>(...models: (Constructor<M> | {
    name: string;
    constructor: Constructor<M>;
})[]): void;
/**
 * @summary Abstract class representing a Validatable Model object
 * @description Meant to be used as a base class for all Model classes
 *
 * Model objects must:
 *  - Have all their required properties marked with '!';
 *  - Have all their optional properties marked as '?':
 *
 * @param {ModelArg<Model>} model base object from which to populate properties from
 *
 * @class Model
 * @category Model
 * @abstract
 * @implements Validatable
 * @implements Serializable
 *
 * @example
 *      class ClassName {
 *          @required()
 *          requiredPropertyName!: PropertyType;
 *
 *          optionalPropertyName?: PropertyType;
 *      }
 */
export declare abstract class Model implements Validatable, Serializable, Hashable, Comparable<Model> {
    protected constructor(arg?: ModelArg<Model>);
    /**
     * @description Validates the model object against its defined validation rules
     * @summary Validates the object according to its decorated properties, returning any validation errors
     *
     * @param {any[]} [exceptions] - Properties in the object to be ignored for the validation. Marked as 'any' to allow for extension but expects strings
     * @return {ModelErrorDefinition | undefined} - Returns a ModelErrorDefinition object if validation errors exist, otherwise undefined
     */
    hasErrors(...exceptions: any[]): ModelErrorDefinition | undefined;
    /**
     * @description Determines if this model is equal to another object
     * @summary Compare object equality recursively, checking all properties unless excluded
     *
     * @param {any} obj - Object to compare to
     * @param {string[]} [exceptions] - Property names to be excluded from the comparison
     * @return {boolean} - True if objects are equal, false otherwise
     */
    equals(obj: any, ...exceptions: string[]): boolean;
    /**
     * @description Converts the model to a serialized string representation
     * @summary Returns the serialized model according to the currently defined {@link Serializer}
     *
     * @return {string} - The serialized string representation of the model
     */
    serialize(): string;
    /**
     * @description Provides a human-readable string representation of the model
     * @summary Override the implementation for js's 'toString()' to provide a more useful representation
     *
     * @return {string} - A string representation of the model including its class name and JSON representation
     * @override
     */
    toString(): string;
    /**
     * @description Generates a hash string for the model object
     * @summary Defines a default implementation for object hash, relying on a basic implementation based on Java's string hash
     *
     * @return {string} - A hash string representing the model
     */
    hash(): string;
    /**
     * @description Converts a serialized string back into a model instance
     * @summary Deserializes a Model from its string representation
     *
     * @param {string} str - The serialized string to convert back to a model
     * @return {any} - The deserialized model instance
     * @throws {Error} If it fails to parse the string, or if it fails to build the model
     */
    static deserialize(str: string): any;
    /**
     * @description Copies properties from a source object to a model instance
     * @summary Repopulates the Object properties with the ones from the new object
     *
     * @template T
     * @param {T} self - The target model instance to update
     * @param {T | Record<string, any>} [obj] - The source object containing properties to copy
     * @return {T} - The updated model instance
     */
    static fromObject<T extends Model>(self: T, obj?: T | Record<string, any>): T;
    /**
     * @description Copies and rebuilds properties from a source object to a model instance, handling nested models
     * @summary Repopulates the instance with properties from the new Model Object, recursively rebuilding nested models
     *
     * @template T
     * @param {T} self - The target model instance to update
     * @param {T | Record<string, any>} [obj] - The source object containing properties to copy
     * @return {T} - The updated model instance with rebuilt nested models
     *
     * @mermaid
     * sequenceDiagram
     *   participant C as Client
     *   participant M as Model.fromModel
     *   participant B as Model.build
     *   participant R as Reflection
     *
     *   C->>M: fromModel(self, obj)
     *   M->>M: Get attributes from self
     *   loop For each property
     *     M->>M: Copy property from obj to self
     *     alt Property is a model
     *       M->>M: Check if property is a model
     *       M->>B: build(property, modelType)
     *       B-->>M: Return built model
     *     else Property is a complex type
     *       M->>R: Get property decorators
     *       R-->>M: Return decorators
     *       M->>M: Filter type decorators
     *       alt Property is Array/Set with list decorator
     *         M->>M: Process each item in collection
     *         loop For each item
     *           M->>B: build(item, itemModelType)
     *           B-->>M: Return built model
     *         end
     *       else Property is another model type
     *         M->>B: build(property, propertyType)
     *         B-->>M: Return built model
     *       end
     *     end
     *   end
     *   M-->>C: Return updated self
     */
    static fromModel<T extends Model>(self: T, obj?: T | Record<string, any>): T;
    /**
     * @description Configures the global model builder function
     * @summary Sets the Global {@link ModelBuilderFunction} used for building model instances
     *
     * @param {ModelBuilderFunction} [builder] - The builder function to set as the global builder
     * @return {void}
     */
    static setBuilder(builder?: ModelBuilderFunction): void;
    /**
     * @description Retrieves the currently configured global model builder function
     * @summary Returns the current global {@link ModelBuilderFunction} used for building model instances
     *
     * @return {ModelBuilderFunction | undefined} - The current global builder function or undefined if not set
     */
    static getBuilder(): ModelBuilderFunction | undefined;
    /**
     * @description Provides access to the current model registry
     * @summary Returns the current {@link ModelRegistryManager} instance, creating one if it doesn't exist
     *
     * @return {ModelRegistry<any>} - The current model registry, defaults to a new {@link ModelRegistryManager} if not set
     * @private
     */
    private static getRegistry;
    /**
     * @description Configures the model registry to be used by the Model system
     * @summary Sets the current model registry to a custom implementation
     *
     * @param {BuilderRegistry<any>} modelRegistry - The new implementation of Registry to use
     * @return {void}
     */
    static setRegistry(modelRegistry: BuilderRegistry<any>): void;
    /**
     * @description Registers a model constructor with the model registry
     * @summary Registers new model classes to make them available for serialization and deserialization
     *
     * @template T
     * @param {ModelConstructor<T>} constructor - The model constructor to register
     * @param {string} [name] - Optional name to register the constructor under, defaults to constructor.name
     * @return {void}
     *
     * @see ModelRegistry
     */
    static register<T extends Model>(constructor: ModelConstructor<T>, name?: string): void;
    /**
     * @description Retrieves a registered model constructor by name
     * @summary Gets a registered Model {@link ModelConstructor} from the model registry
     *
     * @template T
     * @param {string} name - The name of the model constructor to retrieve
     * @return {ModelConstructor<T> | undefined} - The model constructor if found, undefined otherwise
     *
     * @see ModelRegistry
     */
    static get<T extends Model>(name: string): ModelConstructor<T> | undefined;
    /**
     * @description Creates a model instance from a plain object
     * @summary Builds a model instance using the model registry, optionally specifying the model class
     *
     * @template T
     * @param {Record<string, any>} obj - The source object to build the model from
     * @param {string} [clazz] - When provided, it will attempt to find the matching constructor by name
     * @return {T} - The built model instance
     * @throws {Error} If clazz is not found, or obj is not a {@link Model} meaning it has no {@link ModelKeys.ANCHOR} property
     *
     * @see ModelRegistry
     */
    static build<T extends Model>(obj?: Record<string, any>, clazz?: string): T;
    /**
     * @description Retrieves the model metadata from a model instance
     * @summary Gets the metadata associated with a model instance, typically the model class name
     *
     * @template M
     * @param {M} model - The model instance to get metadata from
     * @return {string} - The model metadata (typically the class name)
     */
    static getMetadata<M extends Model>(model: M): any;
    /**
     * @description Retrieves all attribute names from a model class or instance
     * @summary Gets all attributes defined in a model, traversing the prototype chain to include inherited attributes
     *
     * @template V
     * @param {Constructor<V> | V} model - The model class or instance to get attributes from
     * @return {string[]} - Array of attribute names defined in the model
     */
    static getAttributes<V extends Model>(model: Constructor<V> | V): string[];
    /**
     * @description Compares two model instances for equality
     * @summary Determines if two model instances are equal by comparing their properties
     *
     * @template M
     * @param {M} obj1 - First model instance to compare
     * @param {M} obj2 - Second model instance to compare
     * @param {any[]} [exceptions] - Property names to exclude from comparison
     * @return {boolean} - True if the models are equal, false otherwise
     */
    static equals<M extends Model>(obj1: M, obj2: M, ...exceptions: any[]): boolean;
    /**
     * @description Validates a model instance against its validation rules
     * @summary Checks if a model has validation errors, optionally ignoring specified properties
     *
     * @template M
     * @param {M} model - The model instance to validate
     * @param {string[]} [propsToIgnore] - Properties to exclude from validation
     * @return {ModelErrorDefinition | undefined} - Returns validation errors if any, otherwise undefined
     */
    static hasErrors<M extends Model>(model: M, ...propsToIgnore: string[]): ModelErrorDefinition | undefined;
    /**
     * @description Converts a model instance to a serialized string
     * @summary Serializes a model instance using the configured serializer or the default one
     *
     * @template M
     * @param {M} model - The model instance to serialize
     * @return {string} - The serialized string representation of the model
     */
    static serialize<M extends Model>(model: M): any;
    /**
     * @description Generates a hash string for a model instance
     * @summary Creates a hash representation of a model using the configured algorithm or the default one
     *
     * @template M
     * @param {M} model - The model instance to hash
     * @return {string} - The hash string representing the model
     */
    static hash<M extends Model>(model: M): any;
    /**
     * @description Creates a metadata key for use with the Reflection API
     * @summary Builds the key to store as Metadata under Reflections
     *
     * @param {string} str - The base key to concatenate with the model reflection prefix
     * @return {string} - The complete metadata key
     */
    static key(str: string): string;
    /**
     * @description Determines if an object is a model instance or has model metadata
     * @summary Checks whether a given object is either an instance of the Model class or
     * has model metadata attached to it. This function is essential for serialization and
     * deserialization processes, as it helps identify model objects that need special handling.
     * It safely handles potential errors during metadata retrieval.
     *
     * @param {Record<string, any>} target - The object to check
     * @return {boolean} True if the object is a model instance or has model metadata, false otherwise
     *
     * @example
     * ```typescript
     * // Check if an object is a model
     * const user = new User({ name: "John" });
     * const isUserModel = isModel(user); // true
     *
     * // Check a plain object
     * const plainObject = { name: "John" };
     * const isPlainObjectModel = isModel(plainObject); // false
     * ```
     */
    static isModel(target: Record<string, any>): boolean;
    /**
     * @description Checks if a property of a model is itself a model or has a model type
     * @summary Determines whether a specific property of a model instance is either a model instance
     * or has a type that is registered as a model
     *
     * @template M
     * @param {M} target - The model instance to check
     * @param {string} attribute - The property name to check
     * @return {boolean | string | undefined} - Returns true if the property is a model instance,
     * the model name if the property has a model type, or undefined if not a model
     */
    static isPropertyModel<M extends Model>(target: M, attribute: string): boolean | string | undefined;
}
