import type { AddEager, AddOptional, Dictionary, EntityClass, EntityKey, EntityProperty, LazyRef, Loaded, LoadedReference, Primary, Ref } from '../typings.js';
import type { FindOneOptions, FindOneOrFailOptions } from '../drivers/IDatabaseDriver.js';
/** Wrapper around an entity that provides lazy loading capabilities and identity-preserving reference semantics. */
export declare class Reference<T extends object> {
    private entity;
    private property?;
    constructor(entity: T);
    /** Creates a Reference wrapper for the given entity, preserving identity if one already exists. */
    static create<T extends object>(entity: T | Ref<T>): Ref<T>;
    /** Creates a Reference wrapper for an entity identified by its primary key, wrapped in a Ref. */
    static createFromPK<T extends object>(entityType: EntityClass<T>, pk: Primary<T>, options?: {
        schema?: string;
    }): Ref<T>;
    /** Creates an uninitialized entity reference by primary key without wrapping it in a Reference. */
    static createNakedFromPK<T extends object>(entityType: EntityClass<T>, pk: Primary<T>, options?: {
        schema?: string;
    }): T;
    /**
     * Checks whether the argument is instance of `Reference` wrapper.
     */
    static isReference<T extends object>(data: any): data is Reference<T>;
    /**
     * Wraps the entity in a `Reference` wrapper if the property is defined as `ref`.
     */
    static wrapReference<T extends object, O extends object>(entity: T | Reference<T>, prop: EntityProperty<O, T>): Reference<T> | T;
    /**
     * Returns wrapped entity.
     */
    static unwrapReference<T extends object>(ref: T | Reference<T> | ScalarReference<T> | Ref<T>): T;
    /**
     * Ensures the underlying entity is loaded first (without reloading it if it already is loaded). Returns the entity.
     * If the entity is not found in the database (e.g. it was deleted in the meantime, or currently active filters disallow loading of it)
     * the method returns `null`. Use `loadOrFail()` if you want an error to be thrown in such a case.
     */
    load<TT extends T, P extends string = never, F extends string = never, E extends string = never>(options?: LoadReferenceOptions<TT, P, F, E>): Promise<Loaded<TT, P, F, E> | null>;
    /**
     * Ensures the underlying entity is loaded first (without reloading it if it already is loaded).
     * Returns the entity or throws an error just like `em.findOneOrFail()` (and respects the same config options).
     */
    loadOrFail<TT extends T, P extends string = never, F extends string = never, E extends string = never>(options?: LoadReferenceOrFailOptions<TT, P, F, E>): Promise<Loaded<TT, P, F, E>>;
    private set;
    /** Returns the underlying entity without checking initialization state. */
    unwrap(): T;
    /** Returns the underlying entity, throwing an error if the reference is not initialized. */
    getEntity(): T;
    /** Returns the value of a property on the underlying entity. Throws if the reference is not initialized. */
    getProperty<K extends keyof T>(prop: K): T[K];
    /** Loads the entity if needed, then returns the value of the specified property. */
    loadProperty<TT extends T, P extends string = never, K extends keyof TT = keyof TT>(prop: K, options?: LoadReferenceOrFailOptions<TT, P>): Promise<Loaded<TT, P>[K]>;
    /** Returns whether the underlying entity has been fully loaded from the database. */
    isInitialized(): boolean;
    /** Marks the underlying entity as populated or not for serialization purposes. */
    populated(populated?: boolean): void;
    /** Serializes the underlying entity to a plain JSON object. */
    toJSON(...args: any[]): Dictionary;
}
/** Wrapper for lazy scalar properties that provides on-demand loading from the database. */
export declare class ScalarReference<Value> {
    #private;
    private value?;
    private entity?;
    constructor(value?: Value | undefined, initialized?: boolean);
    /**
     * Ensures the underlying entity is loaded first (without reloading it if it already is loaded).
     * Returns either the whole entity, or the requested property.
     */
    load(options?: Omit<LoadReferenceOptions<any, any>, 'populate' | 'fields' | 'exclude'>): Promise<Value | undefined>;
    /**
     * Ensures the underlying entity is loaded first (without reloading it if it already is loaded).
     * Returns the entity or throws an error just like `em.findOneOrFail()` (and respects the same config options).
     */
    loadOrFail(options?: Omit<LoadReferenceOrFailOptions<any, any>, 'populate' | 'fields' | 'exclude'>): Promise<Value>;
    /** Sets the scalar value and marks the reference as initialized. */
    set(value: Value): void;
    /** Binds this scalar reference to a specific entity and property for lazy loading support. */
    bind<Entity extends object>(entity: Entity, property: EntityKey<Entity>): void;
    /** Returns the current scalar value, or undefined if not yet loaded. */
    unwrap(): Value | undefined;
    /** Returns whether the scalar value has been loaded. */
    isInitialized(): boolean;
    static isScalarReference(data: any): data is ScalarReference<any>;
}
/** Options for `Reference.load()` to control how the referenced entity is loaded. */
export interface LoadReferenceOptions<T extends object, P extends string = never, F extends string = never, E extends string = never> extends FindOneOptions<T, P, F, E> {
    /** Whether to use the dataloader for batching reference loads. */
    dataloader?: boolean;
}
/** Options for `Reference.loadOrFail()` which throws when the entity is not found. */
export interface LoadReferenceOrFailOptions<T extends object, P extends string = never, F extends string = never, E extends string = never> extends FindOneOrFailOptions<T, P, F, E> {
    /** Whether to use the dataloader for batching reference loads. */
    dataloader?: boolean;
}
/**
 * shortcut for `wrap(entity).toReference()`
 */
