/**
 * @module Utilities
 */
import { type IInvokableObject, type Invokable, type InvokableFn, type OneOrMore } from "../../../utilities/functions/_module.js";
import { type Promisable } from "../../../utilities/types/_module.js";
import type { HookContext } from "../../../utilities/classes/hooks/types.js";
/**
 *
 * IMPORT_PATH: `"@daiso-tech/core/utilities"`
 * @group Hooks
 */
export type GetSignal<TParameters extends unknown[] = unknown[]> = Invokable<[
    arguments_: TParameters
], AbortSignal | undefined>;
/**
 *
 * IMPORT_PATH: `"@daiso-tech/core/utilities"`
 * @group Hooks
 */
export type ForwardSignal<TParameters> = Invokable<[
    arguments_: TParameters,
    signal: AbortSignal
], void>;
/**
 * With {@link AbortSignalBinder | `AbortSignalBinder`}, you can bind an {@link AbortSignal | `AbortSignal`} to the middleware, enabling two-way abortion control.
 * This means the middleware can abort the function, or the function can abort the middleware if the input function supports an {@link AbortSignal | `AbortSignal`}.
 *
 * IMPORT_PATH: `"@daiso-tech/core/utilities"`
 * @group Hooks
 *
 * @example
 * ```ts
 * import { AsyncHooks } from "@daiso-tech/core/utilities";
 *
 * const abortController = new AbortController();
 *
 * // You can abort the function based on by your on criteria
 * abortController.abort("Aborted") // Remove this code if you want the function to aborted after 2 seconds by the middleware.
 *
 * const data = await new AsyncHooks(async (url: string, signal?: AbortSignal): Promise<unknown> => {
 *   const response = await fetch(url, { signal });
 *   return await response.json();
 * }, [
 *   (args, next, { abort, signal }) => {
 *     // We abort the function when it execdes 2 seconds.
 *     const id = setTimeout(() => abort("Timed out"), 2000);
 *     // We remove the timeout if function is aborted before it execdes 2 seconds.
 *     signal.addEventListener("abort", () => clearTimeout(id), { once: true });
 *     return next(...args);
 *   }
 * ], {
 *   signalBinder: {
 *     getSignal: (args) => args[1],
 *     forwardSignal: (args, signal) => {
 *       args[1] = signal;
 *     }
 *   }
 * })
 * .invoke("url", abortController.signal);
 *
 * console.log("DATA:", data)
 * ```
 */
export type AbortSignalBinder<TParameters extends unknown[] = unknown[]> = {
    getSignal: GetSignal<TParameters>;
    forwardSignal: ForwardSignal<TParameters>;
};
/**
 *
 * IMPORT_PATH: `"@daiso-tech/core/utilities"`
 * @group Hooks
 */
export type AsyncNextFunc<TParameters extends unknown[] = unknown[], TReturn = unknown> = InvokableFn<TParameters, PromiseLike<TReturn>>;
/**
 *
 * IMPORT_PATH: `"@daiso-tech/core/utilities"`
 * @group Hooks
 */
export type AsyncContext<TParameters extends unknown[] = unknown[], TContext extends HookContext = HookContext> = {
    name: string;
    signalBinder: AbortSignalBinder<TParameters>;
    context: TContext;
    abort: (error: unknown) => void;
    signal: AbortSignal;
};
/**
 *
 * IMPORT_PATH: `"@daiso-tech/core/utilities"`
 * @group Hooks
 */
export type AsyncMiddlewareFn<TParameters extends unknown[] = unknown[], TReturn = unknown, TContext extends HookContext = HookContext> = InvokableFn<[
    arguments_: TParameters,
    next: AsyncNextFunc<TParameters, TReturn>,
    settings: AsyncContext<TParameters, TContext>
], Promisable<TReturn>>;
/**
 *
 * IMPORT_PATH: `"@daiso-tech/core/utilities"`
 * @group Hooks
 */
export type IAsyncMiddlewareObject<TParameters extends unknown[] = unknown[], TReturn = unknown, TContext extends HookContext = HookContext> = IInvokableObject<[
    arguments_: TParameters,
    next: AsyncNextFunc<TParameters, TReturn>,
    settings: AsyncContext<TParameters, TContext>
], Promisable<TReturn>>;
/**
 *
 * IMPORT_PATH: `"@daiso-tech/core/utilities"`
 * @group Hooks
 */
