import { EventEmitter } from "events";
import { JSONSchema7 } from "json-schema";
import { ModelGraph, ModelsTree } from "../application.js";
import { Core } from "../core.js";
import { Service } from "../services/service.js";
import { Store, StoreEvents } from "../stores/store.js";
import { OperationContext } from "../utils/context.js";
import { HttpMethodType } from "../utils/httpcontext.js";
import { ModelActions, RawModel } from "./relations.js";
/**
 * Expose the model through API or GraphQL if it exists
 * The model will be exposed using its class name + 's'
 * If you need to have a specific plural, use the annotation WebdaPlural
 *  to define the plural name
 *
 * @returns
 */
export declare function Expose(params?: Partial<ExposeParameters>): (target: CoreModelDefinition) => void;
/**
 *
 */
export declare class CoreModelQuery {
    private type;
    private model;
    private attribute;
    private targetModel;
    constructor(type: string, model: CoreModel, attribute: string);
    /**
     * Retrieve target model definition
     * @returns
     */
    getTargetModel(): CoreModelDefinition;
    /**
     * Query the object
     * @param query
     * @returns
     */
    query(query?: string, context?: OperationContext): Promise<{
        results: CoreModel[];
        continuationToken?: string;
    }>;
    /**
     * Complete the query with condition
     * @param query
     * @returns
     */
    protected completeQuery(query?: string): string;
    /**
     *
     * @param callback
     * @param context
     */
    forEach(callback: (model: any) => Promise<void>, query?: string, context?: OperationContext, parallelism?: number): Promise<void>;
    /**
     * Iterate through all
     * @param context
     * @returns
     */
    iterate(query?: string, context?: OperationContext): AsyncGenerator<CoreModel, any, any>;
    /**
     * Get all the objects
     * @returns
     */
    getAll(context?: OperationContext): Promise<this[]>;
}
/**
 * Attribute of an object
 *
 * Filter out methods
 */
export type Attributes<T extends object> = {
    [K in keyof T]: T[K] extends Function ? never : K;
}[keyof T];
/**
 * Filter type keys by type
 */
export type FilterAttributes<T extends CoreModel, K> = {
    [L in keyof T]: T[L] extends K ? L : never;
}[keyof T];
/**
 * Define an Action on a model
 *
 * It is basically a method designed to be called by the API or external
 * systems
 */
export interface ModelAction {
    /**
     * Method for the route
     *
     * By default ["PUT"]
     */
    methods?: HttpMethodType[];
    /**
     * Define if the action is global or per object
     *
     * The method that implement the action must be called
     * `_${actionName}`
     */
    global?: boolean;
    /**
     * Additional openapi info
     */
    openapi?: any;
    /**
     * Method of the action
     */
    method?: string;
}
/**
 * Expose parameters for the model
 */
export interface ExposeParameters {
    /**
     * If model have parent but you still want it to be exposed as root
     * in domain-like service: DomainService, GraphQL
     *
     * It would create alias for the model in the root too
     */
    root?: boolean;
    /**
     * You can select to not expose some methods like create, update, delete, get, query
     */
    restrict: {
        /**
         * Create a new object
         */
        create?: boolean;
        /**
         * Update an existing object
         *
         * Includes PUT and PATCH
         */
        update?: boolean;
        /**
         * Query the object
         */
        query?: boolean;
        /**
         * Get a single object
         */
        get?: boolean;
        /**
         * Delete an object
         */
        delete?: boolean;
        /**
         * Do not create operations for the model
         */
        operation?: boolean;
    };
}
/**
 *
 */
