import { FormHTMLAttributes } from 'react';
import { JSX } from 'react/jsx-runtime';
import { ReactNode } from 'react';

declare type AllKeys<T> = T extends any ? keyof T : never;

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 Clear = () => void;

declare type ConvertEffect<T> = {
    type: "convert";
    convert: (arg: T, ctx: RefinementCtx) => any;
};

declare type CustomErrorParams = Partial<util.Omit<CustomValidation, "code">>;

declare interface CustomValidation extends ValidationBase {
    code: typeof ErrorCode.custom;
    params?: {
        [k: string]: any;
    };
}

declare type DIRTY<T> = {
    status: "dirty";
    value: T;
};

declare const DIRTY: <T>(value: T) => DIRTY<T>;

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>;

declare const ErrorCode: {
    invalid_type: "invalid_type";
    custom: "custom";
    invalid_string: "invalid_string";
    too_small: "too_small";
    too_big: "too_big";
    required: "required";
};

declare type ErrorData = StripPath<ErrorType> & {
    path?: (string | number)[];
    fatal?: boolean;
};

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;
}

declare type FieldError<T> = {
    [P in AllKeys<T>]?: string;
};

declare type FieldErrors<T, U = string> = {
    [P in AllKeys<T>]?: U[];
};

export declare function Form<T>(props: FormProps<T>): JSX.Element;

export declare type FormActionFn = {
    reset: Reset;
    clear: Clear;
};

export declare type FormCommonProps<T> = {
    values: T;
    error: FieldError<T>;
    isValid: boolean;
    isSubmitting: boolean;
    event: FormEventService;
    validateField: ValidateField<T>;
    registerField: RegisterField<T>;
    getRegisteredField: GetRegisteredField;
};

declare type FormContext<T> = {
    initValues: T;
} & FormCommonProps<T> & FormActionFn;

export declare class FormEventService {
    private clears;
    private resets;
    onClear: (callback: Function) => void;
    clearNotify: () => void;
    onReset: (callback: Function) => () => boolean;
    resetNotify: () => void;
}

export declare type FormProps<T> = {
    innerRef?: React.RefObject<FormRef<T>>;
    initValues: T;
    validation: ValidationProps<T>;
    onSubmit: SubmitCallback<T>;
    children: ReactNode;
} & Omit<FormHTMLAttributes<HTMLFormElement>, 'onChange' | 'onFocus' | 'onSubmit' | 'children'>;

export declare type FormRef<T> = {
    submit: Sumbit<T>;
} & FormCommonProps<T> & FormActionFn;

declare type GetRegisteredField = (props: any) => string | '';

declare type INVALID = {
    status: "aborted";
};

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;
}

declare type OK<T> = {
    status: "valid";
    value: T;
};

declare const OK: <T>(value: T) => OK<T>;

declare type RefinementCtx = {
    addIssue: (arg: ErrorData) => void;
    path: (string | number)[];
};

declare type RefinementEffect<T> = {
    type: "refinement";
    refinement: (arg: T, ctx: RefinementCtx) => any;
};

declare type RegisterField<T> = (name: keyof T) => {
    'field-name': string;
};

declare interface RequiredError extends ValidationBase {
    code: typeof ErrorCode.required;
    params?: {
        [k: string]: any;
    };
}

declare type Reset = () => void;

declare type SchemaError<T> = {
    ok: false;
    validation: Validation<T>;
};

declare enum SchemaKind {
    String = "String",
    Array = "Array",
    Object = "Object",
    Effect = "Effect"
}

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>;
}

declare type SchemaOk<T> = {
    ok: true;
    data: T;
};

declare type SchemaTypeAny = SchemaOf<any, any, any>;

declare interface SchemaTypeDef {
    errorMap?: ErrorMap;
}

declare type SchemaValidation<T> = SchemaOk<T> | SchemaError<T>;

declare type StringValidation = "email" | "url" | "regex" | {
    startWith: string;
} | {
    endWith: string;
};

declare type StripPath<T extends object> = T extends any ? util.OmitKeys<T, "path"> : never;

export declare type SubmitCallback<T> = (data: T, action: FormActionFn) => Promise<void>;

export declare type Sumbit<T> = (callback: SubmitCallback<T>) => Promise<void>;

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";
}

export declare const useForm: <T>(props: FormProps<T>) => UseFormResult<T>;

export declare function useFormContext<T>(): FormContext<T>;

declare type UseFormResult<T> = {
    initValues: T;
    submit: Sumbit<T>;
} & FormCommonProps<T> & FormActionFn;

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 ValidateField<T> = <K extends keyof T>(name: K, value: T[K]) => Promise<ValidateFieldResult>;

export declare type ValidateFieldResult = {
    name: string;
    error: string;
};

declare type ValidateInput = {
    data: any;
    path: (string | number)[];
    parent: ValidationContext;
};

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>;
}

declare type ValidationAsync<T> = Promise<ValidationSync<T>>;

declare type ValidationBase = {
    path: (string | number)[];
    message?: string;
};

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;
}

declare type ValidationError = ErrorType & {
    message: string;
};

declare type ValidationParams = {
    path: (string | number)[];
    errorMap: ErrorMap;
    async: boolean;
};

declare type ValidationPath = ValidationPathComponent[];

declare type ValidationPathComponent = string | number;

declare type ValidationProps<T> = {
    schema: SchemaOf<T>;
    delay?: number;
    initOnMount?: boolean;
};

declare type ValidationResult<T> = ValidationSync<T> | ValidationAsync<T>;

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;
}

declare type ValidationSync<T = any> = OK<T> | DIRTY<T> | INVALID;

export { }
