import type { StandardSchemaV1 } from "@standard-schema/spec";
import { type EventPublishOptions } from "../events/index.js";
/**
 * Any Standard Schema compatible validator.
 */
export type StandardSchema = StandardSchemaV1<unknown, unknown>;
/**
 * Infer the parsed output type from a Standard Schema.
 */
export type InferOutput<T extends StandardSchemaV1> = StandardSchemaV1.InferOutput<T>;
/**
 * Infer the input type accepted by a Standard Schema.
 */
export type InferInput<T extends StandardSchemaV1> = StandardSchemaV1.InferInput<T>;
type SchemaOutput<T> = T extends StandardSchemaV1 ? InferOutput<T> : never;
/**
 * Boundary phase that failed use-case schema validation.
 */
export type UseCaseValidationPhase = "input" | "output";
/**
 * Error thrown when a use case input or output fails schema validation.
 */
export declare class UseCaseValidationError extends Error {
    readonly name = "UseCaseValidationError";
    readonly useCaseName: string;
    readonly phase: UseCaseValidationPhase;
    readonly issues: readonly StandardSchemaV1.Issue[];
    constructor(args: {
        useCaseName: string;
        phase: UseCaseValidationPhase;
        issues: readonly StandardSchemaV1.Issue[];
    });
}
/**
 * Error thrown when a use case tries to emit an event it did not declare with
 * `.emits(...)`.
 */
export declare class UseCaseEventDeclarationError extends Error {
    readonly name = "UseCaseEventDeclarationError";
    readonly useCaseName: string;
    readonly eventName: string;
    readonly declaredEventNames: readonly string[];
    constructor(args: {
        useCaseName: string;
        eventName: string;
        declaredEventNames: readonly string[];
    });
}
/**
 * Error thrown when a use-case event helper fails to validate an event payload
 * before recording or publishing it.
 */
export declare class UseCaseEventValidationError extends Error {
    readonly name = "UseCaseEventValidationError";
    readonly useCaseName: string;
    readonly eventName: string;
    readonly issues: readonly StandardSchemaV1.Issue[];
    constructor(args: {
        useCaseName: string;
        eventName: string;
        issues: readonly StandardSchemaV1.Issue[];
    });
}
/**
 * Minimal domain event definition accepted by use-case event helpers.
 *
 * This structurally matches events from `@beignet/core/events` and compatible
 * app-owned definitions.
 */
export interface DomainEventLike {
    /**
     * Stable event name.
     */
    name: string;
    /**
     * Standard Schema payload validator.
     */
    payload: StandardSchema;
}
/**
 * Infer the output payload type from a use-case event definition.
 */
export type InferUseCaseEventPayload<E extends DomainEventLike> = E["payload"] extends StandardSchemaV1<unknown, infer Output> ? Output : never;
/**
 * Minimal recorder shape accepted by use-case event helpers.
 */
export interface UseCaseEventRecorderTarget {
    /**
     * Record a domain event payload.
     */
    record<E extends DomainEventLike>(event: E, payload: InferUseCaseEventPayload<E>, options?: EventPublishOptions): Promise<void> | void;
}
/**
 * Minimal event-bus shape accepted by use-case event helpers.
 */
export interface UseCaseEventBusTarget {
    /**
     * Publish a domain event payload.
     */
    publish<E extends DomainEventLike>(event: E, payload: InferUseCaseEventPayload<E>, options?: EventPublishOptions): Promise<void> | void;
}
/**
 * Event helper scoped to the events declared by a use case.
 */
export interface UseCaseEventHelpers<Emits extends readonly DomainEventLike[]> {
    /**
     * The exact event definitions declared with `.emits(...)`.
     */
    readonly declared: Emits;
    /**
     * Return whether an event is declared by this use case.
     */
    isDeclared(event: DomainEventLike): boolean;
    /**
     * Throw if an event is not declared by this use case.
     */
    assertDeclared(event: DomainEventLike): void;
    /**
     * Validate and record a declared event into a transaction-scoped recorder.
     */
    record<E extends Emits[number]>(recorder: UseCaseEventRecorderTarget, event: E, payload: InferUseCaseEventPayload<E>): Promise<void>;
    /**
     * Validate and publish a declared event directly through an event bus.
     */
    publish<E extends Emits[number]>(eventBus: UseCaseEventBusTarget, event: E, payload: InferUseCaseEventPayload<E>): Promise<void>;
}
/**
 * Use case kind - distinguishes commands (write/side-effect) from queries (read-only)
 */
