import { DefaultManager, Manager } from './manager';
import type { Field, ForeignKeyField } from './fields';
import type { CustomImportsForFieldType } from './fields/types';
import type { ManagersOfInstanceType, ModelFieldsType, ModelOptionsType } from './types';
import type { DatabaseAdapter } from '../engine';
import type { ExtractFieldsFromAbstracts, ExtractManagersFromAbstracts } from '../types';
declare global {
    var $PManagers: Map<string, Manager<any, any>> | undefined;
}
export declare class BaseModel {
    protected static $$type: string;
    protected __className: string;
    protected __stringfiedArgumentsOfEvents: Set<string>;
    protected __eventsUnsubscribers: (() => Promise<void>)[];
    protected static __isState: boolean;
    protected static __fieldParsersByEngine: Map<string, {
        input: Set<string>;
        output: Set<string>;
        toIgnore: Set<string>;
    }>;
    protected static __associations: {
        [modelName: string]: {
            byRelationName: Map<string, ForeignKeyField<any, any, any>>;
            byRelatedName: Map<string, ForeignKeyField<any, any, any>>;
        };
    };
    protected static __directlyRelatedTo: {
        [modelName: string]: string[];
    };
    protected static __indirectlyRelatedTo: {
        [modelName: string]: string[];
    };
    protected static __indirectlyRelatedModels: {
        [modelName: string]: {
            [relatedModelName: string]: string[];
        };
    } & {
        $set: {
            [modelName: string]: () => void;
        };
    };
    protected static __primaryKeys: string[];
    protected static __domainName: string;
    protected static __domainPath: string;
    protected static __callAfterAllModelsAreLoadedToSetupRelations: Map<string, (engineInstance: DatabaseAdapter) => void>;
    protected static __lazyOptions?: ModelOptionsType<any>;
    protected static __lazyFields?: ModelFieldsType;
    protected static __cachedHashedName?: string;
    protected static __cachedName: string;
    protected static __cachedOriginalName: string;
    protected static __cachedFields: ModelFieldsType | undefined;
    protected static __customOptions: any;
    protected static __cachedOptions: ModelOptionsType<any> | undefined;
    protected static __hasLoadedManagers: boolean;
    protected static __cachedManagers: ManagersOfInstanceType | undefined;
    protected static __hasLoadedAbstracts: boolean;
    protected static __instance: Model & BaseModel;
    protected static __initialized: {
        [engineName: string]: any;
    };
    constructor();
    protected __initializeManagers(engineInstance: DatabaseAdapter, modelInstance: Model & BaseModel, translatedModelInstance: {
        instance: any;
        modifyItself: (newTranslation: any) => void;
    }): Promise<void>;
    /**
     * This will add event listeners to the model. So when an event like `.set` or `.remove` is triggered, we will call
     * the event handler that was defined in the model using the `onSet` or `onRemove` options.
     *
     * By default we will take care to prevent the same data being triggered twice.
     * So we stringify the data and compare it, so for example if a model is trying to save the same data it
     * received through an event it will not trigger the event again by default.
     *
     * @param engineInstance - The current engine instance we are initializing this model instance
     */
    protected __initializeEvents(engineInstance: DatabaseAdapter): Promise<void>;
    /**
     * Initializes the model and returns the model instance for the current engine instance that is being used.
     */
    protected static __init(engineInstance: DatabaseAdapter, domainName: string, domainPath: string, lazyLoadFieldsCallback: (field: Field<any, any, any>, translatedField: any) => void, options?: {
        forceTranslate?: boolean;
    }): Promise<any>;
    /**
     * Compare this and another model to see if they are equal so we can create the migrations automatically for them.
     * You see that we do not compare the fields, for the fields we have a hole set of `CRUD` operations
     * if something changes there.
     * So it doesn't matter if two models don't have the same set of fields, if the options are equal,
     * then they are equal.
     *
     * @param model - The model to compare to the current model.
     *
     * @returns - Returns true if the models are equal and false otherwise.
     */
    protected static __compareModels(engine: DatabaseAdapter, originalModel: typeof Model & typeof BaseModel, otherModel: typeof Model & typeof BaseModel): Promise<boolean>;
    /**
     * Since most data is private, we use this to extract all the data that the model has that might be useful for engine
     * instances. This way we don't need to expose the model to the engine
     */
    protected __getModelAttributes(): {
        modelName: string;
        fields: Record<string, {
            $field: Field<any, any, any>;
            $model: (ModelType<any, any> & typeof Model & typeof BaseModel) | undefined;
            typeName: string;
            fieldName: string;
            modelName: string;
            isAuto: boolean;
            primaryKey: boolean;
            defaultValue: any;
            allowNull: boolean;
            unique: boolean;
            dbIndex: boolean;
            databaseName: string | undefined;
            underscored: boolean;
            customAttributes: unknown;
        }>;
        options: ModelOptionsType<any> | undefined;
    };
    /**
     * Retrieves the managers from the model constructor.
     *
     * This is useful for getting the managers from an abstract model class.
     */
    protected static __getManagers(): ManagersOfInstanceType;
    /**
     * This will load all of the abstract instances of the model. The abstracts will append 3 types of
     * data in the current model:
     * fields, options, managers and other abstracts
     *
     * So for fields we will just accept the ones not already defined in the field, if there
     * is any clash we will throw an error.
     * For options, we will only add them if the options are not already defined for the model.
     * Managers are similar to fields, we will not accept clashing managers with the same manager name.
     *
     * @param abstractInstance - The model class that we are instantiating.
     * @param composedAbstracts - We can have an abstract with an abstract and so on, for that a recursive approach
     * seems a good solution, this is an array with all of the abstracts that were already loaded for the current model.
     */
    protected static __loadAbstract(abstractConstructor: typeof Model & typeof BaseModel, composedAbstracts: string[]): void;
    /**
     * Initializes all of the abstract classes of the model and loads them to the current model.
     *
     * With this we will have the model with all of the fields, options and managers as the other abstracts.
     */
    protected static __initializeAbstracts(): void;
    /**
     * Get the options of the model. Use this to get the options of the model since here we will use the cached data
     * if it exists.
     */
    protected static _options(modelInstance?: any): ModelOptionsType<any> | undefined;
    protected static _fields(modelInstance?: any): ModelFieldsType;
    protected static __originalName(): string;
    /**
     * We use this so the name of the models does not clash with the original ones during migration.
     * During migration we will have 2 instances of the same model running at the
     * same time:
     *
     * 1. The state model, built from the migration files.
     * 2. The original model.
     */
    protected static __getName(): string;
    /**
     * We use the original model name to create a hash name of the model, a hash name of the model is used so
     * we can send events back and forth for the model between
     * multiple palmares instances.
     *
     * @returns - The hashed name of the model.
     */
    protected static __hashedName(): string;
    protected static __fieldsToString(engine: DatabaseAdapter, fields: ModelFieldsType, indentation?: number): Promise<{
        asString: string;
        customImports: CustomImportsForFieldType[];
    }>;
    protected static __optionsToString(engine: DatabaseAdapter, indentation: number | undefined, options: ModelOptionsType): Promise<string>;
}
declare const BaseModelWithoutMethods: {
    new (): Pick<BaseModel, never>;
};
/**
 * This class is used for initializing a model. This will work similar to django except that instead of
 * `objects` we use `instance` to make queries. So in other words, if you want to make queries directly
 * you will need to use. Also the instance will hold the actual instance of the model.
 *
 * >>> (await ModelName.getInstance()).findOne()
 * or
 * >>> (await ModelName.getInstance()).create()
 *
 * and so on.
 *
 * For creating Models it is simple, you've got 4 objects: `attributes`, `objects`, `managers` and `abstracts`
 *
 * The first one is obligatory, the rest is optional.
 * For `attributes` it is simple, just define the attributes of your model there as you would in sequelize
 * normally:
 *
 * Example:
 * In sequelize we define like:
 * >>> sequelize.define('User', {
 *      firstName: {
 *          type: DataTypes.STRING,
 *          allowNull: false
 *      },
 *      lastName: {
 *          type: DataTypes.STRING
 *      }
 * }, {
 *      tableName: 'user'
 * })
 *
 * Notice that 'User' is the name of the model, the second argument of the `.define()` function is the attributes,
 * it is exactly this object we will put in the attributes parameter. The second argument of the function is the
 * sequelize `options` sequelize parameter where we can define indexes, tableName and many other configuration.
 * You might want to check sequelize documentation for this: https://sequelize.org/master/manual/model-basics.html
 *
 * Okay so how do we rewrite this to something more concise and readable?
 * class User extends Model {
 *      attributes = {
 *          firstName: new model.fields.CharField(),
 *          lastName: new model.fields.CharField()
 *      }
 *
 *      options = {
 *          tableName: 'user'
 *      }
 *
 *      getFullName() {
 *          return this.firstName + this.lastName
 *      }
 *
 *      custom = new CustomManager()
 * }
 *
 * Simple and elegant. You will notice the `attributes` is defined, the options is optional, so instead of defining an
 * empty object you can totally omit it if you want.
 *
 * The `.getFullName` function is an instance function it will be appended to the instance so you can make a query like
 * and then it will return an User model, this model will have the method.
 *
 * >>> const response = await User.instance.findOne()
 * >>> response.getFullName()
 *
 * We underline many stuff from sequelize so you, the programmer, don't need to worry about tooling, it will just work.
 *
 * Take a notice at manager. Manager is for building custom managers similar to django managers.
 * Instead of making queries through your code you can keep all of your queries inside of managers and just
 * define them in your model.
 *
 * For the CustomManager, this will be our definition of a custom manager
 * >>> class CustomManager extends Manager {
 *         createUser(firstName, lastName) {
 *              return this.instance.create({ firstName: firstName, lastName: lastName })
 *         }
 *     }
 *
 * Okay so now we don't need to create a new user calling `.create` directly, instead we can use
 *
 * User.custom.createUser('Jane', 'Doe')
 *
 * This way we can keep queries more concise and representative by just making functions. Also
 * you can have the hole power of linting VSCode and other IDEs give you.
 */