export interface CoreModelDefinition<T extends CoreModel = CoreModel> extends EventEmitter {
    new (): T;
    /**
     * If the model have some Expose annotation
     */
    Expose?: ExposeParameters;
    /**
     * Create a CoreModel object loaded with the content of object
     *
     * It allows polymorphism from Store
     *
     * @param model to create by default
     * @param object to load data from
     * @param context if the data is unsafe from http
     */
    factory<T extends CoreModel>(this: Constructor<T>, object: Partial<T>, context?: OperationContext): T;
    /**
     * Get the model actions
     */
    getActions(): {
        [key: string]: ModelAction;
    };
    /**
     * Get the model store
     */
    store(): Store<T>;
    /**
     * Get the model schema
     */
    getSchema(): JSONSchema7;
    /**
     * Get the model hierarchy
     */
    getHierarchy(): {
        ancestors: string[];
        children: ModelsTree;
    };
    /**
     * Get the model relations
     */
    getRelations(): ModelGraph;
    /**
     * Get Model identifier
     */
    getIdentifier(short?: boolean): string;
    /**
     * Complete uuid useful to implement uuid prefix or suffix
     * @param uid
     */
    completeUid(uid: string): string;
    /**
     * Get the model uuid field if you do not want to use the uuid field
     */
    getUuidField(): string;
    /**
     * Permission query for the model
     * @param context
     */
    getPermissionQuery(context?: OperationContext): null | {
        partial: boolean;
        query: string;
    };
    /**
     * Reference to an object without doing a DB request yet
     */
    ref: typeof CoreModel.ref;
    /**
     * Create a new model
     * @param this
     * @param data
     */
    create<T extends CoreModel>(this: Constructor<T>, data: RawModel<T>): Promise<T>;
    /**
     * Query the model
     * @param query
     */
    query(query?: string, includeSubclass?: boolean, context?: OperationContext): Promise<{
        results: T[];
        continuationToken?: string;
    }>;
    /**
     * Iterate through objects
     * @param query
     * @param includeSubclass
     * @param context
     */
    iterate(query?: string, includeSubclass?: boolean, context?: OperationContext): AsyncGenerator<T>;
    /**
     * Listen to events on the model
     * @param event
     * @param listener
     * @param async
     */
    on<T extends CoreModel, Key extends keyof StoreEvents>(this: Constructor<T>, event: Key, listener: (evt: StoreEvents[Key]) => any, async?: boolean): this;
    /**
     * Listen to events on the model asynchronously
     * @param event
     * @param listener
     */
    onAsync<T extends CoreModel, Key extends keyof StoreEvents>(this: Constructor<T>, event: Key, listener: (evt: StoreEvents[Key]) => any): this;
    /**
     * Emit an event for this class
     * @param this
     * @param event
     * @param evt
     */
    emit<T extends CoreModel, Key extends keyof StoreEvents>(this: Constructor<T>, event: Key, evt: StoreEvents[Key]): any;
    /**
     * Emit an event for this class and wait for all listeners to finish
     * @param this
     * @param event
     * @param evt
     */
    emitSync<T extends CoreModel, Key extends keyof StoreEvents>(this: Constructor<T>, event: Key, evt: StoreEvents[Key]): Promise<void>;
    /**
     * Return the event on the model that can be listened to by an
     * external authorized source
     * @see authorizeClientEvent
     */
    getClientEvents(): ({
        name: string;
        global?: boolean;
    } | string)[];
    /**
     * Authorize a public event subscription
     * @param event
     * @param context
     */
    authorizeClientEvent(_event: string, _context: OperationContext, _model?: T): boolean;
    /**
     * EventEmitter interface
     * @param event
     * @param listener
     */
    addListener(event: string | symbol, listener: (...args: any[]) => void): this;
    /**
     * EventEmitter interface
     * @param event
     * @param listener
     */
    once(event: string | symbol, listener: (...args: any[]) => void): this;
    /**
     * EventEmitter interface
     * @param event
     * @param listener
     */
    removeListener(event: string | symbol, listener: (...args: any[]) => void): this;
    /**
     * EventEmitter interface
     * @param event
     * @param listener
     */
    off(eventName: string | symbol, listener: (...args: any[]) => void): this;
    /**
     * EventEmitter interface
     * @param event
     * @param listener
     */
    removeAllListeners(eventName?: any): this;
}
export type Constructor<T, K extends Array<any> = []> = new (...args: K) => T;
/**
 * Make a property hidden from json and schema
 *
 * This property will not be saved in the store
 * Nor it will be exposed in the API
 *
 * @param target
 * @param propertyKey
 */
