import * as _the_minimal_types from '@the-minimal/types';
import { Assertion, Intersection, UnknownAssertion, Nullable, Nullish, Maybe } from '@the-minimal/types';
export { InferAssertion as Infer } from '@the-minimal/types';

/**
 * Checks if all the assertions pass.
 *
 * If you have two or three assertions consider using {@link and2} or {@link and3} respectively.
 *
 * @param assertions - Array of assertions to be checked.
 *
 * @example
 * ```ts
 * const userEmail = and([string, minLength(5), maxLength(35), email]);
 *
 * userEmail(1); // Error: type
 * userEmail("a"); // Error: minLength
 * userEmail("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@test.com"); // Error: maxLength
 * userEmail("Hello world"); // Error: email
 * userEmail("yamiteru@icloud.com"); // passes
 * ```
 */
declare const and: <const $Values extends unknown[]>(assertions: { [$Key in keyof $Values]: Assertion<$Values[$Key]>; }) => Assertion<Intersection<$Values>>;

/**
 * Checks that both assertions pass.
 *
 * @param assertion1- First assertion to be checked.
 * @param assertion2- Second assertion to be checked.
 *
 * @example
 * ```ts
 * const userEmail = and(string, email);
 *
 * userEmail(1); // Error: type
 * userEmail("Hello world"); // Error: email
 * userEmail("yamiteru@icloud.com"); // passes
 * ```
 */
declare const and2: <$Value1, $Value2>(assertion1: Assertion<$Value1>, assertion2: Assertion<$Value2>) => Assertion<$Value1 & $Value2>;

/**
 * Checks that all three assertions pass.
 *
 * @param assertion1- First assertion to be checked.
 * @param assertion2- Second assertion to be checked.
 * @param assertion3- Third assertion to be checked.
 *
 * @example
 * ```ts
 * const userEmail = and(string, minLength(5), email);
 *
 * userEmail(1); // Error: type
 * userEmail("a"); // Error: minLength
 * userEmail("Hello world"); // Error: email
 * userEmail("yamiteru@icloud.com"); // passes
 * ```
 */
declare const and3: <$Value1, $Value2, $Value3>(assertion1: Assertion<$Value1>, assertion2: Assertion<$Value2>, assertion3: Assertion<$Value3>) => Assertion<$Value1 & $Value2 & $Value3>;

/**
 * Checks that assertion passes for each element of the array.
 *
 * @param assertion - Assertion to be applied to each element of the array.
 *
 * @example
 * ```ts
 * const numbers = array(number);
 *
 * numbers([1, "2", 3]); // Error: type
 * numbers([1, 2, 3]); // passes
 * ```
 */
declare const array: <$Value>(assertion: Assertion<$Value>) => Assertion<$Value[]>;

/**
 * Checks that the value is a boolean.
 *
 * @example
 * ```ts
 * boolean(1); // Error: type
 * boolean(true); // passes
 * ```
 */
declare const boolean: _the_minimal_types.Assertion<boolean>;

/**
 * Checks if value matches email RegExp.
 *
 * @example
 * ```ts
 * email("hello"); // Error: regex
 * email("yamiteru@icloud.com"); // passes
 * ```
 */
declare const email: _the_minimal_types.Assertion<unknown>;

/**
 * Checks if value ends with `searchString`.
 *
 * @param input - The characters to be searched for at the end of value.
 *
 * @throws `endsWith` if value does not end with `searchString`.
 *
 * @example
 * ```ts
 * const question = endsWith("?");
 *
 * question("hey!");    // Error: endsWith
 * question("really?"); // passes
 * ```
 */
declare const endsWith: <$Input extends string>(input: $Input) => Assertion<unknown>;

/**
 * Wraps assertion and throws an error with message if assertion fails.
 *
 * @param assertion - Assertion to be checked.
 * @param message - Message to be used in error.
 *
 * @example
 * ```ts
 * const stringWithMessage = expect(
 *    string,
 *    (_, v) => `Expected string, got ${typeof v}`
 * );
 * ```
 */
declare const expect: <$Assertion extends UnknownAssertion>(assertion: $Assertion, message: (error: any, value: unknown) => string) => $Assertion;

/**
 * Checks if value includes with another value.
 *
 * @param input - Value used in matching.
 *
 * @example
 * ```ts
 * const hello = includes("hello");
 *
 * hello("ahoj"); // Error: includes
 * hello("--hello--"); // passes
 * ```
 */
declare const includes: (input: unknown) => Assertion<unknown>;

/**
 * Checks if value is integer.
 *
 * @example
 * ```ts
 * integer(1.1) // Error: integer
 * integer(1) // passes
 * ```
 */
