declare class NopeReference {
    key: string;
    constructor(key: string);
}

type RuleResult<T> = string | undefined | NopePrimitive<T>;
type Context = Record<string | number, any>;
type Rule<T> = (entry?: T | null, context?: Context) => RuleResult<T>;
type AsyncRule<T> = (entry?: T | null, context?: Context) => Promise<RuleResult<T>>;
interface Validatable<T> {
    validate: Rule<T>;
    validateAsync: AsyncRule<T>;
    getType: () => string;
}
interface ShapeErrors {
    [key: string]: string | ShapeErrors;
}
type Nil = null | undefined;

declare abstract class NopePrimitive<T> implements Validatable<T> {
    protected validationRules: (Rule<T> | AsyncRule<T>)[];
    protected _type: string;
    protected _entry: T | undefined;
    getType(): string;
    protected isEmpty(entry: T | Nil): boolean;
    required(message?: string): this;
    notAllowed(message?: string): this;
    when(keys: string[] | string, conditionObject: {
        is: boolean | ((...args: any) => boolean);
        then: NopePrimitive<any>;
        otherwise: NopePrimitive<any>;
    }): this;
    oneOf(options: (T | NopeReference | Nil)[] | NopeReference, message?: string): this;
    notOneOf(options: (T | NopeReference | Nil)[], message?: string): this;
    test(rule: Rule<T> | AsyncRule<T>): this;
    /**
     * @param entry - The value to be validated
     * @param context - Used for internal reference resolving. Do not pass this.
     */
    validate(entry?: T | Nil, context?: Record<string | number, unknown>): string | undefined;
    validateAsync(entry?: T | Nil, context?: Context): Promise<string | undefined>;
}

interface ObjectShape {
    [key: string]: Validatable<any> | NopeObject;
}
type ValidateOptions = {
    abortEarly: boolean;
};
type AnyObject = Record<string | number, any>;
declare class NopeObject {
    private objectShape;
    private validationRules;
    protected _type: string;
    constructor(objectShape?: ObjectShape);
    getType(): string;
    shape(shape: ObjectShape): this;
    extend(Base: NopeObject): this;
    noUnknown(message?: string): this;
    validate(entry: AnyObject, context?: AnyObject | null, options?: ValidateOptions): string | ShapeErrors | NopePrimitive<AnyObject> | Promise<string | NopePrimitive<AnyObject> | undefined> | undefined;
    validateAsync(entry: AnyObject, context?: Context, options?: Omit<ValidateOptions, 'abortEarly'>): any;
    validateAt(path: string, entry: Record<string | number, any>): any;
}

declare class NopeString extends NopePrimitive<string> {
    protected _type: string;
    validate(entry?: any, context?: Record<string, unknown>): string | undefined;
    validateAsync(entry?: any, context?: Record<string, unknown>): Promise<string | undefined>;
    protected isEmpty(value: string | Nil): boolean;
    regex(regex: RegExp, message?: string): this;
    url(message?: string): this;
    email(message?: string): this;
    min(length: number, message?: string): this;
    max(length: number, message?: string): this;
    greaterThan(length: number, message?: string): this;
    lessThan(length: number, message?: string): this;
    atLeast(length: number, message?: string): this;
    atMost(length: number, message?: string): this;
    between(startLength: number, endLength: number, atLeastMessage?: string, atMostMessage?: string): this;
    exactLength(length: number, message?: string): this;
    trim(): this;
}

declare class NopeNumber extends NopePrimitive<number> {
    private message;
    protected _type: string;
    integer(message?: string): this;
    min(size: number, message?: string): this;
    max(size: number, message?: string): this;
    greaterThan(size: number, message?: string): this;
    lessThan(size: number, message?: string): this;
    atLeast(size: number, message?: string): this;
    atMost(size: number, message?: string): this;
    between(sizeStart: number, sizeEnd: number, atLeastMessage?: string, atMostMessage?: string): this;
    positive(message?: string): this;
    negative(message?: string): this;
    validate(entry?: any, context?: Record<string | number, unknown>): string | undefined;
    validateAsync(entry?: any, context?: Record<string | number, unknown>): Promise<string | undefined>;
    constructor(message?: string);
}

declare class NopeBoolean extends NopePrimitive<boolean> {
    protected _type: string;
    true(message?: string): this;
    false(message?: string): this;
    validate(entry?: any, context?: Record<string | number, unknown>): string | undefined;
    validateAsync(entry?: any, context?: Record<string | number, unknown>): Promise<string | undefined>;
}

declare class NopeArray<T> implements Validatable<T[]> {
    protected _type: string;
    validationRules: Rule<T[]>[];
    ofShape: Validatable<T> | NopeObject | null;
    getType(): string;
    required(message?: string): this;
    of(primitive: Validatable<T> | NopeObject, message?: string): this;
    ofAsync(primitive: Validatable<T> | NopeObject, message?: string): this;
    minLength(length: number, message?: string): this;
    maxLength(length: number, message?: string): this;
    mustContain(value: T, message?: string): this;
    hasOnly(values: T[], message?: string): this;
    every(callback: (value: T) => boolean, message?: string): this;
    some(callback: (value: T) => boolean, message?: string): this;
    test(rule: Rule<T[]>): this;
    validate(entry?: T[] | null, context?: Record<string | number, unknown>): string | undefined;
    validateAsync(entry?: T[] | null, context?: Record<string | number, unknown>): Promise<string | undefined>;
}

type T = string | number | Date;
declare class NopeDate extends NopePrimitive<T> {
    private message;
    protected _type: string;
    before(beforeDate: T | NopeReference, message?: string): this;
    after(afterDate: T | NopeReference, message?: string): this;
    private parseDate;
    validate(entry?: any, context?: Record<string | number, unknown>): string | undefined | any;
    validateAsync(entry?: any, context?: Record<string | number, unknown>): Promise<string | undefined | any>;
    constructor(message?: string);
}

declare const object: () => NopeObject;
declare const string: () => NopeString;
declare const number: (message?: string) => NopeNumber;
declare const boolean: () => NopeBoolean;
declare const date: (message?: string) => NopeDate;
declare const array: <T>() => NopeArray<T>;
declare const ref: (key: string) => NopeReference;
declare const Nope: {
    object: () => NopeObject;
    string: () => NopeString;
    number: (message?: string) => NopeNumber;
    boolean: () => NopeBoolean;
    date: (message?: string) => NopeDate;
    array: <T>() => NopeArray<T>;
    ref: (key: string) => NopeReference;
};

export { Nope, array, boolean, date, Nope as default, number, object, ref, string };
