import { IValidation } from "./IValidation";
import { TypeGuardError } from "./TypeGuardError";

/* ===========================================================
  FUNCTIONAL
    - ASSERT
    - IS
    - VALIDATE
==============================================================
  ASSERT
----------------------------------------------------------- */
/**
 * Asserts a function.
 *
 * Asserts a function, by wrapping the function and checking its parameters and
 * return value through {@link assert} function. If some parameter or return value
 * does not match the expected type, it throws an {@link TypeGuardError} or a custom
 * error generated by the *errorFactory* parameter.
 *
 * For reference, {@link TypeGuardError.path} would be a little bit different with
 * individual {@link assert} function. If the {@link TypeGuardError} occurs from
 * some parameter, the path would start from `$input.parameters[number]`. Otherwise
 * the path would start from `$input.return`.
 *
 * - `$input.parameters[0].~`
 * - `$input.return.~`
 *
 * By the way, if what you want is not just finding the 1st type error through
 * assertion, but also finding every type errors, then use {@link validateFunction}
 * instead. Otherwise, what you want is just asserting parameters or return value
 * only, you can use {@link assertParameters} or {@link assertReturn} instead.
 *
 * On the other hand, if don't want to allow any superfluous properties, utilize
 * {@link assertEqualsFunction} or {@link validateEqualsFunction} instead.
 *
 * @template T Target function type
 * @param func Target function to assert
 * @param errorFactory Custom error factory. Default is `TypeGuardError`
 * @returns The wrapper function with type assertions
 * @throws A {@link TypeGuardError} or a custom error generated by *errorFactory*
 *
 * @author Jeongho Nam - https://github.com/samchon
 */
export function assertFunction<T extends (...args: any[]) => any>(
  func: T,
  errorFactory?: undefined | ((props: TypeGuardError.IProps) => Error),
): T;

/**
 * @internal
 */
export function assertFunction(): never {
  halt("assertFunction");
}

/**
 * Asserts parameters.
 *
 * Asserts a function, by wrapping the function and checking its parameters through
 * {@link assert} function. If some parameter does not match the expected type, it
 * throws an {@link TypeGuardError} or a custom error generated by the *errorFactory*
 * parameter.
 *
 * For reference, {@link TypeGuardError.path} would be a little bit different with
 * individual {@link assert} function. If the {@link TypeGuardError} occurs from
 * some parameter, the path would start from `$input.parameters[number]`.
 *
 * By the way, if what you want is not just finding the 1st type error through
 * assertion, but also finding every type errors, then use {@link validateParameters}
 * instead. Otherwise, what you want is not only asserting parameters, but also
 * asserting return value, you can use {@link assertFunction} instead.
 *
 * On the other hand, if don't want to allow any superfluous properties, utilize
 * {@link assertEqualsParameters} or {@link validateEqualsParameters} instead.
 *
 * @template T Target function type
 * @param func Target function to assert
 * @param errorFactory Custom error factory. Default is `TypeGuardError`
 * @returns The wrapper function with type assertions
 * @throws A {@link TypeGuardError} or a custom error generated by *errorFactory*
 *
 * @author Jeongho Nam - https://github.com/samchon
 */
export function assertParameters<T extends (...args: any[]) => any>(
  func: T,
  errorFactory?: undefined | ((props: TypeGuardError.IProps) => Error),
): T;

/**
 * @internal
 */
export function assertParameters(): never {
  halt("assertParameters");
}

/**
 * Asserts return value.
 *
 * Asserts a function, by wrapping the function and checking its return value through
 * {@link assert} function. If the return value does not match the expected type, it
 * throws an {@link TypeGuardError} or a custom error generated by the *errorFactory*
 * parameter.
 *
 * For reference, {@link TypeGuardError.path} would be a little bit different with
 * individual {@link assert} function. If the {@link TypeGuardError} occurs from
 * the return value, the path would start from `$input.return`.
 *
 * By the way, if what you want is not just finding the 1st type error through
 * assertion, but also finding every type errors, then use {@link validateReturn}
 * instead. Otherwise, what you want is not only asserting return value, but also
 * asserting parameters, you can use {@link assertFunction} instead.
 *
 * On the other hand, if don't want to allow any superfluous properties, utilize
 * {@link assertEqualsReturn} or {@link validateEqualsReturn} instead.
 *
 * @template T Target function type
 * @param func Target function to assert
 * @param errorFactory Custom error factory. Default is `TypeGuardError`
 * @returns The wrapper function with type assertions
 * @throws A {@link TypeGuardError} or a custom error generated by *errorFactory*
 *
 * @author Jeongho Nam - https://github.com/samchon
 */