declare const integer: Assertion<unknown>;

/**
 * Checks that the value is an array.
 *
 * @example
 * ```ts
 * isArray(1); // Error: isArray
 * isArray([]); // passes
 * ```
 */
declare const isArray: Assertion<unknown[]>;

/**
 * Checks that the value is of type object and is not null.
 *
 * @example
 * ```ts
 * isObject(1); // Error: isObject
 * isObject(null); // Error: isObject
 * isObject({}); // passes
 * ```
 */
declare const isObject: Assertion<Record<string, unknown>>;

/**
 * Wraps assertion in a function that will be evaluated only when the assertion is called.
 *
 * @param assertion - Assertion to be lazily evaluated.
 *
 * @example
 * ```ts
 * const user = object({
 *    name; string,
 *    friend: optional(lazy(() => user))
 * });
 *
 * user({
 *    name: "yamiteru",
 *    friend: {
 *        name: "yamiteru",
 *    }
 * }); // passes
 * ```
 */
declare const lazy: <$Assertion extends UnknownAssertion>(assertion: $Assertion) => $Assertion;

/**
 * Checks if length of value is equal to the provided length.
 *
 * @param input - Length used in the comparison.
 *
 * @example
 * ```ts
 * const nonEmpty = notLength(0);
 *
 * nonEmpty(""); // Error: notLength
 * nonEmpty("hello"); // passes
 * ```
 */
declare const length: (input: number) => Assertion<unknown>;

/**
 * Checks if length of value is less than or equal to the provided length.
 *
 * @param length - Length used in the comparison.
 *
 * @example
 * ```ts
 * const passwordMaxLength = maxLength(16);
 *
 * passwordMaxLength("aaaaaaaaaaaaaaaaa"); // Error: maxLength
 * passwordMaxLength("Test123456"); // passes
 * ```
 */
declare const maxLength: (length: number) => Assertion<unknown>;

/**
 * Checks if value is less than or equal to the provided length.
 *
 * @param input - Value used in the comparison.
 *
 * @example
 * ```ts
 * const teenagerAge = maxValue(17);
 *
 * teenagerAge(26); // Error: maxValue
 * teenagerAge(15); // passes
 * ```
 */
declare const maxValue: (input: unknown) => (v: unknown) => true;

/**
 * Checks if length of value is greater than or equal to the provided length.
 *
 * @param length - Length used in the comparison.
 *
 * @example
 * ```ts
 * const passwordMinLength = minLength(8);
 *
 * passwordMinLength("hello"); // Error: minLength
 * passwordMinLength("Test123456"); // passes
 * ```
 */
declare const minLength: (length: number) => Assertion<unknown>;

/**
 * Checks if value is greater than or equal to the provided length.
 *
 * @param input - Value used in the comparison.
 *
 * @example
 * ```ts
 * const adultAge = minValue(18);
 *
 * adultAge(15); // Error: minValue
 * adultAge(26); // passes
 * ```
 */
declare const minValue: (input: unknown) => Assertion<unknown>;

/**
 * Checks if the remainer is equal to the specified value when the input is divided by the divider.
 *
 * @param divider - Value used for the division.
 * @param remainder - Value that should remain after the division is done.
 *
 * @example
 * ```ts
 * const multipleOfTwo = modulo(2, 0);
 *
 * multipleOfTwo(3); // Error: modulo
 * multipleOfTwo(6); // passes
 * ```
 */
declare const modulo: (divider: number, remainder: number) => Assertion<unknown>;

/**
 * Checks if length of value is not equal to the provided length.
 *
 * @param length - Length used in the comparison.
 *
 * @example
 * ```ts
 * const twoLetters = length(2);
 *
 * twoLetters(""); // Error: length
 * twoLetters("ab"); // passes
 * ```
 */
declare const notLength: (length: number) => Assertion<unknown>;

/**
 * Checks if value is not equal to the provided value.
 *
 * @param input - Value used in the comparison.
 *
 * @example
 * ```ts
 * const notNan = notValue(NaN);
 *
 * notNan(NaN); // Error: notValue
 * notNan(123); // passes
 * ```
 */
declare const notValue: (input: unknown) => Assertion<unknown>;

/**
 * Checks if the assertion passes or if the value is null.
 *
 * @param assertion - Assertion to be checked.
 *
 * @example
 * ```ts
 * const maybeString = nullable(string);
 *
 * maybeString(1); // Error: type
 * maybeString("hello"); // passes
 * maybeString(null); // passes
 * ```
 */
declare const nullable: <$Value>(assertion: Assertion<$Value>) => Assertion<Nullable<$Value>>;