export declare function NotEnumerable(target: any, propertyKey: string): void;
/**
 * Define an object method as an action
 * @param target
 * @param propertyKey
 */
export declare function Action(options?: {
    methods?: HttpMethodType[];
    openapi?: any;
    name?: string;
}): (target: any, propertyKey: string) => void;
export declare class ModelRef<T extends CoreModel> {
    protected uuid: string;
    protected store: Store<T>;
    protected model: CoreModelDefinition<T>;
    protected parent: CoreModel;
    constructor(uuid: string, model: CoreModelDefinition<T>, parent?: CoreModel);
    get(context?: OperationContext): Promise<T>;
    set(id: string | T): void;
    toString(): string;
    toJSON(): string;
    getUuid(): string;
    deleteItemFromCollection(prop: FilterAttributes<T, any[]>, index: number, itemWriteCondition: any, itemWriteConditionField?: string): Promise<this>;
    upsertItemToCollection(prop: FilterAttributes<T, any[]>, item: any, index?: number, itemWriteCondition?: any, itemWriteConditionField?: string): Promise<this>;
    exists(): Promise<boolean>;
    delete(): Promise<void>;
    conditionalPatch(updates: Partial<T>, conditionField: any, condition: any): Promise<boolean>;
    patch(updates: Partial<T>): Promise<boolean>;
    setAttribute(attribute: keyof T, value: any): Promise<this>;
    removeAttribute(attribute: keyof T, itemWriteCondition?: any, itemWriteConditionField?: keyof T): Promise<this>;
    incrementAttributes(info: {
        property: FilterAttributes<T, number>;
        value: number;
    }[]): Promise<this>;
}
export declare class ModelRefWithCreate<T extends CoreModel> extends ModelRef<T> {
    /**
     * Allow to create a model
     * @param defaultValue
     * @param context
     * @param withSave
     * @returns
     */
    create(defaultValue: RawModel<T>, context?: OperationContext, withSave?: boolean): Promise<T>;
    /**
     * Load a model from the known store
     *
     * @param this the class from which the static is called
     * @param id of the object to load
     * @param defaultValue if object not found return a default object
     * @param context to set on the object
     * @returns
     */
    getOrCreate(defaultValue: RawModel<T>, context?: OperationContext, withSave?: boolean): Promise<T>;
}
export declare class ModelRefCustom<T extends CoreModel> extends ModelRef<T> {
    uuid: string;
    constructor(uuid: string, model: CoreModelDefinition<T>, data: any, parent: CoreModel);
    toJSON(): any;
    getUuid(): string;
}
export type ModelRefCustomProperties<T extends CoreModel, K> = ModelRefCustom<T> & K;
export declare const Emitters: WeakMap<Constructor<CoreModel>, EventEmitter>;
/**
 * Basic Object in Webda
 *
 * It is used to define a data stored
 * Any variable starting with _ can only be set by the server
 * Any variable starting with __ won't be exported outside of the server
 *
 * @class
 * @WebdaModel
 */