export function assertReturn<T extends (...args: any[]) => any>(
  func: T,
  errorFactory?: undefined | ((props: TypeGuardError.IProps) => Error),
): T;

/**
 * @internal
 */
export function assertReturn(): never {
  halt("assertReturn");
}

/**
 * Asserts a function with strict equality.
 *
 * Asserts a function with strict equality, by wrapping the function and checking
 * its parameters and return value through {@link assertEquals} function. If some
 * parameter or return value does not match the expected type, it throws an
 * {@link TypeGuardError} or a custom error generated by the *errorFactory* parameter.
 *
 * For reference, {@link TypeGuardError.path} would be a little bit different with
 * individual {@link assertEquals} function. If the {@link TypeGuardError} occurs from
 * some parameter, the path would start from `$input.parameters[number]`. Otherwise
 * the path would start from `$input.return`.
 *
 * - `$input.parameters[0].~`
 * - `$input.return.~`
 *
 * By the way, if what you want is not just finding the 1st type error through
 * assertion, but also finding every type errors, then use
 * {@link validateEqualsFunction} instead. Otherwise, what you want is just asserting
 * parameters or return value only, you can use {@link assertEqualsParameters} or
 * {@link assertEqualsReturn} instead.
 *
 * On the other hand, if you want to allow any superfluous properties, utilize
 * {@link assertFunction} or {@link validateFunction} instead.
 *
 * @template T Target function type
 * @param func Target function to assert
 * @param errorFactory Custom error factory. Default is `TypeGuardError`
 * @returns The wrapper function with type assertions
 * @throws A {@link TypeGuardError} or a custom error generated by *errorFactory*
 *
 * @author Jeongho Nam - https://github.com/samchon
 */
export function assertEqualsFunction<T extends (...args: any[]) => any>(
  func: T,
  errorFactory?: undefined | ((props: TypeGuardError.IProps) => Error),
): T;

/**
 * @internal
 */
export function assertEqualsFunction(): never {
  halt("assertEqualsFunction");
}

/**
 * Asserts parameters with strict equality.
 *
 * Asserts a function, by wrapping the function and checking its parameters through
 * {@link assertEquals} function. If some parameter does not match the expected type,
 * it throws an {@link TypeGuardError} or a custom error generated by the *errorFactory*
 * parameter.
 *
 * For reference, {@link TypeGuardError.path} would be a little bit different with
 * individual {@link assertEquals} function. If the {@link TypeGuardError} occurs from
 * some parameter, the path would start from `$input.parameters[number]`.
 *
 * By the way, if what you want is not just finding the 1st type error through
 * assertion, but also finding every type errors, then use
 * {@link validateEqualsParameters} instead. Otherwise, what you want is not only
 * asserting parameters, but also asserting return value, you can use
 * {@link assertEqualsFunction} instead.
 *
 * On the other hand, if you want to allow any superfluous properties, utilize
 * {@link assertParameters} or {@link validateParameters} instead.
 *
 * @template T Target function type
 * @param func Target function to assert
 * @param errorFactory Custom error factory. Default is `TypeGuardError`
 * @returns The wrapper function with type assertions
 * @throws A {@link TypeGuardError} or a custom error generated by *errorFactory*
 *
 * @author Jeongho Nam - https://github.com/samchon
 */
export function assertEqualsParameters<T extends (...args: any[]) => any>(
  func: T,
  errorFactory?: undefined | ((props: TypeGuardError.IProps) => Error),
): T;

/**
 * @internal
 */
export function assertEqualsParameters(): never {
  halt("assertEqualsParameters");
}

