import {
  EntityMetadata,
  type AnyEntity,
  type EntityKey,
  type Constructor,
  type DeepPartial,
  type EntityName,
  type EntityProperty,
  type CleanKeys,
  type ExpandProperty,
  type IsNever,
  type EntityCtor,
} from '../typings.js';
import { type EventType, ReferenceKind } from '../enums.js';
import type { EventArgs } from '../events/EventSubscriber.js';
import { Type } from '../types/Type.js';
import type {
  PropertyOptions,
  ManyToOneOptions,
  OneToOneOptions,
  OneToManyOptions,
  ManyToManyOptions,
  EmbeddedOptions,
  EnumOptions,
  PrimaryKeyOptions,
  SerializedPrimaryKeyOptions,
  IndexOptions,
  UniqueOptions,
} from './types.js';
type TypeType =
  | string
  | NumberConstructor
  | StringConstructor
  | BooleanConstructor
  | DateConstructor
  | ArrayConstructor
  | Constructor<Type<any>>
  | Type<any>;
type TypeDef<Target> =
  | {
      type: TypeType;
    }
  | {
      entity: () => EntityName<Target> | EntityName[];
    };
type EmbeddedTypeDef<Target> =
  | {
      type: TypeType;
    }
  | {
      entity: () => EntityName<Target> | EntityName[];
    };
/** Union type representing all possible property definition shapes in an EntitySchema. */
export type EntitySchemaProperty<Target, Owner> =
  | ({
      kind: ReferenceKind.MANY_TO_ONE | 'm:1';
    } & TypeDef<Target> &
      ManyToOneOptions<Owner, Target>)
  | ({
      kind: ReferenceKind.ONE_TO_ONE | '1:1';
    } & TypeDef<Target> &
      OneToOneOptions<Owner, Target>)
  | ({
      kind: ReferenceKind.ONE_TO_MANY | '1:m';
    } & TypeDef<Target> &
      OneToManyOptions<Owner, Target>)
  | ({
      kind: ReferenceKind.MANY_TO_MANY | 'm:n';
    } & TypeDef<Target> &
      ManyToManyOptions<Owner, Target>)
  | ({
      kind: ReferenceKind.EMBEDDED | 'embedded';
    } & EmbeddedTypeDef<Target> &
      EmbeddedOptions<Owner, Target> &
      PropertyOptions<Owner>)
  | ({
      enum: true;
    } & EnumOptions<Owner>)
  | (TypeDef<Target> & PropertyOptions<Owner>);
type OmitBaseProps<Entity, Base> = IsNever<Base> extends true ? Entity : Omit<Entity, keyof Base>;
/** Configuration object for defining an entity via EntitySchema. */
export type EntitySchemaMetadata<Entity, Base = never, Class extends EntityCtor = EntityCtor<Entity>> = Omit<
  Partial<EntityMetadata<Entity>>,
  'name' | 'properties' | 'extends'
> &
  (
    | {
        name: string;
      }
    | {
        class: Class;
        name?: string;
      }
  ) & {
    extends?: EntityName<Base>;
  } & {
    properties?: {
      [Key in keyof OmitBaseProps<Entity, Base> as CleanKeys<OmitBaseProps<Entity, Base>, Key>]-?: EntitySchemaProperty<
        ExpandProperty<NonNullable<Entity[Key]>>,
        Entity
      >;
    };
  } & {
    inheritance?: 'tpt';
  };
