import { TEnum } from './util';
import { TFormatError, TOnError } from './error-stuff';
type TStaticObj<Prop> = string extends keyof Prop ? never : {
    [key: string]: string | number | boolean | TStaticObj<Prop>;
};
type TConvertInterfaceToType<Prop> = {
    [K in keyof Prop]: Prop[K];
};
type IsStaticObj<P, Prop = NonNullable<P>> = (TConvertInterfaceToType<Prop> extends TStaticObj<Prop> ? true : false);
type NotUndef<T> = Exclude<T, undefined>;
type AddNullablesHelper<T, isN> = isN extends true ? NonNullable<T> | null : NonNullable<T>;
type AddNullables<T, isU, isN> = (isU extends true ? AddNullablesHelper<NotUndef<T>, isN> | undefined : AddNullablesHelper<NotUndef<T>, isN>);
type GetTypePredicate<T> = T extends (x: unknown) => x is infer U ? U : never;
export interface IValidatorObj<T = unknown> {
    vf: TValidatorFn<T>;
    default?: T;
    transform?: (arg: unknown) => T;
    formatError?: TFormatError;
}
type TValidatorFn<T = unknown> = (arg: unknown) => arg is T;
type IValidatorFnOrObj<T> = TValidatorFn<T> | IValidatorObj<T>;
type TPickRetVal<T, NnT = NonNullable<T>> = {
    test: (arg: unknown) => arg is T;
    default: () => T;
} & (IsStaticObj<T> extends true ? {
    pick: <K extends keyof NnT>(prop: K) => TPickRetVal<NnT[K]>;
    new: (arg?: Partial<NonNullable<T>>) => NonNullable<T>;
    schema: () => ISchema<T>;
} : unknown);
export interface ISchema<T = unknown> {
    new: (arg?: Partial<NonNullable<T>>) => NonNullable<T>;
    test: (arg: unknown) => arg is T;
    pick: <K extends keyof T>(prop: K) => TPickRetVal<T[K]>;
    parse: (arg: unknown) => NonNullable<T>;
    _schemaOptions: {
        optional: boolean;
        nullable: boolean;
        init: boolean | null;
        id?: string;
        safety?: 'pass' | 'filter' | 'strict';
    };
}
export type TSchemaFnObjArg<T> = Required<{
    [K in keyof T]: (T[K] extends (string | number) ? (IValidatorFnOrObj<T[K]> | TEnum) : T[K] extends Date ? (DateConstructor | IValidatorFnOrObj<T[K]>) : IsStaticObj<T[K]> extends true ? ISchema<T[K]> : IValidatorFnOrObj<T[K]>);
}>;
interface IJetOptions<M> {
    globals?: M extends IValidatorObj[] ? M : never;
    cloneFn?: (value: unknown) => unknown;
    onError?: TOnError;
}
type TGlobalsArr<M> = {
    [K in keyof M]: {
        vf: TValidatorFn;
    } & ('vf' extends keyof M[K] ? {
        default?: GetTypePredicate<M[K]['vf']>;
        transform?: (arg: unknown) => GetTypePredicate<M[K]['vf']>;
        formatError?: TFormatError;
    } : never);
};
type TSchemaOptions<T = unknown> = (unknown extends T ? (IOptNul | IOptNotNul | INotOptButNul | INotOptOrNul | INullish) : (undefined extends T ? (null extends T ? (IOptNul | INullish) : IOptNotNul) : (null extends T ? INotOptButNul : INotOptOrNul)));
interface ISchemaOptionsBase {
    id?: string;
    safety?: 'pass' | 'filter' | 'strict';
}
export interface IOptNul extends ISchemaOptionsBase {
    optional: true;
    nullable: true;
    init?: null | boolean;
    nullish?: undefined;
}
export interface IOptNotNul extends ISchemaOptionsBase {
    optional: true;
    nullable?: false;
    init?: boolean;
}
export interface INotOptButNul extends ISchemaOptionsBase {
    optional?: false;
    nullable: true;
    init?: null | true;
}
export interface INotOptOrNul extends ISchemaOptionsBase {
    optional?: false;
    nullable?: false;
    init?: true;
}
export interface INullish extends ISchemaOptionsBase {
    nullish: true;
    optional?: undefined;
    nullable?: undefined;
    init?: null | boolean;
}
type TSchemaOptionsHelper<T, R> = (unknown extends T ? ([] | [R]) : undefined extends T ? [R] : null extends T ? [R] : ([] | [R]));
export type PublicInferType<S> = (S extends ISchema<infer X> ? X : never);
type InferTypes<U, R, Schema = MakeKeysOptIfUndef<InferTypesHelper<U>>> = ('nullish' extends keyof R ? AddNullables<Schema, true, true> : AddNullables<Schema, 'optional' extends keyof R ? R['optional'] : false, 'nullable' extends keyof R ? R['nullable'] : false>);
type MakeKeysOptIfUndef<T> = {
    [K in keyof T]-?: (x: undefined extends T[K] ? Partial<Record<K, T[K]>> : Record<K, T[K]>) => void;
}[keyof T] extends (x: infer I) => void ? I extends infer U ? {
    [K in keyof U]: U[K];
} : never : never;
type InferTypesHelper<U> = {
    [K in keyof U]: (U[K] extends DateConstructor ? Date : U[K] extends IValidatorFnOrObj<infer X> ? X : U[K] extends ISchema<infer X> ? X : U[K] extends unknown[] ? never : U[K] extends TEnum ? U[K][keyof U[K]] : never);
};
declare function jetSchema<M extends TGlobalsArr<M>>(options?: IJetOptions<M>): <T, U extends TSchemaFnObjArg<T> = Required<{ [K in keyof T]: T[K] extends string | number ? TEnum | IValidatorFnOrObj<T[K]> : T[K] extends Date ? DateConstructor | IValidatorFnOrObj<T[K]> : IsStaticObj<T[K], NonNullable<T[K]>> extends true ? ISchema<T[K]> : IValidatorFnOrObj<T[K]>; }>, R extends TSchemaOptions<T> = TSchemaOptions<T>>(schemaFnObjArg: U, ...options: TSchemaOptionsHelper<T, R>) => unknown extends T ? ISchema<InferTypes<U, R>> : ISchema<T>;
export default jetSchema;