/**
 * Asserts return value with strict equality.
 *
 * Asserts a function, by wrapping the function and checking its return value through
 * {@link assertEquals} function. If the return value does not match the expected type,
 * it throws an {@link TypeGuardError} or a custom error generated by the *errorFactory*
 * parameter.
 *
 * For reference, {@link TypeGuardError.path} would be a little bit different with
 * individual {@link assertEquals} function. If the {@link TypeGuardError} occurs from
 * the return value, the path would start from `$input.return`.
 *
 * By the way, if what you want is not just finding the 1st type error through
 * assertion, but also finding every type errors, then use {@link validateEqualsReturn}
 * instead. Otherwise, what you want is not only asserting return value, but also
 * asserting parameters, you can use {@link assertEqualsFunction} instead.
 *
 * On the other hand, if you want to allow any superfluous properties, utilize
 * {@link assertReturn} or {@link validateReturn} instead.
 *
 * @template T Target function type
 * @param func Target function to assert
 * @param errorFactory Custom error factory. Default is `TypeGuardError`
 * @returns The wrapper function with type assertions
 * @throws A {@link TypeGuardError} or a custom error generated by *errorFactory*
 *
 * @author Jeongho Nam - https://github.com/samchon
 */
export function assertEqualsReturn<T extends (...args: any[]) => any>(
  func: T,
  errorFactory?: undefined | ((props: TypeGuardError.IProps) => Error),
): T;

/**
 * @internal
 */
export function assertEqualsReturn(): never {
  halt("assertEqualsReturn");
}

/* -----------------------------------------------------------
  IS
----------------------------------------------------------- */
/**
 * Tests a function.
 *
 * Tests a function, by wrapping the function and checking its parameters and
 * return value through {@link is} function. If some parameter or return value
 * does not match the expected type, it returns `null`. Otherwise there's no
 * type error, it returns the result of the function.
 *
 * By the way, if you want is not just testing type checking, but also finding
 * detailed type error reason(s), then use {@link assertFunction} or
 * {@link validateFunction} instead.
 *
 * On the other hand, if you don't want to allow any superfluous properties,
 * utilize {@link equalsFunction}, {@link assertEqualsFunction} or
 * {@link validateEqualsFunction} instead.
 *
 * @template T Target function type
 * @param func Target function to test
 * @returns The wrapper function with type tests
 *
 * @author Jeongho Nam - https://github.com/samchon
 */
export function isFunction<T extends (...args: any[]) => any>(
  func: T,
): T extends (...args: infer Arguments) => infer Output
  ? Output extends Promise<infer R>
    ? (...args: Arguments) => Promise<R | null>
    : (...args: Arguments) => Output | null
  : never;

/**
 * @internal
 */
export function isFunction(): never {
  halt("isFunction");
}

/**
 * Tests parameters.
 *
 * Tests a function, by wrapping the function and checking its parameters through
 * {@link is} function. If some parameter does not match the expected type, it
 * returns `null`. Otherwise there's no type error, it returns the result of the
 * function.
 *
 * By the way, if you want is not just testing type checking, but also finding
 * detailed type error reason(s), then use {@link assertParameters} or
 * {@link validateParameters} instead.
 *
 * On the other hand, if you don't want to allow any superfluous properties,
 * utilize {@link equalsParameters}, {@link assertEqualsParameters} or
 * {@link validateEqualsParameters} instead.
 *
 * @template T Target function type
 * @param func Target function to test
 * @returns The wrapper function with type tests
 *
 * @author Jeongho Nam - https://github.com/samchon
 */
export function isParameters<T extends (...args: any[]) => any>(
  func: T,
): T extends (...args: infer Arguments) => infer Output
  ? Output extends Promise<infer R>
    ? (...args: Arguments) => Promise<R | null>
    : (...args: Arguments) => Output | null
  : never;

/**
 * @internal
 */
export function isParameters(): never {
  halt("isParameters");
}

/**
 * Tests return value.
 *
 * Tests a function, by wrapping the function and checking its return value through
 * {@link is} function. If the return value does not match the expected type, it
 * returns `null`. Otherwise there's no type error, it returns the result of the
 * function.
 *
 * By the way, if you want is not just testing type checking, but also finding
 * detailed type error reason(s), then use {@link assertReturn} or
 * {@link validateReturn} instead.
 *
 * On the other hand, if you don't want to allow any superfluous properties,
 * utilize {@link equalsReturn}, {@link assertEqualsReturn} or
 * {@link validateEqualsReturn} instead.
 *
 * @template T Target function type
 * @param func Target function to test
 * @returns The wrapper function with type tests
 *
 * @author Jeongho Nam - https://github.com/samchon
 */
export function isReturn<T extends (...args: any[]) => any>(
  func: T,
): T extends (...args: infer Arguments) => infer Output
  ? Output extends Promise<infer R>
    ? (...args: Arguments) => Promise<R | null>
    : (...args: Arguments) => Output | null
  : never;

/**
 * @internal
 */
