import { type EntityDTO, type EntityKey, type EntityProperty, type FilterQuery, type IPrimaryKey, type Loaded, type LoadedCollection, type Populate, type Primary, CollectionBrand } from '../typings.js';
import { Reference } from './Reference.js';
import type { Transaction } from '../connections/Connection.js';
import type { CountOptions, FindOptions } from '../drivers/IDatabaseDriver.js';
import type { EntityLoaderOptions } from './EntityLoader.js';
/** Options for the `Collection.matching()` method to query a subset of collection items from the database. */
export interface MatchingOptions<T extends object, P extends string = never> extends FindOptions<T, P> {
    /** Additional filtering conditions for the query. */
    where?: FilterQuery<T>;
    /** Whether to store the matched items in the collection (makes it read-only). */
    store?: boolean;
    /** Transaction context for the query. */
    ctx?: Transaction;
}
/** Represents a to-many relation (1:m or m:n) as an iterable, managed collection of entities. */
export declare class Collection<T extends object, O extends object = object> {
    #private;
    readonly owner: O;
    [k: number]: T;
    readonly [CollectionBrand]: true;
    constructor(owner: O, items?: T[], initialized?: boolean);
    /**
     * Creates new Collection instance, assigns it to the owning entity and sets the items to it (propagating them to their inverse sides)
     */
    static isCollection<T extends object, O extends object>(item: any): item is Collection<T, O>;
    static create<T extends object, O extends object = object>(owner: O, prop: EntityKey<O>, items: undefined | T[], initialized: boolean): Collection<T, O>;
    /**
     * Ensures the collection is loaded first (without reloading it if it already is loaded).
     * Returns the Collection instance (itself), works the same as `Reference.load()`.
     */
    load<TT extends T, P extends string = never>(options?: InitCollectionOptions<TT, P>): Promise<LoadedCollection<Loaded<TT, P>>>;
    private setSerializationContext;
    /**
     * Initializes the collection and returns the items
     */
    loadItems<TT extends T, P extends string = never>(options?: InitCollectionOptions<TT, P>): Promise<Loaded<TT, P>[]>;
    /**
     * Gets the count of collection items from database instead of counting loaded items.
     * The value is cached (unless you use the `where` option), use `refresh: true` to force reload it.
     * When the dataloader is enabled (globally or per-query), multiple calls are batched into a single grouped query.
     */
    loadCount(options?: LoadCountOptions<T> | boolean): Promise<number>;
    /** Queries a subset of the collection items from the database with custom filtering, ordering, and pagination. */
    matching<TT extends T, P extends string = never>(options: MatchingOptions<T, P>): Promise<Loaded<TT, P>[]>;
    /**
     * Returns the items (the collection must be initialized)
     */
    getItems(check?: boolean): T[];
    /** Serializes the collection items to plain JSON objects. Returns an empty array if not initialized. */
    toJSON<TT extends T>(): EntityDTO<TT>[];
    /** Adds one or more items to the collection, propagating the change to the inverse side. Returns the number of items added. */
    add<TT extends T>(entity: TT | Reference<TT> | Iterable<TT | Reference<TT>>, ...entities: (T | Reference<T>)[]): number;
    /**
     * Remove specified item(s) from the collection. Note that removing item from collection does not necessarily imply deleting the target entity,
     * it means we are disconnecting the relation - removing items from collection, not removing entities from database - `Collection.remove()`
     * is not the same as `em.remove()`. If we want to delete the entity by removing it from collection, we need to enable `orphanRemoval: true`,
     * which tells the ORM we don't want orphaned entities to exist, so we know those should be removed.
     */
    remove<TT extends T>(entity: TT | Reference<TT> | Iterable<TT | Reference<TT>> | ((item: T) => boolean), ...entities: (T | Reference<T>)[]): number;
    /** Checks whether the collection contains the given item. */
    contains<TT extends T>(item: TT | Reference<TT>, check?: boolean): boolean;
    /** Returns the number of items in the collection. Throws if the collection is not initialized. */
    count(): number;
    /** Returns true if the collection has no items. Throws if the collection is not initialized. */
    isEmpty(): boolean;
    /** Returns whether this collection should be included in serialization based on its populated state. */
    shouldPopulate(populated?: boolean): boolean;
    /** Marks the collection as populated or not for serialization purposes. */
    populated(populated?: boolean | undefined): void;
    /** Initializes the collection by loading its items from the database. */
    init<TT extends T, P extends string = never>(options?: InitCollectionOptions<TT, P>): Promise<LoadedCollection<Loaded<TT, P>>>;
    private getEntityManager;
    private createCondition;
    private createManyToManyCondition;
    private createLoadCountCondition;
    private checkInitialized;
    /**
     * re-orders items after searching with `$in` operator
     */
    private reorderItems;
    private cancelOrphanRemoval;
    private validateModification;
    /** Converts all items in the collection to plain DTO objects. */
    toArray<TT extends T>(): EntityDTO<TT>[];
    /** Returns the primary key values (or a specific field) of all items in the collection. */
    getIdentifiers<U extends IPrimaryKey = Primary<T> & IPrimaryKey>(field?: string | string[]): U[];
    /**
     * @internal
     */
    addWithoutPropagation(entity: T): void;
    /** Replaces all items in the collection with the given items. */
    set(items: Iterable<T | Reference<T>>): void;
    private compare;
    /**
     * @internal
     */
    hydrate(items: T[], forcePropagate?: boolean, partial?: boolean, readonly?: boolean): void;
    /**
     * Remove all items from the collection. Note that removing items from collection does not necessarily imply deleting the target entity,
     * it means we are disconnecting the relation - removing items from collection, not removing entities from database - `Collection.remove()`
     * is not the same as `em.remove()`. If we want to delete the entity by removing it from collection, we need to enable `orphanRemoval: true`,
     * which tells the ORM we don't want orphaned entities to exist, so we know those should be removed.
     */
    removeAll(): void;
    /**
     * @internal
     */
    removeWithoutPropagation(entity: T): void;
    /**
     * Extracts a slice of the collection items starting at position start to end (exclusive) of the collection.
     * If end is null it returns all elements from start to the end of the collection.
     */
    slice(start?: number, end?: number): T[];
    /**
     * Tests for the existence of an element that satisfies the given predicate.
     */
    exists(cb: (item: T) => boolean): boolean;
    /**
     * Returns the first element of this collection that satisfies the predicate.
     */
    find<S extends T>(cb: (item: T, index: number) => item is S): S | undefined;
    /**
     * Returns the first element of this collection that satisfies the predicate.
     */
    find(cb: (item: T, index: number) => boolean): T | undefined;
    /**
     * Extracts a subset of the collection items.
     */
    filter<S extends T>(cb: (item: T, index: number) => item is S): S[];
    /**
     * Extracts a subset of the collection items.
     */
    filter(cb: (item: T, index: number) => boolean): T[];
    /**
     * Maps the collection items based on your provided mapper function.
     */
    map<R>(mapper: (item: T, index: number) => R): R[];
    /**
     * Maps the collection items based on your provided mapper function to a single object.
     */
    reduce<R>(cb: (obj: R, item: T, index: number) => R, initial?: R): R;
    /**
     * Maps the collection items to a dictionary, indexed by the key you specify.
     * If there are more items with the same key, only the first one will be present.
     */
    indexBy<K1 extends keyof T, K2 extends keyof T = never>(key: K1): Record<T[K1] & PropertyKey, T>;
    /**
     * Maps the collection items to a dictionary, indexed by the key you specify.
     * If there are more items with the same key, only the first one will be present.
     */
    indexBy<K1 extends keyof T, K2 extends keyof T = never>(key: K1, valueKey: K2): Record<T[K1] & PropertyKey, T[K2]>;
    /** Returns whether the collection has been initialized. Pass `fully = true` to also check that all items are initialized. */
    isInitialized(fully?: boolean): boolean;
    isDirty(): boolean;
    /** Returns whether the collection was partially loaded (propagation is disabled for partial collections). */
    isPartial(): boolean;
    setDirty(dirty?: boolean): void;
    get length(): number;
    [Symbol.iterator](): IterableIterator<T>;
    /**
     * @internal
     */
    takeSnapshot(forcePropagate?: boolean): void;
    /**
     * @internal
     */
    getSnapshot(): T[] | undefined;
    /**
     * @internal
     */
    get property(): EntityProperty;
    /**
     * @internal
     */
    set property(prop: EntityProperty);
    protected propagate(item: T, method: 'add' | 'remove' | 'takeSnapshot'): void;
    protected propagateToInverseSide(item: T, method: 'add' | 'remove' | 'takeSnapshot'): void;
    protected propagateToOwningSide(item: T, method: 'add' | 'remove' | 'takeSnapshot'): void;
    protected shouldPropagateToCollection(collection: Collection<O, T>, method: 'add' | 'remove' | 'takeSnapshot'): boolean;
    protected incrementCount(value: number): void;
}
/** Options for initializing a collection via `init()` or `load()`. */
export interface InitCollectionOptions<T, P extends string = never, F extends string = never, E extends string = never> extends EntityLoaderOptions<T, F, E> {
    /** Whether to use the dataloader for batching collection loads. */
    dataloader?: boolean;
    /** Relations to populate on the loaded items. */
    populate?: Populate<T, P>;
    /** Populate only references (without loading full entities). Works only with M:N collections that use pivot table. */
    ref?: boolean;
}
/** Options for the `Collection.loadCount()` method. */
export interface LoadCountOptions<T extends object> extends CountOptions<T, '*'> {
    /** Whether to reload the count from the database even if it is already cached. */
    refresh?: boolean;
    /** Additional filtering conditions for the count query. */
    where?: FilterQuery<T>;
    /** Whether to use the dataloader for batching count operations. */
    dataloader?: boolean;
}
