import type { Main } from '../index.js';
import { testSymbol, optionalSymbol, nullableSymbol, absentSymbol, type BasePredicate } from './base-predicate.js';
/**
Function executed when the provided validation fails.

@param value - The tested value.
@param label - Label of the tested value.

@returns {string} - The actual error message.
*/
export type ValidatorMessageBuilder<T> = (value: T, label?: string) => string;
/**
@hidden
*/
export type Validator<T> = {
    message(value: T, label?: string, result?: unknown): string;
    validator(value: T): unknown;
    /**
    Provide custom message used by `not` operator.

    When absent, the return value of `message()` is used and 'not' is inserted after the first 'to', e.g. `Expected 'smth' to be empty` -> `Expected 'smth' to not be empty`.
    */
    negatedMessage?(value: T, label: string): string;
};
/**
@hidden
*/
export type PredicateOptions = {
    optional?: boolean;
    nullable?: boolean;
    absent?: boolean;
};
/**
@hidden
*/
export type Context<T = unknown> = {
    validators: Array<Validator<T>>;
} & PredicateOptions;
/**
@hidden
*/
export declare const validatorSymbol: unique symbol;
export type CustomValidator<T> = (value: T) => {
    /**
    Should be `true` if the validation is correct.
    */
    validator: boolean;
    /**
    The error message which should be shown if the `validator` is `false`. Or a error function which returns the error message and accepts the label as first argument.
    */
    message: string | ((label: string) => string);
};
/**
@hidden
*/
export declare class Predicate<T = unknown> implements BasePredicate<T> {
    [optionalSymbol]?: boolean;
    [nullableSymbol]?: boolean;
    [absentSymbol]?: boolean;
    private readonly type;
    private readonly context;
    protected readonly options: PredicateOptions;
    constructor(type: string, options?: PredicateOptions, existingValidators?: Array<Validator<T>>);
    /**
    @hidden
    */
    [testSymbol](value: T, main: Main, label: string | (() => string), idLabel: boolean): asserts value is T;
    /**
    @hidden
    */
    get [validatorSymbol](): Array<Validator<T>>;
    /**
    Invert the following validators.
    */
    get not(): this;
    /**
    Test if the value matches a custom validation function. The validation function should return an object containing a `validator` and `message`. If the `validator` is `false`, the validation fails and the `message` will be used as error message. If the `message` is a function, the function is invoked with the `label` as argument to let you further customize the error message.

    @param customValidator - Custom validation function.
    */
    validate(customValidator: CustomValidator<T>): this;
    /**
    Test if the value matches a custom validation function. The validation function should return `true` if the value passes the function. If the function either returns `false` or a string, the function fails and the string will be used as error message.

    @param validator - Validation function.
    */
    is(validator: (value: T) => boolean | string): this;
    /**
    Use a custom validation function that throws an error when the validation fails. This is useful for reusing existing validators or composing complex validations.

    @param customValidator - Custom validation function that throws an error if the value is invalid.

    @example
    ```
    import ow from 'ow';

    interface User {
        name: string;
        age: number;
    }

    const validateUser = (user: User) => {
        ow(user.name, 'User.name', ow.string.nonEmpty);
        ow(user.age, 'User.age', ow.number.integer.positive);
    };

    ow([{name: 'Alice', age: 30}], ow.array.ofType(ow.object.custom(validateUser)));
    ```

    @example
    This is particularly useful when you have existing validation functions and want to compose them:

    ```
    import ow from 'ow';

    interface Animal {
        type: string;
        weight: number;
    }

    const validateAnimal = (animal: Animal) => {
        ow(animal.type, 'Animal.type', ow.string.oneOf(['dog', 'cat', 'elephant']));
        ow(animal.weight, 'Animal.weight', ow.number.finite.positive);
    };

    const animals: Animal[] = [
        {type: 'dog', weight: 5},
        {type: 'cat', weight: Number.POSITIVE_INFINITY}
    ];

    ow(animals, ow.array.ofType(ow.object.custom(validateAnimal)));
    //=> ArgumentError: (array) (object) Expected number `Animal.weight` to be finite, got Infinity
    ```
    */
    custom(customValidator: (value: T) => void): this;
    /**
    Provide a new error message to be thrown when the validation fails.

    @param newMessage - Either a string containing the new message or a function returning the new message.

    @example
    ```
    ow('🌈', 'unicorn', ow.string.equals('🦄').message('Expected unicorn, got rainbow'));
    //=> ArgumentError: Expected unicorn, got rainbow
    ```

    @example
    ```
    ow('🌈', ow.string.minLength(5).message((value, label) => `Expected ${label}, to have a minimum length of 5, got \`${value}\``));
    //=> ArgumentError: Expected string, to be have a minimum length of 5, got `🌈`
    ```
    */
    message(newMessage: string | ValidatorMessageBuilder<T>): this;
    /**
    Register a new validator.

    @param validator - Validator to register.
    */
    addValidator(validator: Validator<T>): this;
    /**
    @hidden
    Create a new instance with the given validators
    */
    protected withValidators(validators: Array<Validator<T>>): this;
}
