import { OptimizerHints } from "./optimizer";
import { PipeLineIsHelper } from "./optimizer/is";
interface DefaultEngines {
    name: string;
}
interface DefaultDb {
    modelName: string;
}
export interface Populeration<Engines extends DefaultEngines, Db extends DefaultDb> {
    addEngine: AddEngineFn<Engines, Db>;
    addDb: AddDbFn<Engines, Db>;
    addRelation: AddRelationsFn<Engines, Db>;
    model: <ModelName extends Db["modelName"]>(modelName: ModelName) => ModelQueryBuilder<ModelName, Engines, Db>;
    getModelsByEngine: (engineName: Engines["name"]) => Record<Db["modelName"], any>;
    getModelsByDb: (db: any) => Record<Db["modelName"], any>;
    newQueryBuilder: <Fn extends (queryBuilder: QueryBuilder<any, ModelName, Engines, Db>) => QueryBuilder<any, ModelName, Engines, Db>, ModelName extends Db["modelName"] = Db["modelName"]>(fn: Fn) => Fn;
}
interface ModelQueryBuilder<ModelName extends Db["modelName"], Engines extends DefaultEngines, Db extends DefaultDb> {
    findMany: <Doc = any>(query?: FilterQuery) => QueryBuilder<Doc[], ModelName, Engines, Db> & Promise<Doc[]>;
    findOne: <Doc = any>(query?: FilterQuery) => QueryBuilder<Doc, ModelName, Engines, Db>;
    fillPopulations: (populationObject: Populate<Db>) => FilledPopulations<Db>;
}
export interface AggregatorOptions extends IOptions<any> {
    db: any;
    model: any;
}
export interface EngineInfo<Name> {
    name: Name;
    aggregator: (options: AggregatorOptions) => Promise<any>;
    transformModelName?: ({ modelName, model, db, }: {
        modelName: string;
        model: any;
        db: any;
    }) => string;
    optimizer?: (useOptimizer: boolean, options: AggregatorOptions) => AggregatorOptions;
    useOptimizer?: boolean;
    pipeLineIsHelper?: PipeLineIsHelper;
}
interface AddEngineFn<Engines extends DefaultEngines, Db extends DefaultDb> {
    <EngineName extends string>(engine: EngineInfo<EngineName>): Populeration<Engines | {
        name: EngineName;
    }, Db>;
}
interface DbInfo<Engine, ModelNames extends string> {
    engine: Engine;
    db: any;
    idField: string;
    defaultRequired: boolean;
    models: Record<ModelNames, any>;
}
interface AddDbFn<Engines extends DefaultEngines, Db extends DefaultDb> {
    <ModelNames extends string>(options: DbInfo<Engines["name"], ModelNames>): Populeration<Engines, Db | {
        modelName: ModelNames;
    }>;
}
interface ModelThroughRelation<Db extends DefaultDb> {
    name: Db["modelName"];
    model1Field: string;
    model2Field: string;
}
declare type ModelRelationsObject<Db extends DefaultDb> = {
    [field in string]: RelationOptions<Db> | Db["modelName"];
};
interface AddRelationsFn<Engines extends DefaultEngines, Db extends DefaultDb> {
    (modelName: Db["modelName"], relations: ModelRelationsObject<Db>): Populeration<Engines, Db>;
}
export declare function createPopurelation(): Populeration<never, never>;
declare type AllProps = "query" | "sort" | "limit" | "skip" | "populate" | "project" | "withCount" | "rawPipeline" | "invisible" | "addFields" | "count";
declare type NullifyOthers<T> = T & {
    [key in Exclude<AllProps, keyof T>]?: undefined;
};
export declare type QueryPipeline = NullifyOthers<{
    query: FilterQuery;
}>;
export declare type SortPipeline = NullifyOthers<{
    sort: SortQuery;
}>;
export declare type LimitPipeline = NullifyOthers<{
    limit: number;
}>;
export declare type SkipPipeline = NullifyOthers<{
    skip: number;
}>;
export declare type PopulatePipeline = NullifyOthers<{
    parentIdField: string;
    myIdField: string;
    field: string;
    populate: PopulatePipelineOptions;
}>;
export declare type ProjectPipeline = NullifyOthers<{
    project: Project;
}>;
export declare type WithCountPipeline = NullifyOthers<{
    withCount: true;
    countKey: string;
    docsKey: string;
    docsPipelines: Pipeline[];
}>;
export declare type RawPipeline = NullifyOthers<{
    rawPipeline: any;
}>;
export declare type AddFieldsPipeline = NullifyOthers<{
    addFields: AddFields;
}>;
export declare type CountPipeline = NullifyOthers<{
    count: true;
    countKey: string;
}>;
export declare type OptimizerPipeline = NullifyOthers<{
    invisible: true;
    optimizer: true;
    useOptimizer: boolean | undefined;
    hints: Partial<OptimizerHints> | undefined;
}>;
export declare type Pipeline = QueryPipeline | SortPipeline | LimitPipeline | SkipPipeline | PopulatePipeline | ProjectPipeline | WithCountPipeline | RawPipeline | AddFieldsPipeline | CountPipeline | OptimizerPipeline;
interface IOptions<ModelName = any> {
    readonly modelName: ModelName;
    readonly transformedModelName: string;
    readonly findOne: boolean;
    readonly pipelines: Pipeline[];
}
declare type FilterQuery = {
    [key in string]?: any;
};
declare type Project = {
    [key in string]?: any;
};
declare type SortQuery = {
    [key in string]?: any;
};
declare type AddFields = {
    [key in string]?: any;
};
export interface PopulatePipelineOptions {
    required: boolean;
    matchesMany: boolean;
    modelName: string;
    transformedModelName: string;
    alreadyPopulated?: boolean;
    foreignField: string;
    through?: ModelThroughRelation<any>;
    localField: string;
    globalPathPrefix: string;
    localPathPrefix: string;
    children?: Record<string, PopulatePipeline>;
}
interface PopulateOptions<Db extends DefaultDb> {
    required?: boolean;
    matchesMany?: boolean;
    model?: Db["modelName"];
    /**
     * @deprecated Not supported yet
     */
    through?: ModelThroughRelation<Db>;
    localField?: string;
    foreignField?: string;
    populate?: Populate<Db>;
}
export interface FilledPopulateOptions<Db extends DefaultDb> {
    required: boolean;
    matchesMany: boolean;
    model: Db["modelName"];
    /**
     * @deprecated Not supported yet
     */
    through?: ModelThroughRelation<Db>;
    localField: string;
    foreignField: string;
    populate?: FilledPopulations<Db>;
}
export declare type FilledPopulations<Db extends DefaultDb = DefaultDb> = Record<string, FilledPopulateOptions<Db>>;
export interface RelationOptions<Db extends DefaultDb> {
    required?: boolean;
    matchesMany: boolean;
    model: Db["modelName"];
    /**
     * @deprecated Not supported yet
     */
    through?: ModelThroughRelation<Db>;
    localField?: string;
    foreignField?: string;
}
declare type Populate<Db extends DefaultDb> = {
    [key in string]: 1 | true | PopulateOptions<Db>;
};
declare type WithCount<Doc, CountKey extends string = "count", DocsKey extends string = "docs"> = {
    [count in CountKey]: number;
} & {
    [docs in DocsKey]: Doc;
};
export interface QueryBuilder<Doc = any, ModelName = any, Engines extends DefaultEngines = any, Db extends DefaultDb = any> {
    where: (query?: FilterQuery) => QueryBuilder<Doc, ModelName, Engines, Db>;
    project: (project: Project) => QueryBuilder<Doc, ModelName, Engines, Db>;
    populate: {
        (field: string): QueryBuilder<Doc, ModelName, Engines, Db>;
        (field: string, options: PopulateOptions<Db>): QueryBuilder<Doc, ModelName, Engines, Db>;
        (bulkPopulate: Populate<Db>): QueryBuilder<Doc, ModelName, Engines, Db>;
    };
    sort: (sort?: SortQuery) => QueryBuilder<Doc, ModelName, Engines, Db>;
    limit: (number?: number) => QueryBuilder<Doc, ModelName, Engines, Db>;
    skip: (number?: number) => QueryBuilder<Doc, ModelName, Engines, Db>;
    withCount: {
        <CountKey extends string, DocsKey extends string, NewDoc = Doc>(keys: {
            countKey: CountKey;
            docsKey: DocsKey;
            skip?: number;
            limit?: number;
            queryBuilder?: (queryBuilder: QueryBuilder<Doc, ModelName, Engines, Db>) => QueryBuilder<NewDoc, ModelName, Engines, Db>;
        }): QueryBuilder<WithCount<NewDoc, CountKey, DocsKey>, ModelName, Engines, Db>;
        <NewDoc = Doc>(keys: {
            skip?: number;
            limit?: number;
            queryBuilder?: (queryBuilder: QueryBuilder<Doc, ModelName, Engines, Db>) => QueryBuilder<NewDoc, ModelName, Engines, Db>;
        }): QueryBuilder<WithCount<NewDoc>, ModelName, Engines, Db>;
        (include?: true): QueryBuilder<WithCount<Doc>, ModelName, Engines, Db>;
        (include: false): QueryBuilder<Doc, ModelName, Engines, Db>;
    };
    rawPipeline: (pipeline: any) => QueryBuilder<Doc, ModelName, Engines, Db>;
    addFields: (newFields?: AddFields) => QueryBuilder<Doc, ModelName, Engines, Db>;
    count: () => QueryBuilder<{
        count: number;
    }, ModelName, Engines, Db>;
    as: <T>() => QueryBuilder<T, ModelName, Engines, Db>;
    queryBuilder: <Fn extends ((queryBuilder: QueryBuilder<any, ModelName, Engines, Db>) => QueryBuilder<any, ModelName, Engines, Db>) | undefined | null>(fn: Fn) => Fn extends undefined ? QueryBuilder<Doc, ModelName, Engines, Db> : ReturnType<NonNullable<Fn>>;
    optimizer: (options?: {
        use?: boolean;
        hints?: Partial<OptimizerHints>;
    } | boolean) => QueryBuilder<Doc, ModelName, Engines, Db>;
    inspect: (fn?: (aggregators: {
        optimizedOptions: AggregatorOptions;
        unoptimizedOptions: AggregatorOptions;
    }) => void) => QueryBuilder<Doc, ModelName, Engines, Db>;
    exec(): Promise<Doc>;
}
export declare const getLastSkipAndLimitIndex: (pipelines: Pipeline[]) => number;
export {};
