import { type AnyFunction } from '@augment-vir/core';
import { type IsAny } from 'type-fest';
import { type WaitUntilOptions } from '../guard-types/wait-until-function.js';
/**
 * A custom asserter for `.output` guards (`assert.output`, `check.output`, etc.). This is typically
 * not necessary, as the `.output` guards already perform deep equality checks by default.
 *
 * @category Assert : Util
 * @category Package : @augment-vir/assert
 * @example
 *
 * ```ts
 * import {assert, AssertionError, CustomOutputAsserter} from '@augment-vir/assert';
 *
 * function myFunctionToTest(name: string) {
 *     return `Hello there ${name}`;
 * }
 *
 * const myCustomAsserter: CustomOutputAsserter<typeof myFunctionToTest> = (
 *     actual,
 *     expected,
 *     failureMessage,
 * ) => {
 *     // Write your assertion in an `if`.
 *     if (!actual.startsWith('hello there') || actual.endsWith(expected)) {
 *         // Throw an `AssertionError` if the `if` fails.
 *         throw new AssertionError('', failureMessage);
 *     }
 * };
 * // Use your custom asserter as the first input to any `.output` guard.
 * assert.output(myCustomAsserter, myFunctionToTest, ['John'], 'John', 'Name insertion failed');
 * ```
 *
 * @param FunctionToCall The function type that your custom asserter will be run on.
 * @package [`@augment-vir/assert`](https://www.npmjs.com/package/@augment-vir/assert)
 */
