import type {
  AnyEntity,
  EntityData,
  EntityMetadata,
  EntityName,
  EntityProperty,
  FilterQuery,
  Primary,
} from '../typings.js';
import { Collection } from '../entity/Collection.js';
import { Reference } from '../entity/Reference.js';
import { ChangeSet, ChangeSetType } from './ChangeSet.js';
import { ChangeSetPersister } from './ChangeSetPersister.js';
import type { EntityManager } from '../EntityManager.js';
import { IdentityMap } from './IdentityMap.js';
import type { LockOptions } from '../drivers/IDatabaseDriver.js';
/** Implements the Unit of Work pattern: tracks entity changes, computes change sets, and flushes them to the database. */
export declare class UnitOfWork {
  #private;
  constructor(em: EntityManager);
  /** Merges an entity into the identity map, taking a snapshot of its current state. */
  merge<T extends object>(entity: T, visited?: Set<AnyEntity>): void;
  /**
   * Entity data can wary in its shape, e.g. we might get a deep relation graph with joined strategy, but for diffing,
   * we need to normalize the shape, so relation values are only raw FKs. This method handles that.
   * @internal
   */
  normalizeEntityData<T extends object>(meta: EntityMetadata<T>, data: EntityData<T>): void;
  /**
   * @internal
   */
  register<T extends object>(entity: T, data?: EntityData<T>, options?: RegisterOptions): T;
  /**
   * @internal
   */
  dispatchOnLoadEvent(): Promise<void>;
  /**
   * @internal
   */
  unmarkAsLoaded(entity: AnyEntity): void;
  /**
   * Returns entity from the identity map. For composite keys, you need to pass an array of PKs in the same order as they are defined in `meta.primaryKeys`.
   */
  getById<T extends object>(
    entityName: EntityName<T>,
    id: Primary<T> | Primary<T>[],
    schema?: string,
    convertCustomTypes?: boolean,
  ): T | undefined;
  /**
   * Returns entity from the identity map by an alternate key (non-PK property).
   * @param convertCustomTypes - If true, the value is in database format and will be converted to JS format for lookup.
   *                             If false (default), the value is assumed to be in JS format already.
   */
  getByKey<T extends object>(
    entityName: EntityName<T>,
    key: string,
    value: unknown,
    schema?: string,
    convertCustomTypes?: boolean,
  ): T | undefined;
  /**
   * Stores an entity in the identity map under an alternate key (non-PK property).
   * Also sets the property value on the entity.
   * @param convertCustomTypes - If true, the value is in database format and will be converted to JS format.
   *                             If false (default), the value is assumed to be in JS format already.
   */
  storeByKey<T extends object>(
    entity: T,
    key: string,
    value: unknown,
    schema?: string,
    convertCustomTypes?: boolean,
  ): void;
  /** Attempts to extract a primary key from the where condition and look up the entity in the identity map. */
  tryGetById<T extends object>(
    entityName: EntityName<T>,
    where: FilterQuery<T>,
    schema?: string,
    strict?: boolean,
  ): T | null;
  /**
   * Returns map of all managed entities.
   */
  getIdentityMap(): IdentityMap;
  /**
   * Returns stored snapshot of entity state that is used for change set computation.
   */
  getOriginalEntityData<T extends object>(entity: T): EntityData<T> | undefined;
  /** Returns the set of entities scheduled for persistence. */
  getPersistStack(): Set<AnyEntity>;
  /** Returns the set of entities scheduled for removal. */
  getRemoveStack(): Set<AnyEntity>;
  /** Returns all computed change sets for the current flush. */
  getChangeSets(): ChangeSet<AnyEntity>[];
  /** Returns all M:N collections that need synchronization. */
  getCollectionUpdates(): Collection<AnyEntity>[];
  /** Returns extra updates needed for relations that could not be resolved in the initial pass. */
  getExtraUpdates(): Set<
    [
      AnyEntity,
      string | string[],
      AnyEntity | AnyEntity[] | Reference<any> | Collection<any>,
      ChangeSet<any> | undefined,
      ChangeSetType,
    ]
  >;
  /** Checks whether an auto-flush is needed before querying the given entity type. */
  shouldAutoFlush<T extends object>(meta: EntityMetadata<T>): boolean;
  /** Clears the queue of entity types that triggered auto-flush detection. */
  clearActionsQueue(): void;
  /** Computes and registers a change set for the given entity. */
  computeChangeSet<T extends object>(entity: T, type?: ChangeSetType): void;
  /** Recomputes and merges the change set for an already-tracked entity. */
  recomputeSingleChangeSet<T extends object>(entity: T): void;
  /** Marks an entity for persistence, cascading to related entities. */
  persist<T extends object>(
    entity: T,
    visited?: Set<AnyEntity>,
    options?: {
      checkRemoveStack?: boolean;
      cascade?: boolean;
    },
  ): void;
  /** Marks an entity for removal, cascading to related entities. */
  remove<T extends object>(
    entity: T,
    visited?: Set<AnyEntity>,
    options?: {
      cascade?: boolean;
    },
  ): void;
  /** Flushes all pending changes to the database within a transaction. */
  commit(): Promise<void>;
  private doCommit;
  lock<T extends object>(entity: T, options: LockOptions): Promise<void>;
  clear(): void;
  unsetIdentity(entity: AnyEntity): void;
  computeChangeSets(): void;
  scheduleExtraUpdate<T extends object>(changeSet: ChangeSet<T>, props: EntityProperty<T>[]): void;
  scheduleOrphanRemoval(entity?: AnyEntity, visited?: Set<AnyEntity>): void;
  cancelOrphanRemoval(entity: AnyEntity, visited?: Set<AnyEntity>): void;
  getOrphanRemoveStack(): Set<AnyEntity>;
  getChangeSetPersister(): ChangeSetPersister;
  private findNewEntities;
  /**
   * For TPT inheritance, creates separate changesets for each table in the hierarchy.
   * Uses the same entity instance for all changesets - only the metadata and payload differ.
   */
  private createTPTChangeSets;
  /**
   * Returns `true` when the change set should be skipped as it will be empty after the extra update.
   */
  private checkUniqueProps;
  private expandUniqueProps;
  private initIdentifier;
  private processReference;
  private processToOneReference;
  private processToManyReference;
  private runHooks;
  private postCommitCleanup;
  private cascade;
  private cascadeReference;
  private isCollectionSelfReferenced;
  private shouldCascade;
  private lockPessimistic;
  private lockOptimistic;
  private fixMissingReference;
  private persistToDatabase;
  private commitCreateChangeSets;
  private findExtraUpdates;
  private findEarlyUpdates;
  private commitUpdateChangeSets;
  private commitDeleteChangeSets;
  private commitExtraUpdates;
  private commitCollectionUpdates;
  private filterCollectionUpdates;
  /**
   * Orders change sets so FK constrains are maintained, ensures stable order (needed for node < 11)
   */
  private getChangeSetGroups;
  private getCommitOrder;
  private resetTransaction;
  /**
   * Takes snapshots of all processed collections
   */
  private takeCollectionSnapshots;
}
export interface RegisterOptions {
  refresh?: boolean;
  newEntity?: boolean;
  loaded?: boolean;
}
