import { Logger } from "./logger";
import { UserAgent } from "./util";
export interface InitializeHandlerArguments<Input extends object> {
    
    input: Input;
}
export interface InitializeHandlerOutput<Output extends object> extends DeserializeHandlerOutput<Output> {
    output: Output;
}
export interface SerializeHandlerArguments<Input extends object> extends InitializeHandlerArguments<Input> {
    
    request?: unknown;
}
export interface SerializeHandlerOutput<Output extends object> extends InitializeHandlerOutput<Output> {
}
export interface BuildHandlerArguments<Input extends object> extends FinalizeHandlerArguments<Input> {
}
export interface BuildHandlerOutput<Output extends object> extends InitializeHandlerOutput<Output> {
}
export interface FinalizeHandlerArguments<Input extends object> extends SerializeHandlerArguments<Input> {
    
    request: unknown;
}
export interface FinalizeHandlerOutput<Output extends object> extends InitializeHandlerOutput<Output> {
}
export interface DeserializeHandlerArguments<Input extends object> extends FinalizeHandlerArguments<Input> {
}
export interface DeserializeHandlerOutput<Output extends object> {
    
    response: unknown;
    output?: Output;
}
export interface InitializeHandler<Input extends object, Output extends object> {
    
    (args: InitializeHandlerArguments<Input>): Promise<InitializeHandlerOutput<Output>>;
}
export declare type Handler<Input extends object, Output extends object> = InitializeHandler<Input, Output>;
export interface SerializeHandler<Input extends object, Output extends object> {
    
    (args: SerializeHandlerArguments<Input>): Promise<SerializeHandlerOutput<Output>>;
}
export interface FinalizeHandler<Input extends object, Output extends object> {
    
    (args: FinalizeHandlerArguments<Input>): Promise<FinalizeHandlerOutput<Output>>;
}
export interface BuildHandler<Input extends object, Output extends object> {
    (args: BuildHandlerArguments<Input>): Promise<BuildHandlerOutput<Output>>;
}
export interface DeserializeHandler<Input extends object, Output extends object> {
    (args: DeserializeHandlerArguments<Input>): Promise<DeserializeHandlerOutput<Output>>;
}

export interface InitializeMiddleware<Input extends object, Output extends object> {
    
    (next: InitializeHandler<Input, Output>, context: HandlerExecutionContext): InitializeHandler<Input, Output>;
}

export interface SerializeMiddleware<Input extends object, Output extends object> {
    
    (next: SerializeHandler<Input, Output>, context: HandlerExecutionContext): SerializeHandler<Input, Output>;
}

export interface FinalizeRequestMiddleware<Input extends object, Output extends object> {
    
    (next: FinalizeHandler<Input, Output>, context: HandlerExecutionContext): FinalizeHandler<Input, Output>;
}
export interface BuildMiddleware<Input extends object, Output extends object> {
    (next: BuildHandler<Input, Output>, context: HandlerExecutionContext): BuildHandler<Input, Output>;
}
export interface DeserializeMiddleware<Input extends object, Output extends object> {
    (next: DeserializeHandler<Input, Output>, context: HandlerExecutionContext): DeserializeHandler<Input, Output>;
}
export declare type MiddlewareType<Input extends object, Output extends object> = InitializeMiddleware<Input, Output> | SerializeMiddleware<Input, Output> | BuildMiddleware<Input, Output> | FinalizeRequestMiddleware<Input, Output> | DeserializeMiddleware<Input, Output>;

export interface Terminalware {
    <Input extends object, Output extends object>(context: HandlerExecutionContext): DeserializeHandler<Input, Output>;
}
export declare type Step = "initialize" | "serialize" | "build" | "finalizeRequest" | "deserialize";
export declare type Priority = "high" | "normal" | "low";
export interface HandlerOptions {
    
    step?: Step;
    
    tags?: Array<string>;
    
    name?: string;
    
    override?: boolean;
}
export interface AbsoluteLocation {
    
    priority?: Priority;
}
export declare type Relation = "before" | "after";
export interface RelativeLocation {
    
    relation: Relation;
    
    toMiddleware: string;
}
export declare type RelativeMiddlewareOptions = RelativeLocation & Pick<HandlerOptions, Exclude<keyof HandlerOptions, "step">>;
export interface InitializeHandlerOptions extends HandlerOptions {
    step?: "initialize";
}
export interface SerializeHandlerOptions extends HandlerOptions {
    step: "serialize";
}
export interface BuildHandlerOptions extends HandlerOptions {
    step: "build";
}
export interface FinalizeRequestHandlerOptions extends HandlerOptions {
    step: "finalizeRequest";
}
export interface DeserializeHandlerOptions extends HandlerOptions {
    step: "deserialize";
}

export interface MiddlewareStack<Input extends object, Output extends object> extends Pluggable<Input, Output> {
    
    add(middleware: InitializeMiddleware<Input, Output>, options?: InitializeHandlerOptions & AbsoluteLocation): void;
    
    add(middleware: SerializeMiddleware<Input, Output>, options: SerializeHandlerOptions & AbsoluteLocation): void;
    
    add(middleware: BuildMiddleware<Input, Output>, options: BuildHandlerOptions & AbsoluteLocation): void;
    
    add(middleware: FinalizeRequestMiddleware<Input, Output>, options: FinalizeRequestHandlerOptions & AbsoluteLocation): void;
    
    add(middleware: DeserializeMiddleware<Input, Output>, options: DeserializeHandlerOptions & AbsoluteLocation): void;
    
    addRelativeTo(middleware: MiddlewareType<Input, Output>, options: RelativeMiddlewareOptions): void;
    
    use(pluggable: Pluggable<Input, Output>): void;
    
    clone(): MiddlewareStack<Input, Output>;
    
    remove(toRemove: MiddlewareType<Input, Output> | string): boolean;
    
    removeByTag(toRemove: string): boolean;
    
    concat<InputType extends Input, OutputType extends Output>(from: MiddlewareStack<InputType, OutputType>): MiddlewareStack<InputType, OutputType>;
    
    resolve<InputType extends Input, OutputType extends Output>(handler: DeserializeHandler<InputType, OutputType>, context: HandlerExecutionContext): InitializeHandler<InputType, OutputType>;
}

export interface HandlerExecutionContext {
    
    logger?: Logger;
    
    userAgent?: UserAgent;
    [key: string]: any;
}
export interface Pluggable<Input extends object, Output extends object> {
    
    applyToStack: (stack: MiddlewareStack<Input, Output>) => void;
}