export declare class Model extends BaseModelWithoutMethods {
    protected static $$type: string;
    fields: ModelFieldsType;
    options: ModelOptionsType<any> | undefined;
    abstracts: readonly (typeof Model & typeof BaseModel)[];
}
/**
 * Actual model returned
 */
export type ModelType<TModel, TDefinitions extends {
    engineInstance: DatabaseAdapter;
    customOptions: any;
} = {
    engineInstance: DatabaseAdapter;
    customOptions: any;
}> = {
    default: DefaultManager<TModel>;
    appendFields: <TOtherFields extends ModelFieldsType>(fields: TOtherFields) => ModelType<TModel & {
        fields: TOtherFields;
    }, TDefinitions>;
    setCustomOptions: <TCustomOptions extends Parameters<TDefinitions['engineInstance']['models']['translate']>[5]>(customOptions: TCustomOptions) => ModelType<TModel, {
        engineInstance: TDefinitions['engineInstance'];
        customOptions: TCustomOptions;
    }>;
    setManagers: <TManagers extends Record<string, Manager<any>>>(managers: TManagers) => ModelType<TModel, TDefinitions> & TManagers;
    new (): {
        fields: TModel extends {
            fields: infer TFields;
        } ? TFields : any;
        options: TModel extends {
            options: infer TOptions;
        } ? TOptions : any;
    };
};
/**
 * This function is needed so we can add the type to the DefaultManager. This will help keeping the API simple for the
 * end user without complicating too much stuff.
 */