export function isReturn(): never {
  halt("isReturn");
}

/**
 * Tests a function with strict equality.
 *
 * Tests a function with strict equality, by wrapping the function and checking its
 * parameters and return value through {@link isEquals} function. If some parameter
 * or return value does not match the expected type, it returns `null`. Otherwise
 * there's no type error, it returns the result of the function.
 *
 * By the way, if you want is not just testing type checking, but also finding
 * detailed type error reason(s), then use {@link assertEqualsFunction} or
 * {@link validateEqualsFunction} instead.
 *
 * On the other hand, if you want to allow any superfluous properties, utilize
 * {@link isFunction}, {@link assertFunction} or {@link validateFunction} instead.
 *
 * @template T Target function type
 * @param func Target function to test
 * @returns The wrapper function with type tests
 *
 * @author Jeongho Nam - https://github.com/samchon
 */
export function equalsFunction<T extends (...args: any[]) => any>(
  func: T,
): T extends (...args: infer Arguments) => infer Output
  ? Output extends Promise<infer R>
    ? (...args: Arguments) => Promise<R | null>
    : (...args: Arguments) => Output | null
  : never;

/**
 * @internal
 */
export function equalsFunction(): never {
  halt("equalsFunction");
}

/**
 * Tests parameters with strict equality.
 *
 * Tests a function, by wrapping the function and checking its parameters through
 * {@link isEquals} function. If some parameter does not match the expected type,
 * it returns `null`. Otherwise there's no type error, it returns the result of the
 * function.
 *
 * By the way, if you want is not just testing type checking, but also finding
 * detailed type error reason(s), then use {@link assertEqualsParameters} or
 * {@link validateEqualsParameters} instead.
 *
 * @template T Target function type
 * @param func Target function to test
 * @returns The wrapper function with type tests
 *
 * @author Jeongho Nam - https://github.com/samchon
 */
export function equalsParameters<T extends (...args: any[]) => any>(
  func: T,
): T extends (...args: infer Arguments) => infer Output
  ? Output extends Promise<infer R>
    ? (...args: Arguments) => Promise<R | null>
    : (...args: Arguments) => Output | null
  : never;

/**
 * @internal
 */
export function equalsParameters(): never {
  halt("equalsParameters");
}

/**
 * Tests return value with strict equality.
 *
 * Tests a function, by wrapping the function and checking its return value through
 * {@link isEquals} function. If the return value does not match the expected type,
 * it returns `null`. Otherwise there's no type error, it returns the result of the
 * function.
 *
 * By the way, if you want is not just testing type checking, but also finding
 * detailed type error reason(s), then use {@link assertEqualsReturn} or
 * {@link validateEqualsReturn} instead.
 *
 * On the other hand, if you want to allow any superfluous properties, utilize
 * {@link isReturn}, {@link assertReturn} or {@link validateReturn} instead.
 *
 * @template T Target function type
 * @param func Target function to test
 * @returns The wrapper function with type tests
 *
 * @author Jeongho Nam - https://github.com/samchon
 */
export function equalsReturn<T extends (...args: any[]) => any>(
  func: T,
): T extends (...args: infer Arguments) => infer Output
  ? Output extends Promise<infer R>
    ? (...args: Arguments) => Promise<R | null>
    : (...args: Arguments) => Output | null
  : never;

/**
 * @internal
 */
export function equalsReturn(): never {
  halt("equalsReturn");
}

/* -----------------------------------------------------------
  VALIDATE
----------------------------------------------------------- */
/**
 * Validates a function.
 *
 * Validates a function, by wrapping the function and checking its parameters and
 * return value through {@link validate} function. If some parameter or return value
 * does not match the expected type, it returns {@link IValidation.IError} typed
 * object. Otherwise there's no type error, it returns {@link IValidation.ISuccess}
 * typed object instead.
 *
 * For reference, {@link IValidation.IError.path} would be a little bit different with
 * individual {@link validate} function. If the {@link IValidation.IError} occurs from
 * some parameter, the path would start from `$input.parameters[number]`. Otherwise
 * the path would start from `$input.return`.
 *
 * - `$input.parameters[0].~`
 * - `$input.return.~`
 *
 * By the way, if what you want is not finding every type errors, but just finding
 * the 1st type error, then use {@link assertFunction} instead. Otherwise, what you
 * want is just validating parameters or return value only, you can use
 * {@link validateParameters} or {@link validateReturn} instead.
 *
 * On the other hand, if you don't want to allow any superfluous properties, utilize
 * {@link validateEqualsFunction} or {@link assertEqualsFunction} instead.
 *
 * @template T Target function type
 * @param func Target function to validate
 * @returns The wrapper function with type validations
 *
 * @author Jeongho Nam - https://github.com/samchon
 */