export declare function ref<I extends unknown | Ref<unknown> | undefined | null, T extends I & {}>(entity: I): (Ref<T> & LoadedReference<Loaded<T, AddEager<T>>>) | AddOptional<typeof entity>;
/**
 * shortcut for `Reference.createFromPK(entityType, pk)`
 */
export declare function ref<I extends unknown | undefined | null, T, PKV extends Primary<T> = Primary<T>>(entityType: EntityClass<T>, pk: I): Ref<T> | AddOptional<typeof pk>;
/**
 * Unwraps a reference to its underlying value, returning the entity or scalar. Works on any of:
 *
 * - `Ref<T>` / `Reference<T>` (entity) — calls `.unwrap()` to extract the wrapped entity, returns `T`.
 * - `LazyRef<T>` — type-only marker; runtime value is already `T`, so returned as-is.
 * - plain entity `T` — returned as-is.
 * - `ScalarReference<V>` / `ScalarRef<V>` — calls `.unwrap()` to extract the scalar value, returns `V | undefined`
 *   (a scalar reference may be bound without an initial value).
 *
 * Inverse of the {@link ref} helper. Use as a typed escape hatch when you know a relation is populated
 * but don't want to (or can't) thread `Loaded<T, 'relation'>` through a function signature.
 *
 * ```ts
 * function logAuthor(book: Book) {
 *   // `book.author` is typed as `LazyRef<Author>`, so `.name` is not directly accessible
 *   console.log(unref(book.author).name);
 * }
 *
 * // Scalar reference:
 * const scalar = ref('hello');
 * unref(scalar); // 'hello' (type: string | undefined)
 * ```
 *
 * Note: `unref` is a compile-time narrowing only for entity refs. On a `LazyRef<T>` or plain entity
 * relation that is a stub (PK only, never populated), reads of non-PK properties will still return
 * `undefined` at runtime — same footgun as a plain non-`Ref` relation.
 */
export declare function unref<V>(value: ScalarReference<V>): V | undefined;
export declare function unref<T extends object>(value: Ref<T> | LazyRef<T> | T): T;
/**
 * Unwraps a reference to its underlying value. Nullable variant — returns `null` unchanged.
 */
export declare function unref<T extends object>(value: Ref<T> | LazyRef<T> | T | null): T | null;
/**
 * Unwraps a reference to its underlying value. Optional variant — returns `undefined` unchanged.
 */
export declare function unref<T extends object>(value: Ref<T> | LazyRef<T> | T | undefined): T | undefined;
/**
 * Unwraps a reference to its underlying value. Nullable variant — returns `null`/`undefined` unchanged.
 */
export declare function unref<T extends object>(value: Ref<T> | LazyRef<T> | T | null | undefined): T | null | undefined;
/**
 * shortcut for `Reference.createNakedFromPK(entityType, pk)`
 */
export declare function rel<T, PK extends Primary<T>>(entityType: EntityClass<T>, pk: T | PK): T;
export { Reference as Ref };