export declare function model<TModel, TDefinitions extends {
    engineInstance: DatabaseAdapter;
    customOptions: any;
} = {
    engineInstance: DatabaseAdapter;
    customOptions: any;
}>(): ModelType<TModel, TDefinitions>;
/**
 * Used for creating a model from a function instead of needing to define a class.
 */
export declare function initialize<TTypeName extends string, TFields extends ModelFieldsType, const TAbstracts extends readonly {
    new (): {
        fields: any;
        options?: any;
    };
}[], const TOptions extends ModelOptionsType<{
    fields: TFields;
    abstracts: TAbstracts;
}>, const TManagers extends unknown | {
    [managerName: string]: Manager<any, any> | {
        [functionName: string]: (this: Manager<ReturnType<typeof model<{
            fields: ExtractFieldsFromAbstracts<TFields, TAbstracts>;
            options: TOptions;
        }>> & {
            fields: ExtractFieldsFromAbstracts<TFields, TAbstracts>;
            options: TOptions;
        }, any>, ...args: any) => any;
    };
} = unknown>(modelName: TTypeName, args: {
    fields: TFields;
    options?: TOptions;
    abstracts?: TAbstracts;
    managers?: TManagers;
}): ExtractManagersFromAbstracts<TAbstracts> & (unknown extends TManagers ? unknown : {
    [TManagerName in keyof TManagers]: Manager<any> & {
        [TFunctionName in keyof TManagers[TManagerName]]: TManagers[TManagerName][TFunctionName];
    };
}) & ModelType<{
    fields: ExtractFieldsFromAbstracts<TFields, TAbstracts>;
    options: TOptions;
}>;
export {};
//# sourceMappingURL=model.d.ts.map