export type UseCaseKind = "command" | "query";
/**
 * Symbol key for the trusted run path attached to finalized use cases.
 *
 * The server route binder calls this method instead of `run` when the route's
 * input was already validated by the exact same schema object at the HTTP
 * boundary. It behaves like `run` but skips the input parse; output
 * validation, instrumentation, events, and `onRun` are unchanged.
 *
 * The key uses `Symbol.for(...)` so the binder and the application builder
 * agree on the key even across separately bundled copies of the package.
 */
export declare const USE_CASE_TRUSTED_RUN: unique symbol;
/**
 * Finalized use case definition.
 *
 * Use cases validate their input before `run(...)` executes and validate their
 * output before returning, unless validation is disabled on the builder.
 */
export interface UseCaseDef<Ctx, Name extends string, Kind extends UseCaseKind, InputSchema extends StandardSchemaV1, OutputSchema extends StandardSchemaV1, Emits extends readonly DomainEventLike[] = readonly []> {
    /**
     * Stable use-case name, usually namespaced by feature.
     */
    name: Name;
    /**
     * Whether this use case is a command or query.
     */
    kind: Kind;
    /** Input schema, suitable for reuse in HTTP contracts and forms. */
    inputSchema: InputSchema;
    /** Output schema, suitable for reuse in HTTP contracts and clients. */
    outputSchema: OutputSchema;
    /**
     * Domain events this use case is allowed to record or publish through the
     * scoped `events` helper.
     */
    emits: Emits;
    /**
     * Execute the use case with application context and typed input.
     */
    run: (args: {
        ctx: Ctx;
        input: InferInput<InputSchema>;
    }) => Promise<InferOutput<OutputSchema>>;
}
/**
 * Event passed to the `onRun` hook for instrumentation.
 */
export interface UseCaseRunEvent<Ctx> {
    /**
     * Use-case name.
     */
    name: string;
    /**
     * Use-case kind.
     */
    kind: UseCaseKind;
    /**
     * Execution phase being observed.
     */
    phase: "start" | "end" | "error";
    /**
     * Elapsed time for end/error events.
     */
    durationMs?: number;
    /**
     * Error captured for error events.
     */
    error?: unknown;
    /**
     * Application context used for the run.
     */
    ctx: Ctx;
}
/**
 * Options for `createUseCase(...)`.
 */
export interface CreateUseCaseOptions<Ctx> {
    /**
     * Optional app-owned observer called on use case start, end, and error.
     *
     * Observers run in addition to the built-in instrumentation.
     */
    onRun?: (event: UseCaseRunEvent<Ctx>) => void | Promise<void>;
    /**
     * Built-in use-case instrumentation.
     *
     * By default every run records `usecase` lifecycle events (plus `error`
     * events for failed runs) into the provider instrumentation port resolved
     * from `ctx.ports` (`ports.instrumentation`, then `ports.devtools`). When no
     * port is installed, runs stay silent. Pass `false` to opt out.
     *
     * @default true
     */
    instrumentation?: boolean;
    /**
     * Enable or disable schema validation for use case boundaries.
     *
     * Defaults to validating both input and output. Pass `false` to opt out, or
     * configure phases independently with `{ input: boolean, output: boolean }`.
     */
    validate?: boolean | {
        input?: boolean;
        output?: boolean;
    };
}
type ValidationOptions = {
    input: boolean;
    output: boolean;
};
/**
 * Internal configuration for the use case builder
 */
interface UseCaseBuilderConfig<Name extends string, Kind extends UseCaseKind, InputSchema extends StandardSchemaV1 | undefined, OutputSchema extends StandardSchemaV1 | undefined, Emits extends readonly DomainEventLike[]> {
    name: Name;
    kind: Kind;
    input?: InputSchema;
    output?: OutputSchema;
    emits: Emits;
}
/**
 * Fluent builder for creating use cases
 */