export type AsyncMiddleware<TParameters extends unknown[] = unknown[], TReturn = unknown, TContext extends HookContext = HookContext> = IAsyncMiddlewareObject<TParameters, TReturn, TContext> | AsyncMiddlewareFn<TParameters, TReturn, TContext>;
/**
 *
 * IMPORT_PATH: `"@daiso-tech/core/utilities"`
 * @group Hooks
 */
export type AsyncHooksSettings<TParameters extends unknown[] = unknown[], TContext extends HookContext = HookContext> = {
    /**
     * The name of the function which can be used for logging inside the middleware.
     * By default, it takes the function or method name. If an anonymous function is provided, the name defaults to "func".
     */
    name?: string;
    /**
     * If the input function accepts an {@link AbortSignal | `AbortSignal`}, you can bind it to the middleware by providing an {@link AbortSignalBinder | `AbortSignalBinder`}.
     * This enables two-way abortion control: you can either abort the function from within the middleware, or abort the middleware from the function itself.
     */
    signalBinder?: AbortSignalBinder<TParameters>;
    /**
     * You can provide addtional context that can be used in the middleware.
     */
    context?: TContext;
};
/**
 * The `AsyncHooks` class provides a convenient way to change and inspect arguments and return value of both async and sync functions.
 * For example `AsyncHooks` class can be used to log function arguments and return values. Note this class will always return promise and is immutable.
 *
 * Middlewares apply left to right: each wraps the next, with the leftmost being the outermost layer and the rightmost wrapping the original function.
 *
 * IMPORT_PATH: `"@daiso-tech/core/utilities"`
 * @group Hooks
 */
export declare class AsyncHooks<TParameters extends unknown[] = unknown[], TReturn = unknown, TContext extends HookContext = HookContext> implements IInvokableObject<TParameters, Promise<TReturn>> {
    private readonly invokable;
    private readonly middlewares;
    private readonly settings;
    private static defaultAbortSignalBinder;
    private static resolveSignalBinder;
    private static init;
    private readonly func;
    /**
     * @example
     * ```ts
     * import { AsyncHooks, type AsyncMiddlewareFn } from "@daiso-tech/core/utilities";
     *
     * function log<TParameters extends unknown[], TReturn>(): AsyncMiddlewareFn<TParameters, TReturn> {
     *   return async (args, next, { name: funcName }) => {
     *     console.log("FUNCTION_NAME:", funcName);
     *     console.log("ARGUMENTS:", args);
     *     const value = await next(...args);
     *     console.log("RETURN:", value);
     *     return value;
     *   }
     * }
     *
     * function time<TParameters extends unknown[], TReturn>(): AsyncMiddlewareFn<TParameters, TReturn> {
     *   return async (args, next) => {
     *     const start = performance.now();
     *     const value = await next(...args);
     *     const end = performance.now();
     *     const time = end - start;
     *     console.log("TIME:", `${String(time)}ms`);
     *     return value;
     *   }
     * }
     *
     * function add(a: number, b: number): number {
     *   return a + b;
     * }
     *
     * const enhancedAdd = new AsyncHooks(add, [
     *   log(),
     *   time()
     * ], {
     *   // You can provide addtional data to be used the middleware.
     *   context: {},
     * });
     *
     * // Will log the function name, arguments and return value.
     * // Will also log the execution time.
     * const result = await enhancedAdd.invoke(1, 2);
     *
     * // Will be 3.
     * console.log(result);
     * ```
     */
    constructor(invokable: Invokable<TParameters, Promisable<TReturn>>, middlewares: NoInfer<OneOrMore<AsyncMiddleware<TParameters, TReturn, TContext>>>, settings?: AsyncHooksSettings<TParameters, TContext>);
    /**
     * The `pipe` method returns a new `AsyncHooks` instance with the additional `middlewares` applied.
     */
    pipe(middlewares: OneOrMore<AsyncMiddleware<TParameters, TReturn, TContext>>): AsyncHooks<TParameters, TReturn, TContext>;
    /**
     * The `pipeWhen` method conditionally applies additional `middlewares`, returning a new `AsyncHooks` instance only if the specified condition is met.
     */
    pipeWhen(condition: boolean, middlewares: OneOrMore<AsyncMiddleware<TParameters, TReturn, TContext>>): AsyncHooks<TParameters, TReturn, TContext>;
    /**
     * The `toFunc` will return the function with all middlewares applied.
     */
    toFunc(): InvokableFn<TParameters, PromiseLike<TReturn>>;
    /**
     * The `invoke` method executes the constructor's input function, applying all middlewares.
     */
    invoke(...arguments_: TParameters): Promise<TReturn>;
}
