import { Effect as Effect_2 } from './schema';

export declare function addError(ctx: ValidationContext, errData: ErrorData): void;

declare type AllKeys<T> = T extends any ? keyof T : never;

export declare const array: <T extends SchemaTypeAny>(schema: T) => Array_2<T>;

declare class Array_2<T extends SchemaTypeAny, Cardinality extends ArrayCardinality = "many"> extends SchemaOf<arrayOutputType<T, Cardinality>, ArrayDef<T>, Cardinality extends "atleastone" ? [T["_input"], ...T["_input"][]] : T["_input"][]> {
    _validation(input: ValidateInput): ValidationResult<this["_output"]>;
    min(minLength: number, message?: errorUtil.ErrorMessage): this;
    max(maxLength: number, message?: errorUtil.ErrorMessage): this;
    length(len: number, message?: errorUtil.ErrorMessage): this;
    nonempty(message?: errorUtil.ErrorMessage): Array_2<T, "atleastone">;
    static create: <T_1 extends SchemaTypeAny>(schema: T_1) => Array_2<T_1>;
}

declare type ArrayCardinality = "many" | "atleastone";

declare interface ArrayDef<T extends SchemaTypeAny = SchemaTypeAny> extends SchemaTypeDef {
    type: T;
    typeName: SchemaKind.Array;
    minLength: {
        value: number;
        message?: string;
    } | null;
    maxLength: {
        value: number;
        message?: string;
    } | null;
}

declare type arrayOutputType<T extends SchemaTypeAny, Cardinality extends ArrayCardinality = "many"> = Cardinality extends "atleastone" ? [T["_output"], ...T["_output"][]] : T["_output"][];

declare type BaseObjectInputType<Shape extends SchemaRawShape> = objectUtil.Flatten<objectUtil.AddQuestionMarks<{
    [k in keyof Shape]: Shape[k]["_input"];
}>>;

declare type BaseObjectOutputType<Shape extends SchemaRawShape> = objectUtil.Flatten<objectUtil.AddQuestionMarks<{
    [k in keyof Shape]: Shape[k]["_output"];
}>>;

declare type ConvertEffect<T> = {
    type: "convert";
    convert: (arg: T, ctx: RefinementCtx) => any;
};

declare type CustomErrorParams = Partial<util.Omit<CustomValidation, "code">>;

export declare interface CustomValidation extends ValidationBase {
    code: typeof ErrorCode.custom;
    params?: {
        [k: string]: any;
    };
}

export declare type DIRTY<T> = {
    status: "dirty";
    value: T;
};

export declare const DIRTY: <T>(value: T) => DIRTY<T>;

export declare class Effect<T extends SchemaTypeAny, Output = T["_output"], Input = T["_input"]> extends SchemaOf<Output, EffectDef<T>, Input> {
    innerType(): T;
    _validation(input: ValidateInput): ValidationResult<this["_output"]>;
    static create: <I extends SchemaTypeAny>(schema: I, effect: EffectType<I["_output"]>) => Effect<I, I["_output"]>;
}

declare interface EffectDef<T extends SchemaTypeAny = SchemaTypeAny> extends SchemaTypeDef {
    schema: T;
    type: SchemaKind.Effect;
    effect: EffectType<any>;
}

declare type EffectType<T> = RefinementEffect<T> | ConvertEffect<T>;

export declare const ErrorCode: {
    invalid_type: "invalid_type";
    custom: "custom";
    invalid_string: "invalid_string";
    too_small: "too_small";
    too_big: "too_big";
    required: "required";
};

export declare type ErrorData = StripPath<ErrorType> & {
    path?: (string | number)[];
    fatal?: boolean;
};

export declare type ErrorMap = (error: ErrorType, _ctx: ErrorMapCtx) => {
    message: string;
};

declare type ErrorMapCtx = {
    defaultError: string;
    data: any;
};

declare type ErrorType = InvalidTypeError | InvalidString | TooSmall | TooBig | RequiredError | CustomValidation;