export function validateFunction<T extends (...args: any[]) => any>(
  func: T,
): T extends (...args: infer Arguments) => infer Output
  ? Output extends Promise<infer R>
    ? (...args: Arguments) => Promise<IValidation<R>>
    : (...args: Arguments) => IValidation<Output>
  : never;

/**
 * @internal
 */
export function validateFunction(): never {
  halt("validateFunction");
}

/**
 * Validates parameters.
 *
 * Validates a function, by wrapping the function and checking its parameters through
 * {@link validate} function. If some parameter does not match the expected type, it
 * returns {@link IValidation.IError} typed object. Otherwise there's no type error,
 * it returns {@link IValidation.ISuccess} typed object instead.
 *
 * For reference, {@link IValidation.IError.path} would be a little bit different with
 * individual {@link validate} function. If the {@link IValidation.IError} occurs from
 * some parameter, the path would start from `$input.parameters[number]`.
 *
 * By the way, if what you want is not finding every type errors, but just finding
 * the 1st type error, then use {@link assertParameters} instead. Otherwise, what you
 * want is not only validating parameters, but also validating return value, you can
 * use {@link validateFunction} instead.
 *
 * On the other hand, if you don't want to allow any superfluous properties, utilize
 * {@link validateEqualsParameters} or {@link assertEqualsParameters} instead.
 *
 * @template T Target function type
 * @param func Target function to validate
 * @returns The wrapper function with type validations
 *
 * @author Jeongho Nam - https://github.com/samchon
 */
export function validateParameters<T extends (...args: any[]) => any>(
  func: T,
): T extends (...args: infer Arguments) => infer Output
  ? Output extends Promise<infer R>
    ? (...args: Arguments) => Promise<IValidation<R>>
    : (...args: Arguments) => IValidation<Output>
  : never;

/**
 * @internal
 */
export function validateParameters(): never {
  halt("validateReturn");
}

/**
 * Validates return value.
 *
 * Validates a function, by wrapping the function and checking its return value through
 * {@link validate} function. If the return value does not match the expected type, it
 * returns {@link IValidation.IError} typed object. Otherwise there's no type error,
 * it returns {@link IValidation.ISuccess} typed object instead.
 *
 * For reference, {@link IValidation.IError.path} would be a little bit different with
 * individual {@link validate} function. If the {@link IValidation.IError} occurs from
 * the return value, the path would start from `$input.return`.
 *
 * By the way, if what you want is not finding every type errors, but just finding
 * the 1st type error, then use {@link assertReturn} instead. Otherwise, what you want
 * is not only validating return value, but also validating parameters, you can use
 * {@link validateFunction} instead.
 *
 * On the other hand, if you don't want to allow any superfluous properties, utilize
 * {@link validateEqualsReturn} or {@link assertEqualsReturn} instead.
 *
 * @template T Target function type
 * @param func Target function to validate
 * @returns The wrapper function with type validations
 *
 * @author Jeongho Nam - https://github.com/samchon
 */
export function validateReturn<T extends (...args: any[]) => any>(
  func: T,
): T extends (...args: infer Arguments) => infer Output
  ? Output extends Promise<infer R>
    ? (...args: Arguments) => Promise<IValidation<R>>
    : (...args: Arguments) => IValidation<Output>
  : never;

/**
 * @internal
 */
export function validateReturn(): never {
  halt("validateReturn");
}

/**
 * Validates a function with strict equality.
 *
 * Validates a function with strict equality, by wrapping the function and checking
 * its parameters and return value through {@link validateEquals} function. If some
 * parameter or return value does not match the expected type, it returns
 * {@link IValidation.IError} typed object. Otherwise there's no type error, it
 * returns {@link IValidation.ISuccess} typed object instead.
 *
 * For reference, {@link IValidation.IError.path} would be a little bit different with
 * individual {@link validateEquals} function. If the {@link IValidation.IError} occurs
 * from some parameter, the path would start from `$input.parameters[number]`. Otherwise
 * the path would start from `$input.return`.
 *
 * - `$input.parameters[0].~`
 * - `$input.return.~`
 *
 * By the way, if what you want is not finding every type errors, but just finding
 * the 1st type error, then use {@link assertEqualsFunction} instead. Otherwise, what
 * you want is just validating parameters or return value only, you can use
 * {@link validateEqualsParameters} or {@link validateEqualsReturn} instead.
 *
 * On the other hand, if you want to allow any superfluous properties, utilize
 * {@link validateFunction} or {@link assertFunction} instead.
 *
 * @template T Target function type
 * @param func Target function to validate
 * @returns The wrapper function with type validations
 *
 * @author Jeongho Nam - https://github.com/samchon
 */