declare class UseCaseBuilder<Ctx, Name extends string, Kind extends UseCaseKind, InputSchema extends StandardSchemaV1 | undefined, OutputSchema extends StandardSchemaV1 | undefined, Emits extends readonly DomainEventLike[] = readonly []> {
    private readonly config;
    private readonly onRun?;
    private readonly validation;
    private readonly instrumented;
    constructor(config: UseCaseBuilderConfig<Name, Kind, InputSchema, OutputSchema, Emits>, onRun?: ((event: UseCaseRunEvent<Ctx>) => void | Promise<void>) | undefined, validation?: ValidationOptions, instrumented?: boolean);
    /**
     * Define the input schema for this use case
     */
    input<I extends StandardSchemaV1>(schema: I): UseCaseBuilder<Ctx, Name, Kind, I, OutputSchema, Emits>;
    /**
     * Define the output schema for this use case
     */
    output<O extends StandardSchemaV1>(schema: O): UseCaseBuilder<Ctx, Name, Kind, InputSchema, O, Emits>;
    /**
     * Define the domain events that this use case may emit.
     */
    emits<E extends readonly DomainEventLike[]>(events: E): UseCaseBuilder<Ctx, Name, Kind, InputSchema, OutputSchema, E>;
    /**
     * Define the run function and finalize the use case definition
     */
    run(fn: InputSchema extends StandardSchemaV1 ? OutputSchema extends StandardSchemaV1 ? (args: {
        ctx: Ctx;
        input: SchemaOutput<InputSchema>;
        events: UseCaseEventHelpers<Emits>;
    }) => Promise<SchemaOutput<OutputSchema>> | SchemaOutput<OutputSchema> : never : never): InputSchema extends StandardSchemaV1 ? OutputSchema extends StandardSchemaV1 ? UseCaseDef<Ctx, Name, Kind, InputSchema, OutputSchema, Emits> : never : never;
}
/**
 * Root builder returned by createUseCase.
 */
export interface UseCaseBuilderRoot<Ctx> {
    /**
     * Create a command use case (write/side-effect path)
     */
    command<Name extends string>(name: Name): UseCaseBuilder<Ctx, Name, "command", undefined, undefined, readonly []>;
    /**
     * Create a query use case (read-only path)
     */
    query<Name extends string>(name: Name): UseCaseBuilder<Ctx, Name, "query", undefined, undefined, readonly []>;
}
/**
 * Infer the application context type from a finalized use case.
 */
export type UseCaseContext<TUseCase> = TUseCase extends {
    run: (args: {
        ctx: infer Ctx;
        input: infer _Input;
    }) => Promise<infer _Output>;
} ? Ctx : never;
/**
 * Infer the public input type accepted by a finalized use case.
 */
export type UseCaseInput<TUseCase> = TUseCase extends {
    run: (args: {
        ctx: infer _Ctx;
        input: infer Input;
    }) => Promise<infer _Output>;
} ? Input : never;
/**
 * Infer the public output type returned by a finalized use case.
 */
export type UseCaseOutput<TUseCase> = TUseCase extends {
    run: (args: {
        ctx: infer _Ctx;
        input: infer _Input;
    }) => Promise<infer Output>;
} ? Output : never;
type MaybePromise<T> = T | Promise<T>;
/**
 * Small test harness for running use cases with typed inputs.
 */
export interface UseCaseTester<Ctx> {
    /**
     * Create a fresh test context.
     */
    ctx(): Promise<Ctx>;
    /**
     * Run a use case with a typed input and either a fresh or explicit context.
     */
    run<Input, Output>(useCase: {
        run(args: {
            ctx: Ctx;
            input: Input;
        }): Promise<Output>;
    }, input: Input, options?: {
        ctx?: Ctx;
    }): Promise<Output>;
}
/**
 * Create a small test harness for use cases.
 *
 * Pass a context factory when tests mutate ports or state. Pass a fixed context
 * for simple, immutable tests.
 */
export declare function createUseCaseTester<Ctx>(createContext: Ctx | (() => MaybePromise<Ctx>)): UseCaseTester<Ctx>;
/**
 * Create a use case builder with a specific context type.
 *
 * Create this once in app code, usually in `lib/use-case.ts`, then import that
 * configured builder from feature use-case modules.
 *
 * @example
 * ```ts
 * export const useCase = createUseCase<AppContext>();
 *
 * export const createTodo = useCase
 *   .command("todos.create")
 *   .input(CreateTodoInput)
 *   .output(CreateTodoOutput)
 *   .run(async ({ ctx, input }) => ctx.ports.todos.create(input));
 * ```
 *
 * @param options - Optional instrumentation and validation configuration.
 * @returns A root builder for command and query use cases.
 */
export declare function createUseCase<Ctx>(options?: CreateUseCaseOptions<Ctx>): UseCaseBuilderRoot<Ctx>;
export {};
//# sourceMappingURL=index.d.ts.map