declare namespace errorUtil {
    type ErrorMessage = string | {
        message?: string;
    };
    const errToObj: (message?: ErrorMessage) => {
        message?: string;
    };
    const toString: (message?: ErrorMessage) => string | undefined;
}

export declare type FieldError<T> = {
    [P in AllKeys<T>]?: string;
};

export declare type FieldErrors<T, U = string> = {
    [P in AllKeys<T>]?: U[];
};

export declare type INVALID = {
    status: "aborted";
};

export declare const INVALID: INVALID;

declare interface InvalidString extends ValidationBase {
    code: typeof ErrorCode.invalid_string;
    validation: StringValidation;
}

declare interface InvalidTypeError extends ValidationBase {
    code: typeof ErrorCode.invalid_type;
    expected: ValidatedType;
    received: ValidatedType;
}

export declare const isAborted: (x: ValidationResult<any>) => x is INVALID;

export declare const isAsync: <T>(x: ValidationResult<T>) => x is ValidationAsync<T>;

export declare const isDirty: <T>(x: ValidationResult<T>) => x is OK<T> | DIRTY<T>;

export declare const isValid: <T>(x: ValidationResult<T>) => x is OK<T> | DIRTY<T>;

export declare const makeIssue: (params: {
    data: any;
    path: (string | number)[];
    errorMaps: ErrorMap[];
    errorData: ErrorData;
}) => ValidationError;

export declare const object: <T extends SchemaRawShape>(shape: T) => Object_2<T>;

declare class Object_2<T extends SchemaRawShape, Catchall extends SchemaTypeAny = SchemaTypeAny, Output = ObjectOutputType<T, Catchall>, Input = ObjectInputType<T, Catchall>> extends SchemaOf<Output, ObjectDef<T>, Input> {
    private _cached;
    _getCached(): {
        shape: T;
        keys: string[];
    };
    _validation(input: ValidateInput): ValidationResult<this["_output"]>;
    static create: <T_1 extends SchemaRawShape>(shape: T_1) => Object_2<T_1>;
}

declare interface ObjectDef<T extends SchemaRawShape = SchemaRawShape> extends SchemaTypeDef {
    typeName: SchemaKind.Object;
    shape: () => T;
}

declare type ObjectInputType<Shape extends SchemaRawShape, Catchall extends SchemaTypeAny> = SchemaTypeAny extends Catchall ? BaseObjectInputType<Shape> : objectUtil.Flatten<BaseObjectInputType<Shape> & {
    [k: string]: Catchall["_input"];
}>;

declare type ObjectOutputType<Shape extends SchemaRawShape, Catchall extends SchemaTypeAny> = SchemaTypeAny extends Catchall ? BaseObjectOutputType<Shape> : objectUtil.Flatten<BaseObjectOutputType<Shape> & {
    [k: string]: Catchall["_output"];
}>;

declare namespace objectUtil {
    type OptionalKeys<T extends object> = {
        [k in keyof T]: undefined extends T[k] ? k : never;
    }[keyof T];
    type RequiredKeys<T extends object> = {
        [k in keyof T]: undefined extends T[k] ? never : k;
    }[keyof T];
    type AddQuestionMarks<T extends object> = Partial<Pick<T, OptionalKeys<T>>> & Pick<T, RequiredKeys<T>>;
    type Identity<T> = T;
    type Flatten<T extends object> = Identity<{
        [k in keyof T]: T[k];
    }>;
    const mergeShapes: <U extends SchemaRawShape, T extends SchemaRawShape>(first: U, second: T) => T & U;
        {};
}

export declare type OK<T> = {
    status: "valid";
    value: T;
};

export declare const OK: <T>(value: T) => OK<T>;

export declare type RefinementCtx = {
    addIssue: (arg: ErrorData) => void;
    path: (string | number)[];
};

declare type RefinementEffect<T> = {
    type: "refinement";
    refinement: (arg: T, ctx: RefinementCtx) => any;
};

declare interface RequiredError extends ValidationBase {
    code: typeof ErrorCode.required;
    params?: {
        [k: string]: any;
    };
}

