type Message = {
    __TAG: string;
};

type Metadata = Record<string, unknown>;

type Payload = Record<string, unknown>;

interface DomainEventData {
    eventId: string;
    occurredOn?: Date;
    payload?: Payload;
    metadata?: Metadata;
}
declare abstract class DomainEvent implements Message {
    abstract readonly __TAG: string;
    readonly eventId: string;
    readonly occurredOn: Date;
    readonly payload: Payload;
    readonly metadata: Metadata;
    constructor(eventData: DomainEventData);
}

declare abstract class ValueObject<ValueObjectData> {
    private readonly data;
    protected constructor(data: ValueObjectData);
    equals(other: ValueObject<ValueObjectData>): boolean;
    get<T extends keyof ValueObjectData>(key: T): ValueObjectData[T];
    private reflectiveEqual;
    private deepFreeze;
}

interface UUIDData {
    value: string;
    isNew: boolean;
}

declare class UUID extends ValueObject<UUIDData> {
    static readonly UUID_PATTERN: RegExp;
    static create(value: string, isNew?: boolean): UUID;
    static createFrom(value: string): UUID;
    equals(other: ValueObject<UUIDData>): boolean;
    static isValid(uuid: string): boolean;
    isNew(): boolean;
}

declare abstract class Entity<EntityData extends {
    id: UUID;
}> {
    private readonly data;
    protected constructor(entityData: EntityData);
    id(): string;
    get<Key extends keyof EntityData>(key: Key): EntityData[Key];
    protected set<Key extends keyof EntityData>(key: Key, value: EntityData[Key]): void;
    equals(other: Entity<EntityData>): boolean;
    isNew(): boolean;
    toObject(): Readonly<EntityData>;
}

interface IEventHandler<T extends DomainEvent> {
    readonly __TAG: string;
    handle(event: T): Promise<void>;
}

interface IEventMiddleware {
    execute(event: DomainEvent, next: (event: DomainEvent) => Promise<void>): Promise<void>;
}

interface EventBusPort {
    use(middleware: IEventMiddleware): void;
    on(eventType: string, handler: () => IEventHandler<DomainEvent>): void;
    dispatch(domainEvent: DomainEvent): Promise<void>;
    dispatchEvents(events: DomainEvent[]): Promise<void>;
    dispatchEventsAsync(events: DomainEvent[]): void;
}

declare abstract class Aggregate<EntityData extends {
    id: UUID;
}> extends Entity<EntityData> {
    private domainEvents;
    addEvent(domainEvent: DomainEvent): void;
    clearEvents(): void;
    dispatchEvents(bus: EventBusPort): Promise<void>;
    dispatchEventsAsync(bus: EventBusPort): void;
    getEvents(): DomainEvent[];
}

interface IMessageHandler<M extends Message, R = unknown> {
    __TAG: string;
    handle(message: M): Promise<R>;
}

interface Middleware<T> {
    execute: <R>(message: T, next: (message: T) => Promise<R>) => Promise<R>;
}

declare class Bus<M extends Message> {
    private handlers;
    private middlewares;
    register<R>(handlerFactory: () => IMessageHandler<M, R>): void;
    use(middleware: Middleware<M>): void;
    execute<R>(message: M): Promise<R>;
}

declare abstract class Command implements Message {
    abstract readonly __TAG: string;
}

declare abstract class CommandHandler<TCommand extends Command, TResponse = void> {
    abstract readonly __TAG: string;
    abstract handle(command: TCommand): Promise<TResponse>;
}

interface Logger {
    log: (message: string) => void;
}

declare class CommandLoggerMiddleware implements Middleware<Command> {
    private readonly logger;
    private readonly middlewareId;
    constructor(logger: Logger, middlewareId: string);
    execute<T>(command: Command, next: (command: Command) => Promise<T>): Promise<T>;
}

declare abstract class Query {
    abstract readonly __TAG: string;
}

declare class QueryLoggerMiddleware implements Middleware<Query> {
    private readonly logger;
    private readonly middlewareId;
    constructor(logger: Logger, middlewareId: string);
    execute<T>(query: Query, next: (query: Query) => Promise<T>): Promise<T>;
}

declare abstract class QueryHandler<TQuery extends Query, TResponse> {
    abstract readonly __TAG: string;
    abstract handle(query: TQuery): Promise<TResponse>;
}

declare abstract class CustomError extends Error {
    abstract readonly __TAG: string;
    protected constructor(message?: string);
    abstract isDomainError(): boolean;
    abstract isTechnicalError(): boolean;
}

declare abstract class DomainError extends CustomError {
    isDomainError(): boolean;
    isTechnicalError(): boolean;
}

declare abstract class TechnicalError extends CustomError {
    isDomainError(): boolean;
    isTechnicalError(): boolean;
}

declare class EventBus implements EventBusPort {
    private middlewares;
    private readonly handlers;
    use(middleware: IEventMiddleware): void;
    on(eventType: string, handler: () => IEventHandler<DomainEvent>): void;
    dispatch(domainEvent: DomainEvent): Promise<void>;
    dispatchEvents(events: DomainEvent[]): Promise<void>;
    dispatchEventsAsync(events: DomainEvent[]): void;
}

type Success<ValueType> = {
    _tag: 'success';
    value: ValueType;
};
type Failure<ErrorType extends Error> = {
    _tag: 'failure';
    error: ErrorType;
};
declare class Result<ValueType, ErrorType extends Error> {
    private readonly result;
    private constructor();
    static ok<ValueType, ErrorType extends Error>(value: ValueType): Result<ValueType, ErrorType>;
    static fail<ValueType, ErrorType extends Error>(error: ErrorType): Result<ValueType, ErrorType>;
    isOk(): this is Success<ValueType>;
    isFail(): this is Failure<ErrorType>;
    getValue(): ValueType;
    getError(): ErrorType;
}

interface IUseCase<Request> {
    execute(request?: Request): Promise<void>;
}

declare function Uuid(value: string, isNew?: boolean): UUID;
declare function UuidFrom(value: string): UUID;

export { Aggregate, Bus, Command, CommandHandler, CommandLoggerMiddleware, CustomError, DomainError, DomainEvent, Entity, EventBus, type EventBusPort, type IEventHandler, type IUseCase, type Logger, type Message, type Metadata, type Payload, Query, QueryHandler, QueryLoggerMiddleware, Result, TechnicalError, UUID, type UUIDData, Uuid, UuidFrom, ValueObject };