/** Class-less entity definition that provides a programmatic API for defining entities without decorators. */
export declare class EntitySchema<Entity = any, Base = never, Class extends EntityCtor = EntityCtor<Entity>> {
  /**
   * When schema links the entity class via `class` option, this registry allows the lookup from opposite side,
   * so we can use the class in `entities` option just like the EntitySchema instance.
   */
  static REGISTRY: Map<AnyEntity, EntitySchema>;
  /** @internal Type-level marker for fast entity type inference */
  readonly '~entity': Entity;
  private readonly _meta;
  private internal;
  private initialized;
  constructor(meta: EntitySchemaMetadata<Entity, Base, Class>);
  /**
   * Checks if the given value is an EntitySchema instance, using duck-typing
   * as a fallback when `instanceof` fails due to CJS/ESM dual-package hazard
   * (e.g. when using `tsx` or `@swc-node/register` with `"type": "commonjs"` projects).
   */
  static is(item: unknown): item is EntitySchema;
  /** Creates an EntitySchema from existing EntityMetadata (used internally). */
  static fromMetadata<T = AnyEntity, U = never>(
    meta: EntityMetadata<T> | DeepPartial<EntityMetadata<T>>,
  ): EntitySchema<T, U>;
  /** Adds a scalar property to the entity schema. */
  addProperty(
    name: EntityKey<Entity>,
    type?: TypeType,
    options?: PropertyOptions<Entity> | EntityProperty<Entity>,
  ): void;
  /** Adds an enum property to the entity schema. */
  addEnum(name: EntityKey<Entity>, type?: TypeType, options?: EnumOptions<Entity>): void;
  /** Adds a version property for optimistic locking. */
  addVersion(name: EntityKey<Entity>, type: TypeType, options?: PropertyOptions<Entity>): void;
  /** Adds a primary key property to the entity schema. */
  addPrimaryKey(name: EntityKey<Entity>, type: TypeType, options?: PrimaryKeyOptions<Entity>): void;
  /** Adds a serialized primary key property (e.g. for MongoDB ObjectId). */
  addSerializedPrimaryKey(name: EntityKey<Entity>, type: TypeType, options?: SerializedPrimaryKeyOptions<Entity>): void;
  /** Adds an embedded property to the entity schema. */
  addEmbedded<Target = AnyEntity>(name: EntityKey<Entity>, options: EmbeddedOptions<Entity, Target>): void;
  /** Adds a many-to-one relation to the entity schema. */
  addManyToOne<Target = AnyEntity>(
    name: EntityKey<Entity>,
    type: TypeType,
    options: ManyToOneOptions<Entity, Target>,
  ): void;
  /** Adds a many-to-many relation to the entity schema. */
  addManyToMany<Target = AnyEntity>(
    name: EntityKey<Entity>,
    type: TypeType,
    options: ManyToManyOptions<Entity, Target>,
  ): void;
  /** Adds a one-to-many relation to the entity schema. */
  addOneToMany<Target = AnyEntity>(
    name: EntityKey<Entity>,
    type: TypeType,
    options: OneToManyOptions<Entity, Target>,
  ): void;
  /** Adds a one-to-one relation to the entity schema. */
  addOneToOne<Target = AnyEntity>(
    name: EntityKey<Entity>,
    type: TypeType,
    options: OneToOneOptions<Entity, Target>,
  ): void;
  /** Adds an index definition to the entity schema. */
  addIndex<Key extends string>(options: IndexOptions<Entity, Key>): void;
  /** Adds a unique constraint definition to the entity schema. */
  addUnique<Key extends string>(options: UniqueOptions<Entity, Key>): void;
  /** Sets a custom repository class for this entity. */
  setCustomRepository(repository: () => Constructor): void;
  /** Sets the base entity that this schema extends. */
  setExtends(base: EntityName): void;
  /** Sets or replaces the entity class associated with this schema. */
  setClass(cls: Class): void;
  /** Returns the underlying EntityMetadata. */
  get meta(): EntityMetadata<Entity, Class>;
  /** Returns the entity class name. */
  get name(): string | EntityName<Entity>;
  /** Returns the database table name. */
  get tableName(): string;
  get class(): Class;
  get properties(): Record<string, any>;
  new(...params: ConstructorParameters<Class>): Entity;
  /**
   * @internal
   */
  init(): this;
  /**
   * Check if this entity is part of a TPT hierarchy by walking up the extends chain.
   * This handles mid-level abstract entities (e.g., Animal -> Mammal -> Dog where Mammal is abstract).
   */
  private isPartOfTPTHierarchy;
  private initProperties;
  private initPrimaryKeys;
  private normalizeType;
  private createProperty;
  private rename;
  private renameCompositeOptions;
  /**
   * Adds a lifecycle hook handler to the entity schema.
   * This method allows registering hooks after the entity is defined,
   * which can be useful for avoiding circular type references.
   *
   * @example
   * ```ts
   * export const Article = defineEntity({
   *   name: 'Article',
   *   properties: { ... },
   * });
   *
   * Article.addHook('beforeCreate', async args => {
   *   args.entity.slug = args.entity.title.toLowerCase();
   * });
   * ```
   */
  addHook<T extends Entity = Entity>(
    event: EventType | `${EventType}`,
    handler: (args: EventArgs<T>) => void | Promise<void>,
  ): this;
}
export {};