export declare type SchemaError<T> = {
    ok: false;
    validation: Validation<T>;
};

export declare enum SchemaKind {
    String = "String",
    Array = "Array",
    Object = "Object",
    Effect = "Effect"
}

export declare abstract class SchemaOf<Output = any, Def extends SchemaTypeDef = SchemaTypeDef, Input = Output> {
    readonly _output: Output;
    readonly _input: Input;
    readonly _def: Def;
    abstract _validation(input: ValidateInput): ValidationResult<Output>;
    _getOrReturnCtx(input: ValidateInput, ctx?: ValidationContext | undefined): ValidationContext;
    _processInputParams(input: ValidateInput): {
        status: ValidationStatus;
        ctx: ValidationContext;
    };
    _validationSync(input: ValidateInput): ValidationSync<Output>;
    _validationAsync(input: ValidateInput): ValidationAsync<Output>;
    validate<T>(data: T, params?: Partial<ValidationParams>): Promise<SchemaValidation<T>>;
    add<RefinedOutput extends Output>(check: (arg: Output) => arg is RefinedOutput, message?: string | CustomErrorParams | ((arg: Output) => CustomErrorParams)): Effect<this, RefinedOutput, Input>;
    add(check: (arg: Output) => unknown | Promise<unknown>, message?: string | CustomErrorParams | ((arg: Output) => CustomErrorParams)): Effect<this, Output, Input>;
    refinement<RefinedOutput extends Output>(check: (arg: Output) => arg is RefinedOutput, refinementData: ErrorData | ((arg: Output, ctx: RefinementCtx) => ErrorData)): Effect<this, RefinedOutput, Input>;
    refinement(check: (arg: Output) => boolean, refinementData: ErrorData | ((arg: Output, ctx: RefinementCtx) => ErrorData)): Effect<this, Output, Input>;
    _refinement(refinement: RefinementEffect<Output>["refinement"]): Effect<this, Output, Input>;
    constructor(def: Def);
    array(): Array_2<this>;
    convert<NewOut>(convert: (arg: Output, ctx: RefinementCtx) => NewOut | Promise<NewOut>): Effect<this, NewOut>;
}

export declare type SchemaOk<T> = {
    ok: true;
    data: T;
};

export declare type SchemaRawShape = {
    [k: string]: SchemaTypeAny;
};

export declare type SchemaTypeAny = SchemaOf<any, any, any>;

export declare interface SchemaTypeDef {
    errorMap?: ErrorMap;
}

export declare type SchemaValidation<T> = SchemaOk<T> | SchemaError<T>;

export declare const string: () => String_2;

declare class String_2 extends SchemaOf<string, StringDef> {
    _validation(input: ValidateInput): ValidationResult<string>;
    protected _regex: (regex: RegExp, validation: StringValidation, message?: errorUtil.ErrorMessage) => Effect_2<this, string, string>;
    _addCheck(check: StringCheck): String_2;
    required(message?: errorUtil.ErrorMessage): String_2;
    email(message?: errorUtil.ErrorMessage): String_2;
    url(message?: errorUtil.ErrorMessage): String_2;
    regex(regex: RegExp, message?: errorUtil.ErrorMessage): String_2;
    startWith(value: string, message?: errorUtil.ErrorMessage): String_2;
    endWith(value: string, message?: errorUtil.ErrorMessage): String_2;
    min(minLength: number, message?: errorUtil.ErrorMessage): String_2;
    max(maxLength: number, message?: errorUtil.ErrorMessage): String_2;
    length(len: number, message?: errorUtil.ErrorMessage): String_2;
    trim: () => String_2;
    static create: () => String_2;
}

declare type StringCheck = {
    kind: "required";
    message?: string;
} | {
    kind: "min";
    value: number;
    message?: string;
} | {
    kind: "max";
    value: number;
    message?: string;
} | {
    kind: "email";
    message?: string;
} | {
    kind: "url";
    message?: string;
} | {
    kind: "startWith";
    value: string;
    message?: string;
} | {
    kind: "endWith";
    value: string;
    message?: string;
} | {
    kind: "regex";
    regex: RegExp;
    message?: string;
} | {
    kind: "trim";
    message?: string;
};