declare class CoreModel {
    /**
     * Class reference to the object
     */
    __class: CoreModelDefinition<this>;
    /**
     * Type name
     */
    __type: string;
    /**
     * Types name
     */
    __types: string[];
    /**
     * Object context
     *
     * @TJS-ignore
     */
    __ctx: OperationContext;
    /**
     * If object is attached to its store
     *
     * @TJS-ignore
     */
    __store: Store<this>;
    __dirty: Set<string | symbol>;
    /**
     * Creation date
     */
    _creationDate: Date;
    /**
     * Last update date
     */
    _lastUpdate: Date;
    /**
     * If an object is deleted but not removed from DB for historic
     *
     * @ignore
     */
    __deleted: boolean;
    constructor();
    /**
     * Listen to events on the model
     * @param event
     * @param listener
     * @param async
     */
    static on<T extends CoreModel, Key extends keyof StoreEvents>(this: Constructor<T>, event: Key, listener: (evt: StoreEvents[Key]) => any, async?: boolean): any;
    /**
     * Emit an event for this class
     * @param this
     * @param event
     * @param evt
     */
    static emit<T extends CoreModel, Key extends keyof StoreEvents>(this: Constructor<T>, event: Key, evt: StoreEvents[Key]): void;
    /**
     * Emit an event for this class and wait for all listeners to finish
     * @param this
     * @param event
     * @param evt
     */
    static emitSync<T extends CoreModel, Key extends keyof StoreEvents>(this: Constructor<T>, event: Key, evt: StoreEvents[Key]): Promise<void>;
    /**
     * Listen to events on the model asynchronously
     * @param event
     * @param listener
     */
    static onAsync<Key extends keyof StoreEvents>(event: Key, listener: (evt: StoreEvents[Key]) => any, queue?: string): any;
    /**
     *
     * @param event
     * @param listener
     * @returns
     */
    static addListener<Key extends keyof StoreEvents>(event: Key, listener: (...args: any[]) => void): any;
    static emitter(method: any, ...args: any[]): any;
    static removeListener(...args: any[]): any;
    static off(...args: any[]): any;
    static once(...args: any[]): any;
    static removeAllListeners(...args: any[]): any;
    static setMaxListeners(...args: any[]): any;
    static getMaxListeners(...args: any[]): any;
    static listeners(...args: any[]): any;
    static rawListeners(...args: any[]): any;
    static listenerCount(...args: any[]): any;
    static prependListener(...args: any[]): any;
    static prependOnceListener(...args: any[]): any;
    static eventNames(...args: any[]): any;
    /**
     *
     * @returns
     */
    static getRelations(): ModelGraph;
    /**
     * Do not declare any public events by default
     * @returns
     */
    static getClientEvents(): any[];
    /**
     * Does not allow any event by default
     * @param _event
     * @param _context
     * @returns
     */
    static authorizeClientEvent(_event: string, _context: OperationContext, _model?: CoreModel): boolean;
    /**
     * Get Store for this model
     * @param this
     * @returns
     */
    static store<T extends CoreModel>(this: Constructor<T>): Store<T>;
    /**
     * Complete the uid with prefix if any
     *
     * Useful when object are stored with a prefix for full uuid
     * @param uid
     * @returns
     */
    static completeUid(uid: string): string;
    /**
     * Return the known schema
     * @returns
     */
    static getSchema(): JSONSchema7;
    /**
     * Get a reference to a model
     * @param this
     * @param uid
     * @returns
     */
    static ref<T extends CoreModel>(this: Constructor<T>, uid: string): ModelRefWithCreate<T>;
    /**
     * Get a reference to a model
     * @param this
     * @param uid
     * @returns
     */
    static create<T extends CoreModel>(this: Constructor<T>, data: RawModel<T>): Promise<T>;
    /**
     * Get identifier for this model
     * @returns
     */
    static getIdentifier(short?: boolean): string;
    /**
     * Get the model hierarchy
     *
     * Ancestors will contain every model it inherits from
     * Children will contain every model that inherits from this model in a tree structure
     */
    static getHierarchy(): {
        ancestors: string[];
        children: ModelsTree;
    };
    /**
     * Unflat an object
     * @param data
     * @param split
     * @returns
     */
    static unflat<T = any>(data: any, split?: string): T;
    /**
     * Create subobject for a model
     *
     * Useful for counters
     *
     * @param split
     * @returns
     */
    unflat<T>(split?: string): T;
    /**
     * Flat an object into another
     *
     * {
     *    a: {
     *      b: 1
     *    },
     *    c: 1
     * }
     *
     * become
     *
     * {
     *    "a#b": 1
     *    "c": 1
     * }
     *
     * @param target
     * @param data
     * @param split
     * @param prefix
     */
    static flat(target: any, data: any, split?: string, prefix?: string): any;
    /**
     * Complete the query with __type/__types
     * @param query
     * @param includeSubclass
     * @returns
     */
    protected static completeQuery(query: string, includeSubclass?: boolean): string;
    /**
     * Iterate through the model
     *
     * How to use a iterator is:
     *
     * ```
     * for await (const model of CoreModel.iterate()) {
     *    // Do something with my model
     * }
     * ```
     *
     * @param this
     * @param query
     * @param includeSubclass
     * @param context
     * @returns
     */
    static iterate<T extends CoreModel>(this: Constructor<T>, query?: string, includeSubclass?: boolean, context?: OperationContext): AsyncGenerator<T>;
    /**
     * Query for models
     * @param this
     * @param id
     * @returns
     */
    static query<T extends CoreModel>(this: Constructor<T>, query?: string, includeSubclass?: boolean, context?: OperationContext): Promise<{
        results: T[];
        continuationToken?: string;
    }>;
    /**
     * Return a proxy to the object to detect if dirty
     * @returns
     */
    getProxy(): this;
    /**
     * Return true if needs a save
     * @returns
     */
    isDirty(): boolean;
    /**
     *
     * @returns the uuid of the object
     */
    getUuid(): string;
    /**
     *
     * @param uuid
     * @param target
     */
    setUuid(uuid: string, target?: this): this;
    /**
     * Get actions callable on an object
     *
     * This will expose them by the Store with /storeUrl/{uuid}/{action}
     */
    static getActions<T>(this: Constructor<T>): ModelActions;
    /**
     * Return the expressable query for permission
     *
     * @param context of the query
     * @returns
     */
    static getPermissionQuery(_ctx: OperationContext): null | {
        partial: boolean;
        query: string;
    };
    checkAct(context: OperationContext, action: "create" | "update" | "get" | "delete" | "get_binary" | "detach_binary" | "attach_binary" | "update_binary_metadata" | "subscribe" | string): Promise<void>;
    /**
     * By default nothing is permitted on a CoreModel
     * @returns
     */
    canAct(_context: OperationContext, _action: "create" | "update" | "get" | "delete" | "get_binary" | "detach_binary" | "attach_binary" | "update_binary_metadata" | "subscribe" | string): Promise<string | boolean>;
    /**
     * Get the UUID property
     */
    static getUuidField(): string;
    /**
     * Create an object
     * @returns
     */
    static factory<T extends CoreModel>(this: Constructor<T>, object: Partial<T>, context?: OperationContext): T;
    /**
     * Detect what looks like a CoreModel but can be from different version
     * @param object
     * @returns
     */
    static instanceOf(object: any): boolean;
    /**
     * Return a unique reference within the application to the object
     *
     * It contains the Store containing it
     * @returns
     */
    getFullUuid(): string;
    /**
     * Get an object from the full uuid
     * @param core
     * @param fullUuid
     * @param partials
     * @returns
     */
    static fromFullUuid<T extends CoreModel = CoreModel>(fullUuid: string, core?: Core, partials?: any): Promise<T>;
    /**
     * Allow to define custom permission per attribute
     *
     * This method allows you to do permission based attribute
     * But also a mask destructive attribute
     *
     * @param key
     * @param value
     * @param mode
     * @param context
     * @returns updated value
     */
    attributePermission(key: string, value: any, mode: "READ" | "WRITE", context?: OperationContext): any;
    /**
     * Load an object from RAW
     *
     * @param raw data
     * @param secure if false will ignore any _ variable
     */
    load(raw: RawModel<this>, secure?: boolean, relations?: boolean): this;
    /**
     * Patch every attribute that is based on a relation
     * to add all the helpers
     */
    protected handleRelations(): void;
    /**
     * Context of the request
     */
    setContext(ctx: OperationContext): this;
    /**
     * Get object context
     *
     * Global object does not belong to a request
     */
    getContext<T extends OperationContext>(): T;
    /**
     * Return the object registered store
     */
    getStore(): Store<this>;
    /**
     * Get the object
     * @returns
     */
    get(): Promise<this>;
    /**
     * Get the object again
     *
     * @throws Error if the object is not coming from a store
     */
    refresh(): Promise<this>;
    /**
     * Delete this object
     *
     * @throws Error if the object is not coming from a store
     */
    delete(): Promise<void>;
    /**
     * Patch current object with this update
     * @param obj
     * @param conditionField if null no condition used otherwise fallback to lastUpdate
     * @param conditionValue
     */
    patch(obj: Partial<this>, conditionField?: keyof this | null, conditionValue?: any): Promise<void>;
    /**
     * Save this object
     *
     * @throws Error if the object is not coming from a store
     */
    save(full?: boolean | keyof this, ...args: (keyof this)[]): Promise<this>;
    /**
     * Validate objet modification
     *
     * @param ctx
     * @param updates
     */
    validate(ctx: OperationContext, updates: any, ignoreRequired?: boolean): Promise<boolean>;
    /**
     * Generate uuid for the object
     *
     * @param object
     * @returns
     */
    generateUid(_object?: any): string;
    /**
     * Return the object to be serialized without the __store
     *
     * @param stringify
     * @returns
     */
    toStoredJSON(stringify?: boolean): any | string;
    /**
     * Get a pre typed service
     *
     * @param service to retrieve
     * WARNING: Only object attached to a store can retrieve service
     */
    getService<T extends Service>(service: any): T;
    /**
     * Remove the specific attributes if not secure
     *
     *
     *
     * @param secure serialize server fields also
     * @returns filtered object to be serialized
     */
    _toJSON(secure: any): any;
    /**
     * Return the object without sensitive attributes
     *
     * @returns Object to serialize
     */
    toJSON(): any;
    /**
     * Called when object is about to be deleted
     */
    _onDelete(): Promise<void>;
    /**
     * Called when object has been deleted
     */
    _onDeleted(): Promise<void>;
    /**
     * Called when object is retrieved
     */
    _onGet(): Promise<void>;
    /**
     * Called when object is about to be saved
     */
    _onSave(): Promise<void>;
    /**
     * Called when object is saved
     */
    _onSaved(): Promise<void>;
    /**
     * Called when object is about to be updates
     *
     * @param updates to be send
     */
    _onUpdate(_updates: any): Promise<void>;
    /**
     * Called when object is updated
     */
    _onUpdated(): Promise<void>;
    /**
     * Set attribute on the object and database
     * @param property
     * @param value
     */
    setAttribute(property: keyof this, value: any): Promise<void>;
    /**
     * Remove attribute from both the object and db
     * @param property
     */
    removeAttribute(property: keyof this): Promise<void>;
    /**
     * Increment an attribute both in store and object
     * @param property
     * @param value
     */
    incrementAttribute(property: FilterAttributes<this, number>, value: number): Promise<void>;
    /**
     * Return a model ref
     * @returns
     */
    getRef<T extends this>(): ModelRef<T>;
    /**
     * Increment a attributes both in store and object
     * @param info
     */
    incrementAttributes(info: {
        property: string;
        value: number;
    }[]): Promise<void>;
}
/**
 * CoreModel with a uuid
 */
declare class UuidModel extends CoreModel {
    uuid: string;
    /**
     * @override
     */
    validate(ctx: OperationContext<any, any>, updates: any, ignoreRequired?: boolean): Promise<boolean>;
}
export { CoreModel, UuidModel };