export type CustomOutputAsserter<FunctionToCall extends AnyFunction> = (actual: Awaited<ReturnType<FunctionToCall>>, expected: Awaited<ReturnType<FunctionToCall>>, failureMessage?: string | undefined) => void;
type OutputReturn<FunctionToCall extends AnyFunction, Return> = Promise<any> extends ReturnType<NoInfer<FunctionToCall>> ? IsAny<ReturnType<FunctionToCall>> extends true ? Return : Promise<Return> : Return;
export type OutputAssertWithoutAsserter = <const FunctionToCall extends AnyFunction>(functionToCall: FunctionToCall, inputs: Parameters<NoInfer<FunctionToCall>>, expectedOutput: Awaited<ReturnType<NoInfer<FunctionToCall>>>, failureMessage?: string | undefined) => OutputReturn<NoInfer<FunctionToCall>, void>;
export type OutputAssertWithAsserter = <const FunctionToCall extends AnyFunction>(asserter: CustomOutputAsserter<NoInfer<FunctionToCall>>, functionToCall: FunctionToCall, inputs: Parameters<NoInfer<FunctionToCall>>, expectedOutput: Awaited<ReturnType<NoInfer<FunctionToCall>>>, failureMessage?: string | undefined) => OutputReturn<NoInfer<FunctionToCall>, void>;
declare function assertOutput<const FunctionToCall extends AnyFunction>(functionToCall: FunctionToCall, inputs: Parameters<NoInfer<FunctionToCall>>, expectedOutput: Awaited<ReturnType<NoInfer<FunctionToCall>>>, failureMessage?: string | undefined): OutputReturn<NoInfer<FunctionToCall>, void>;
declare function assertOutput<const FunctionToCall extends AnyFunction>(asserter: CustomOutputAsserter<NoInfer<FunctionToCall>>, functionToCall: FunctionToCall, inputs: Parameters<NoInfer<FunctionToCall>>, expectedOutput: Awaited<ReturnType<NoInfer<FunctionToCall>>>, failureMessage?: string | undefined): OutputReturn<NoInfer<FunctionToCall>, void>;
export type OutputCheckWithoutAsserter = <const FunctionToCall extends AnyFunction>(functionToCall: FunctionToCall, inputs: Parameters<NoInfer<FunctionToCall>>, expectedOutput: Awaited<ReturnType<NoInfer<FunctionToCall>>>, failureMessage?: string | undefined) => OutputReturn<NoInfer<FunctionToCall>, boolean>;
export type OutputCheckWithAsserter = <const FunctionToCall extends AnyFunction>(asserter: CustomOutputAsserter<NoInfer<FunctionToCall>>, functionToCall: FunctionToCall, inputs: Parameters<NoInfer<FunctionToCall>>, expectedOutput: Awaited<ReturnType<NoInfer<FunctionToCall>>>, failureMessage?: string | undefined) => OutputReturn<NoInfer<FunctionToCall>, boolean>;
declare function checkOutput<const FunctionToCall extends AnyFunction>(functionToCall: FunctionToCall, inputs: Parameters<NoInfer<FunctionToCall>>, expectedOutput: Awaited<ReturnType<NoInfer<FunctionToCall>>>, failureMessage?: string | undefined): OutputReturn<FunctionToCall, boolean>;
declare function checkOutput<const FunctionToCall extends AnyFunction>(asserter: CustomOutputAsserter<NoInfer<FunctionToCall>>, functionToCall: FunctionToCall, inputs: Parameters<NoInfer<FunctionToCall>>, expectedOutput: Awaited<ReturnType<NoInfer<FunctionToCall>>>, failureMessage?: string | undefined): OutputReturn<FunctionToCall, boolean>;
export type OutputAssertWrapWithoutAsserter = <const FunctionToCall extends AnyFunction>(functionToCall: FunctionToCall, inputs: Parameters<NoInfer<FunctionToCall>>, expectedOutput: Awaited<ReturnType<NoInfer<FunctionToCall>>>, failureMessage?: string | undefined) => OutputReturn<NoInfer<FunctionToCall>, Awaited<ReturnType<NoInfer<FunctionToCall>>>>;
export type OutputAssertWrapWithAsserter = <const FunctionToCall extends AnyFunction>(asserter: CustomOutputAsserter<NoInfer<FunctionToCall>>, functionToCall: FunctionToCall, inputs: Parameters<NoInfer<FunctionToCall>>, expectedOutput: Awaited<ReturnType<NoInfer<FunctionToCall>>>, failureMessage?: string | undefined) => OutputReturn<NoInfer<FunctionToCall>, Awaited<ReturnType<NoInfer<FunctionToCall>>>>;
declare function assertWrapOutput<const FunctionToCall extends AnyFunction>(functionToCall: FunctionToCall, inputs: Parameters<NoInfer<FunctionToCall>>, expectedOutput: Awaited<ReturnType<NoInfer<FunctionToCall>>>, failureMessage?: string | undefined): OutputReturn<FunctionToCall, Awaited<ReturnType<NoInfer<FunctionToCall>>>>;
declare function assertWrapOutput<const FunctionToCall extends AnyFunction>(asserter: CustomOutputAsserter<NoInfer<FunctionToCall>>, functionToCall: FunctionToCall, inputs: Parameters<NoInfer<FunctionToCall>>, expectedOutput: Awaited<ReturnType<NoInfer<FunctionToCall>>>, failureMessage?: string | undefined): OutputReturn<FunctionToCall, Awaited<ReturnType<NoInfer<FunctionToCall>>>>;
export type OutputCheckWrapWithoutAsserter = <const FunctionToCall extends AnyFunction>(functionToCall: FunctionToCall, inputs: Parameters<NoInfer<FunctionToCall>>, expectedOutput: Awaited<ReturnType<NoInfer<FunctionToCall>>>, failureMessage?: string | undefined) => OutputReturn<NoInfer<FunctionToCall>, Awaited<ReturnType<NoInfer<FunctionToCall>>> | undefined>;
export type OutputCheckWrapWithAsserter = <const FunctionToCall extends AnyFunction>(asserter: CustomOutputAsserter<NoInfer<FunctionToCall>>, functionToCall: FunctionToCall, inputs: Parameters<NoInfer<FunctionToCall>>, expectedOutput: Awaited<ReturnType<NoInfer<FunctionToCall>>>, failureMessage?: string | undefined) => OutputReturn<NoInfer<FunctionToCall>, Awaited<ReturnType<NoInfer<FunctionToCall>>> | undefined>;
declare function checkWrapOutput<const FunctionToCall extends AnyFunction>(functionToCall: FunctionToCall, inputs: Parameters<NoInfer<FunctionToCall>>, expectedOutput: Awaited<ReturnType<NoInfer<FunctionToCall>>>, failureMessage?: string | undefined): OutputReturn<FunctionToCall, Awaited<ReturnType<NoInfer<FunctionToCall>>> | undefined>;
declare function checkWrapOutput<const FunctionToCall extends AnyFunction>(asserter: CustomOutputAsserter<NoInfer<FunctionToCall>>, functionToCall: FunctionToCall, inputs: Parameters<NoInfer<FunctionToCall>>, expectedOutput: Awaited<ReturnType<NoInfer<FunctionToCall>>>, failureMessage?: string | undefined): OutputReturn<FunctionToCall, Awaited<ReturnType<NoInfer<FunctionToCall>>> | undefined>;
export type OutputWaitUntilWithoutAsserter = <const FunctionToCall extends AnyFunction>(functionToCall: FunctionToCall, inputs: Parameters<NoInfer<FunctionToCall>>, expectedOutput: Awaited<ReturnType<NoInfer<FunctionToCall>>>, options?: WaitUntilOptions | undefined, failureMessage?: string | undefined) => Promise<Awaited<ReturnType<NoInfer<FunctionToCall>>>>;
export type OutputWaitUntilWithAsserter = <const FunctionToCall extends AnyFunction>(asserter: CustomOutputAsserter<NoInfer<FunctionToCall>>, functionToCall: FunctionToCall, inputs: Parameters<NoInfer<FunctionToCall>>, expectedOutput: Awaited<ReturnType<NoInfer<FunctionToCall>>>, options?: WaitUntilOptions | undefined, failureMessage?: string | undefined) => Promise<Awaited<ReturnType<NoInfer<FunctionToCall>>>>;
export declare function waitUntilOutput<const FunctionToCall extends AnyFunction>(functionToCall: FunctionToCall, inputs: Parameters<NoInfer<FunctionToCall>>, expectedOutput: Awaited<ReturnType<NoInfer<FunctionToCall>>>, options?: WaitUntilOptions | undefined, failureMessage?: string | undefined): Promise<Awaited<ReturnType<NoInfer<FunctionToCall>>>>;
export declare function waitUntilOutput<const FunctionToCall extends AnyFunction>(asserter: CustomOutputAsserter<NoInfer<FunctionToCall>>, functionToCall: FunctionToCall, inputs: Parameters<NoInfer<FunctionToCall>>, expectedOutput: Awaited<ReturnType<NoInfer<FunctionToCall>>>, options?: WaitUntilOptions | undefined, failureMessage?: string | undefined): Promise<Awaited<ReturnType<NoInfer<FunctionToCall>>>>;
export declare const outputGuards: {
    assert: {
        /**
         * Asserts that the output of the given function deeply equals expectations. A custom asserter
         * can optionally be provided as the first argument to change the expectation checking from
         * "deeply equals" to whatever you want.
         *
         * Performs no type guarding.
         *
         * ```ts
         * import {assert} from '@augment-vir/assert';
         *
         * assert.output((input: number) => String(input), [5], '5'); // passes
         * assert.output((input: number) => String(input), [10], '5'); // fails
         *
         * assert.output(assert.isLengthAtLeast, (input: number) => String(input), [5], 2); // fails
         * assert.output(assert.isLengthAtLeast, (input: number) => String(input), [10], 2); // passes
         * ```
         *
         * @throws {@link AssertionError} If the assertion fails.
         */
        output: typeof assertOutput;
    };
    check: {
        /**
         * Checks that the output of the given function deeply equals expectations. A custom
         * asserter can optionally be provided as the first argument to change the expectation
         * checking from the default "deeply equals" to whatever you want.
         *
         * Performs no type guarding.
         *
         * ```ts
         * import {check} from '@augment-vir/assert';
         *
         * check.output((input: number) => String(input), [5], '5'); // returns `true`
         * check.output((input: number) => String(input), [10], '5'); // returns `false`
         *
         * check.output(assert.isLengthAtLeast, (input: number) => String(input), [5], 2); // returns `false`
         * check.output(assert.isLengthAtLeast, (input: number) => String(input), [10], 2); // returns `true`
         * ```
         */
        output: typeof checkOutput;
    };
    assertWrap: {
        /**
         * Asserts that the output of the given function deeply equals expectations. A custom
         * asserter can optionally be provided as the first argument to change the expectation
         * checking from the default "deeply equals" to whatever you want. Returns the output if the
         * assertion passes.
         *
         * Performs no type guarding.
         *
         * ```ts
         * import {assertWrap} from '@augment-vir/assert';
         *
         * assertWrap.output((input: number) => String(input), [5], '5'); // returns `'5'`
         * assertWrap.output((input: number) => String(input), [10], '5'); // throws an error
         *
         * assertWrap.output(assert.isLengthAtLeast, (input: number) => String(input), [5], 2); // throws an error
         * assertWrap.output(assert.isLengthAtLeast, (input: number) => String(input), [10], 2); // returns `10`
         * ```
         *
         * @returns The output if the assertion passes.
         * @throws {@link AssertionError} If the assertion fails.
         */
        output: typeof assertWrapOutput;
    };
    checkWrap: {
        /**
         * Asserts that the output of the given function deeply equals expectations. A custom
         * asserter can optionally be provided as the first argument to change the expectation
         * checking from the default "deeply equals" to whatever you want. Returns the output if the
         * check passes, otherwise `undefined`.
         *
         * Performs no type guarding.
         *
         * ```ts
         * import {checkWrap} from '@augment-vir/assert';
         *
         * checkWrap.output((input: number) => String(input), [5], '5'); // returns `'5'`
         * checkWrap.output((input: number) => String(input), [10], '5'); // returns `undefined`
         *
         * checkWrap.output(assert.isLengthAtLeast, (input: number) => String(input), [5], 2); // returns `undefined`
         * checkWrap.output(assert.isLengthAtLeast, (input: number) => String(input), [10], 2); // returns `10`
         * ```
         *
         * @returns The output if the assertion passes, otherwise `undefined`.
         */
        output: typeof checkWrapOutput;
    };
    waitUntil: {
        /**
         * Repeatedly calls a callback until its output deeply equals expectations. A custom
         * asserter can optionally be provided as the first argument to change the expectation
         * checking from the default "deeply equals" to whatever you want.
         *
         * Performs no type guarding.
         *
         * ```ts
         * import {waitUntil} from '@augment-vir/assert';
         *
         * await waitUntil.output((input: number) => String(input), [5], '5'); // returns `'5'`
         * await waitUntil.output((input: number) => String(input), [10], '5'); // throws an error
         *
         * await waitUntil.output(
         *     assert.isLengthAtLeast,
         *     (input: number) => String(input),
         *     [5],
         *     2,
         * ); // throws an error
         * await waitUntil.output(
         *     assert.isLengthAtLeast,
         *     (input: number) => String(input),
         *     [10],
         *     2,
         * ); // returns `10`
         * ```
         *
         * @throws {@link AssertionError} If the assertion fails.
         */
        output: typeof waitUntilOutput;
    };
};
export {};