/**
 * Checks if the assertion passes or if the value is null or undefined.
 *
 * @param assertion - Assertion to be checked.
 *
 * @example
 * ```ts
 * const maybeString = nullish(string);
 *
 * maybeString(1); // Error: type
 * maybeString("hello"); // passes
 * maybeString(null); // passes
 * maybeString(undefined); // passes
 * ```
 */
declare const nullish: <$Value>(assertion: Assertion<$Value>) => Assertion<Nullish<$Value>>;

/**
 * Checks that the value is a number.
 *
 * @example
 * ```ts
 * number("hello"); // Error: type
 * number(1); // passes
 * ```
 */
declare const number: _the_minimal_types.Assertion<number>;

/**
 * Checks that assertion passes for each key/value of the object.
 *
 * @param schema - Object schema to be checked.
 *
 * @example
 * ```ts
 * const user = object({
 *    name: string,
 *    age: number,
 * });
 *
 * user(1); // Error: type
 * user(null); // Error: isObject
 * user({
 *    name: 1,
 *    age: "23",
 * }); // Error: type
 * user({
 *    name: "yamiteru",
 *    age: 23,
 * }); // passes
 * ```
 */
declare const object: <$Schema extends Record<string, unknown>>(schema: { [$Key in keyof $Schema]: Assertion<$Schema[$Key]>; }) => Assertion<$Schema>;

/**
 * Checks if the assertion passes or if the value is undefined.
 *
 * @param assertion - Assertion to be checked.
 *
 * @example
 * ```ts
 * const maybeString = optional(string);
 *
 * maybeString(1); // Error: type
 * maybeString("hello"); // passes
 * maybeString(undefined); // passes
 * ```
 */
declare const optional: <$Value>(assertion: Assertion<$Value>) => Assertion<Maybe<$Value>>;

/**
 * Checks if one of the assertions passes.
 *
 * If none of them passes it throws an error.
 *
 * If you have two or three assertions consider using {@link or2} or {@link or3} respectively.
 *
 * @param assertions - Array of assertions to be checked
 *
 * @example
 * ```ts
 * const trueish = or([literal(1), literal(true), literal("true"), literal("yes")]);
 *
 * trueish("yassss"); // Error: literal
 * trueish(1); // passes
 * trueish(true); // passes
 * trueish("true"); // passes
 * trueish("yes"); // passes
 * ```
 */
declare const or: <const $Values extends unknown[]>(assertions: { [$Key in keyof $Values]: Assertion<$Values[$Key]>; }) => Assertion<$Values[number]>;

/**
 * Checks if one of two assertions passes.
 *
 * If none of them passes it throws an error.
 *
 * @param assertion1 - First assertion to be checked.
 * @param assertion2 - Second assertion to be checked.
 *
 * @example
 * ```ts
 * const trueish = or(literal(1), literal(true), literal("true"));
 *
 * trueish("yassss"); // Error: literal
 * trueish(1); // passes
 * trueish(true); // passes
 * trueish("true"); // passes
 * ```
 */
declare const or2: <$Value1, $Value2>(assertion1: Assertion<$Value1>, assertion2: Assertion<$Value2>) => Assertion<$Value1 | $Value2>;

/**
 * Checks if one of two assertions passes.
 *
 * If none of them passes it throws an error.
 *
 * @param assertion1 - First assertion to be checked.
 * @param assertion2 - Second assertion to be checked.
 * @param assertion2 - Third assertion to be checked.
 *
 * @example
 * ```ts
 * const trueish = or(literal(1), literal(true));
 *
 * trueish("yassss"); // Error: literal
 * trueish(1); // passes
 * trueish(true); // passes
 * ```
 */
declare const or3: <$Value1, $Value2, $Value3>(assertion1: Assertion<$Value1>, assertion2: Assertion<$Value2>, assertion3: Assertion<$Value3>) => Assertion<$Value1 | $Value2 | $Value3>;

/**
 * Checks if length of value is in the range of min and max.
 *
 * @param min - Minimal value.
 * @param max - Maximal value.
 *
 * @example
 * ```ts
 * const passwordLength = rangeLength(8, 16);
 *
 * passwordLength("hello"); // Error: rangeLength
 * passwordLength("aaaaaaaaaaaaaaaaaaaa"); // Error: rangeLength
 * passwordLength("Test123456"); // passes
 * ```
 */
declare const rangeLength: (min: number, max: number) => Assertion<unknown>;

/**
 * Checks if value is in the range of min and max.
 *
 * @param min - Minimal value.
 * @param max - Maximal value.
 *
 * @example
 * ```ts
 * const alive = rangeValue(0, 150);
 *
 * alive(-10); // Error: rangeValue
 * alive(450); // Error: rangeValue
 * alive(26); // passes
 * ```
 */