export function validateEqualsFunction<T extends (...args: any[]) => any>(
  func: T,
): T extends (...args: infer Arguments) => infer Output
  ? Output extends Promise<infer R>
    ? (...args: Arguments) => Promise<IValidation<R>>
    : (...args: Arguments) => IValidation<Output>
  : never;

/**
 * @internal
 */
export function validateEqualsFunction(): never {
  halt("validateEqualsFunction");
}

/**
 * Validates parameters with strict equality.
 *
 * Validates a function, by wrapping the function and checking its parameters through
 * {@link validateEquals} function. If some parameter does not match the expected type,
 * it returns {@link IValidation.IError} typed object. Otherwise there's no type error,
 * it returns {@link IValidation.ISuccess} typed object instead.
 *
 * For reference, {@link IValidation.IError.path} would be a little bit different with
 * individual {@link validateEquals} function. If the {@link IValidation.IError} occurs
 * from some parameter, the path would start from `$input.parameters[number]`.
 *
 * By the way, if what you want is not finding every type errors, but just finding
 * the 1st type error, then use {@link assertEqualsParameters} instead. Otherwise,
 * what you want is not only validating parameters, but also validating return value,
 * you can use {@link validateEqualsFunction} instead.
 *
 * On the other hand, if you want to allow any superfluous properties, utilize
 * {@link validateParameters} or {@link assertParameters} instead.
 *
 * @template T Target function type
 * @param func Target function to validate
 * @returns The wrapper function with type validations
 *
 * @author Jeongho Nam - https://github.com/samchon
 */
export function validateEqualsParameters<T extends (...args: any[]) => any>(
  func: T,
): T extends (...args: infer Arguments) => infer Output
  ? Output extends Promise<infer R>
    ? (...args: Arguments) => Promise<IValidation<R>>
    : (...args: Arguments) => IValidation<Output>
  : never;

/**
 * @internal
 */
export function validateEqualsParameters(): never {
  halt("validateEqualsParameters");
}

/**
 * Validates return value with strict equality.
 *
 * Validates a function, by wrapping the function and checking its return value through
 * {@link validateEquals} function. If the return value does not match the expected type,
 * it returns {@link IValidation.IError} typed object. Otherwise there's no type error,
 * it returns {@link IValidation.ISuccess} typed object instead.
 *
 * For reference, {@link IValidation.IError.path} would be a little bit different with
 * individual {@link validateEquals} function. If the {@link IValidation.IError} occurs
 * from the return value, the path would start from `$input.return`.
 *
 * By the way, if what you want is not finding every type errors, but just finding
 * the 1st type error, then use {@link assertEqualsReturn} instead. Otherwise, what you
 * want is not only validating return value, but also validating parameters, you can use
 * {@link validateEqualsFunction} instead.
 *
 * On the other hand, if you want to allow any superfluous properties, utilize
 * {@link validateReturn} or {@link assertReturn} instead.
 *
 * @template T Target function type
 * @param func Target function to validate
 * @returns The wrapper function with type validations
 *
 * @author Jeongho Nam - https://github.com/samchon
 */
export function validateEqualsReturn<T extends (...args: any[]) => any>(
  func: T,
): T extends (...args: infer Arguments) => infer Output
  ? Output extends Promise<infer R>
    ? (...args: Arguments) => Promise<IValidation<R>>
    : (...args: Arguments) => IValidation<Output>
  : never;

/**
 * @internal
 */
export function validateEqualsReturn(): never {
  halt("validateEqualsReturn");
}

/* -----------------------------------------------------------
  HALTER
----------------------------------------------------------- */
/**
 * @internal
 */
function halt(name: string): never {
  throw new Error(
    `Error on typia.functional.${name}(): no transform has been configured. Read and follow https://typia.io/docs/setup please.`,
  );
}