declare interface StringDef extends SchemaTypeDef {
    checks: StringCheck[];
    type: SchemaKind.String;
}

export declare type StringValidation = "email" | "url" | "regex" | {
    startWith: string;
} | {
    endWith: string;
};

declare type StripPath<T extends object> = T extends any ? util.OmitKeys<T, "path"> : never;

declare interface TooBig extends ValidationBase {
    code: typeof ErrorCode.too_big;
    maximum: number;
    inclusive: boolean;
    type: "array" | "string";
}

declare interface TooSmall extends ValidationBase {
    code: typeof ErrorCode.too_small;
    minimum: number;
    inclusive: boolean;
    type: "array" | "string";
}

declare namespace util {
    function assertNever(_x: never): never;
    type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
    type OmitKeys<T, K extends string> = Pick<T, Exclude<keyof T, K>>;
    const arrayToEnum: <T extends string, U extends [T, ...T[]]>(items: U) => { [k in U[number]]: k; };
    const objectValues: (obj: any) => any[];
    const objectKeys: ObjectConstructor["keys"];
}

declare const ValidatedType: {
    string: "string";
    object: "object";
    unknown: "unknown";
    array: "array";
};

declare type ValidatedType = keyof typeof ValidatedType;

export declare type ValidateInput = {
    data: any;
    path: (string | number)[];
    parent: ValidationContext;
};

export declare class ValidateInputPath implements ValidateInput {
    parent: ValidationContext;
    data: any;
    _path: ValidationPath;
    _key: string | number | (string | number)[];
    constructor(parent: ValidationContext, value: any, path: ValidationPath, key: string | number | (string | number)[]);
    get path(): (string | number)[];
}

export declare class Validation<T = any> extends Error {
    errors: ValidationError[];
    constructor(errors: ValidationError[]);
    static create: (errors: ValidationError[]) => Validation<any>;
    addError: (sub: ValidationError) => void;
    addErrors: (subs?: ValidationError[]) => void;
    getFieldErrors(): FieldErrors<T>;
    getFieldErrors<U>(mapper?: (error: ValidationError) => U): FieldErrors<T, U>;
    getFieldError<K extends keyof T>(fields?: K[]): FieldError<T>;
}

export declare type ValidationAsync<T> = Promise<ValidationSync<T>>;

declare type ValidationBase = {
    path: (string | number)[];
    message?: string;
};

export declare interface ValidationContext {
    readonly common: {
        readonly issues: ValidationError[];
        readonly errorMap?: ErrorMap;
        readonly async: boolean;
    };
    readonly path: ValidationPath;
    readonly schemaErrorMap?: ErrorMap;
    readonly parent: ValidationContext | null;
    readonly data: any;
    readonly type: ValidatedType;
}

export declare type ValidationError = ErrorType & {
    message: string;
};

export declare type ValidationParams = {
    path: (string | number)[];
    errorMap: ErrorMap;
    async: boolean;
};

export declare type ValidationPath = ValidationPathComponent[];

declare type ValidationPathComponent = string | number;

export declare type ValidationResult<T> = ValidationSync<T> | ValidationAsync<T>;

export declare class ValidationStatus {
    value: "aborted" | "dirty" | "valid";
    dirty(): void;
    abort(): void;
    static mergeArray(status: ValidationStatus, results: ValidationSync<any>[]): ValidationSync;
    static mergeObjectAsync(status: ValidationStatus, pairs: {
        key: ValidationResult<any>;
        value: ValidationResult<any>;
    }[]): Promise<ValidationSync<any>>;
    static mergeObjectSync(status: ValidationStatus, pairs: {
        key: ValidationSync<any>;
        value: ValidationSync<any>;
        alwaysSet?: boolean;
    }[]): ValidationSync;
}

export declare type ValidationSync<T = any> = OK<T> | DIRTY<T> | INVALID;

export { }