declare const rangeValue: <$Min, $Max extends $Min>(min: $Min, max: $Max) => Assertion<unknown>;

/**
 * Checks if value matches regex pattern.
 *
 * @param pattern - Pattern used for matching.
 *
 * @example
 * ```ts
 * const digits = regex(/[0-9]/);
 *
 * email("hello"); // Error: regex
 * email("123"); // passes
 * ```
 */
declare const regex: (pattern: RegExp) => Assertion<unknown>;

/**
 * Checks if value starts with `searchString`.
 *
 * @param input - The characters to be searched for at the start of value.
 *
 * @throws `startsWith` if value does not start with `searchString`.
 *
 * @example
 * ```ts
 * const startsWithId = startsWith("ID: ");
 *
 * startsWithId("123");     // Error: startsWith
 * startsWithId("ID: 123"); // passes
 * ```
 */
declare const startsWith: (input: string) => Assertion<unknown>;

/**
 * Checks that the value is a string.
 *
 * @example
 * ```ts
 * string(1); // Error: type
 * string("hello"); // passes
 * ```
 */
declare const string: _the_minimal_types.Assertion<string>;

/**
 * Checks that assertion passes for each element of the tuple.
 *
 * @param assertions - Tuple schema to be checked.
 *
 * @example
 * ```ts
 * const position = tuple([number, number]);
 *
 * position(1); // Error: isArray
 * position([1, "2"]) // Error: type
 * position([1, 2]) // passes
 * ```
 */
declare const tuple: <const $Values extends unknown[]>(assertions: { [$Key in keyof $Values]: Assertion<$Values[$Key]>; }) => Assertion<$Values>;

/**
 * Checks that the value is of the provided type.
 *
 * @param input - Type to be validated against.
 *
 * @example
 * ```ts
 * const string = type<string>("string");
 *
 * string(1); // Error: type
 * string("hello"); // passes
 * ```
 */
declare const type: <$Type>(input: string) => Assertion<$Type>;

/**
 * Checks if the value is one of the provided options.
 *
 * @param options - Options to be validated against.
 *
 * @example
 * ```ts
 * const userRole = union(["admin", "user"]);
 *
 * userRole("super-admin"); // Error: union
 * userRole("admin"); // passes
 * userRole("user"); // passes
 * ```
 */
declare const union: <const $Options extends unknown[]>(options: $Options) => Assertion<$Options[number]>;

/**
 * Checks if value is equal to the provided value.
 *
 * @param input - Value used in the comparison.
 *
 * @example
 * ```ts
 * const isNull = value(null);
 *
 * isNull(""); // Error: value
 * isNull(null); // passes
 * ```
 */
declare const value: <const $Input>(input: $Input) => Assertion<$Input>;

/**
 * Asserts that value is value of assertion.
 *
 * @param assertion - Assertion to be checked.
 * @param value - Value to be passed into assertion.
 *
 * @example
 * ```ts
 * assert(number, value); // asserts value is number
 * ```
 */
declare function assert<$Value>(assertion: Assertion<$Value>, value: unknown): asserts value is $Value;

/**
 * Throws ValidationError
 *
 * @param cause - What caused the error.
 * @param message - Optional error message.
 *
 * @throws {ValidationError}
 */
declare const ValidationError: (cause: unknown, message?: string | undefined, code?: number | undefined) => never;

/**
 * Returns true if assertion passes and false if it throws.
 *
 * @param assertion - Assertion to be checked.
 * @param value - Value to be passed into assertion.
 *
 * @example
 * ```ts
 * is(number, 1); // true
 * is(number, "nope"); // false
 * ```
 */
declare function is<$Value>(assertion: $Value, value: unknown): value is $Value;

/**
 * Checks if assertion is valid and returns value with type inferred from the assertion.
 *
 * @param assertion - Assertion to be checked.
 * @param value - Value to be passed into assertion.
 *
 * @example
 * ```ts
 * parse(number, 1); // 1
 * parse(number, "nope"); // Error: number
 * ```
 */
declare function parse<$Value>(assertion: Assertion<$Value>, value: unknown): $Value;

export { ValidationError, and, and2, and3, array, assert, boolean, email, endsWith, expect, includes, integer, is, isArray, isObject, lazy, length, maxLength, maxValue, minLength, minValue, modulo, notLength, notValue, nullable, nullish, number, object, optional, or, or2, or3, parse, rangeLength, rangeValue, regex, startsWith, string, tuple, type, union, value };
