import { _QSTATE } from '@qundus/qstate';
import { deriveAddon, updateAddon } from '@qundus/qstate/addons';
import { OptionHooksIn } from '@qundus/qstate/hooks';

declare function createProcessors<F extends Field, O extends Options<any>>(props: ProcessorProps<F, O>): {
    readonly checkbox: (checked: any, value: any) => any;
    readonly file: (value: any) => any;
    readonly select: (value: any) => any;
    readonly number: (value: any) => number | null;
};

type FieldStoreObject<F extends Field> = {
    value: F["value"] | undefined;
    condition: FieldCondition;
    errors?: string[];
    extras?: FieldExtras<F["type"]>;
};
type FieldStore<F extends Field, O extends Options<any>> = _QSTATE.StoreDerived<FieldStoreObject<F>, {
    hooks: O["hooks"];
}>;

type FormState<F extends Fields> = _QSTATE.NanoMap<FormObject<F>>;
type FormStore<F extends Fields, O extends Options<any>> = _QSTATE.Store<FormState<F>, {
    hooks: O["hooks"];
    addons: {
        derive: typeof deriveAddon;
        update: typeof updateAddon;
    };
}>;

type FormButtonObject = {
    status: FormStatus;
    disabled: boolean;
    canSubmit: boolean;
    submitting: boolean;
};
type FormButtonStore<F extends Fields, O extends Options<F>> = _QSTATE.StoreDerived<FormButtonObject, {
    hooks: O["hooks"];
}>;
type FormButton<F extends Fields, O extends Options<F>, S extends FormButtonStore<F, O> = FormButtonStore<F, O>> = {
    $store: S;
};

declare const PLACEHOLDERS: {
    readonly select: {
        value: string;
        disabled: boolean;
        selected: boolean;
    };
    readonly selectButton: {
        value: string;
    };
};

type ElementCustomProps<D extends ElementDomType, K extends ElementKeysType> = {
    dType?: D;
    kType?: K;
};
interface Props<F extends Field, O extends Options<any>> {
    derived: FieldStore<F, O>;
}
type FieldElement<F extends Field, O extends Options<any>> = ReturnType<typeof prepareFieldElement<F, O>>;
declare function prepareFieldElement<F extends Field, O extends Options<any>>(props: Props<F, O> & ElementProps<F, O>): {
    readonly dom: <D extends ElementDomType, K extends ElementKeysType>(props?: ElementCustomProps<D, K>) => {
        name: string;
        value: any;
    } & {
        required: boolean;
        disabled: boolean;
    } & {
        onfocus: (event: FocusEvent) => void;
        onblur: (event: FocusEvent) => void;
        autocomplete?: "on" | "off";
    } & {
        oninput: (event: Event) => void;
    };
    readonly preact: <D extends ElementDomType, K extends ElementKeysType>(props?: ElementCustomProps<D, K>) => {
        name: string;
        value: any;
    } & {
        required: boolean;
        disabled: boolean;
    } & {
        onfocus: (event: FocusEvent) => void;
        onblur: (event: FocusEvent) => void;
        autocomplete?: "on" | "off";
    } & {
        oninput: (event: Event) => void;
    };
    readonly react: <D extends ElementDomType, K extends ElementKeysType>(props?: ElementCustomProps<D, K>) => {
        name: string;
        value: any;
    } & {
        required: boolean;
        disabled: boolean;
    } & {
        onfocus: (event: FocusEvent) => void;
        onblur: (event: FocusEvent) => void;
        autocomplete?: "on" | "off";
    } & {
        oninput: (event: Event) => void;
    };
    readonly solid: <D extends ElementDomType, K extends ElementKeysType>(props?: ElementCustomProps<D, K>) => {
        name: string;
        value: any;
    } & {
        required: boolean;
        disabled: boolean;
    } & {
        onfocus: (event: FocusEvent) => void;
        onblur: (event: FocusEvent) => void;
        autocomplete?: "on" | "off";
    } & {
        oninput: (event: Event) => void;
    };
    readonly svelte: {
        name: string;
        value: any;
    } & {
        required: boolean;
        disabled: boolean;
    } & {
        onfocus: (event: FocusEvent) => void;
        onblur: (event: FocusEvent) => void;
        autocomplete?: "on" | "off";
    } & {
        oninput: (event: Event) => void;
    };
    ref: <K extends ElementKeysType>(ref: any, props?: ElementCustomProps<"dom", K>) => void;
};

type FieldAtom<F extends Field, O extends Options<any>> = F extends Field<infer type, infer value> ? {
    $store: FieldStore<F, O>;
    key: string;
    type: type;
    label: string;
    getOptions?: F["options"];
    get element(): FieldElement<F, O>;
    placeholders: typeof PLACEHOLDERS;
    get addValidation(): (func: FieldValidate) => (() => void) | null;
    updateValue: (value: value | ((prev: value) => void), configs?: {
        preprocessValue?: boolean;
    }) => void;
    clearValue: () => void;
    updateCondition: (value: Partial<FieldCondition> | ((prev: Partial<FieldCondition>) => Partial<FieldCondition>)) => void;
} : unknown;

type Atoms<S extends Fields, O extends Options<S>> = {
    [K in keyof S]: FieldAtom<S[K], O>;
};
type AtomsPrepared<S extends Fields, O extends Options<S>> = ReturnType<typeof prepareFormAtoms<S, O>>;
declare function prepareFormAtoms<F extends Fields, O extends Options<F>>(props: {
    fields: F;
    options: O;
    $store: FormStore<F, O>;
}): {
    readonly atoms: <G extends keyof F>(key: G) => Atoms<F, O>[G];
    readonly elements: <G extends keyof F>(key: G) => FieldElement<Field<FieldType, string>, O>;
};

type _Processors<F extends Field, O extends Options<any>> = ReturnType<typeof createProcessors<F, O>>;
type IsUnknown<T> = unknown extends T ? true : false;
type IsUndefined<T> = unknown extends T ? never : undefined extends T ? true : false;
type IsNull<T> = unknown extends T ? never : null extends T ? true : false;
type IsEmpty<T> = unknown extends T ? never : {} extends T ? true : false;
type ElementDomType = "dom" | "vdom";
/**
 * when element props is accessed, usually the returned keys are
 * all that support for html element, this gives user the option
 * to choose which ones are wanted.
 * @default "all";
 */
type ElementKeysType = "base" | "special" | "all";
type ElementReturns<T extends ElementDomType, Base, Dom, VDom> = Base & ("dom" extends T ? Dom : VDom) extends infer G ? G : never;
type FieldType = "checkbox" | "color" | "date" | "datetime-local" | "email" | "file" | "image" | "month" | "number" | "password" | "radio" | "range" | "reset" | "search" | "tel" | "text" | "time" | "url" | "week" | "select";
type FieldValue<T extends FieldType, D> = true extends IsUnknown<D> | IsUndefined<D> | IsNull<D> ? T extends "select" ? string[] : T extends "file" ? FileList : T extends "checkbox" ? boolean : T extends "radio" ? string : T extends "tel" ? number | string : string : D;
/**
 * sometimes some field types require additional information beyond the
 * the standard field "value", this offers that through value processing
 * and places it under FieldState or State
 */
type FieldExtras<T extends FieldType> = T extends "file" ? {
    buffer: string | ArrayBuffer | null | undefined;
    file: File;
    placeholder: string;
    name: string;
}[] : never;
type FieldCondition = {
    valid: boolean;
    hidden: boolean;
    value: {
        updated: boolean;
        lastUpdate: false | "user" | "manual";
        error: false | "validation" | "incomplete" | "optional";
    };
    element: {
        state: false | "focus" | "blur";
        visited: boolean;
        disabled: boolean;
        required: boolean;
    };
};
type FieldErrors = string[] | null | undefined;
type FieldValidate = (value: any, helpers: {
    $values: FormObject<any>["values"];
}) => string | string[] | undefined | null;
/**
 * processor used in case of complex data values
 * need to be extracted from field element and the basic
 * element.value processor isn't accurate enough.
 * some processors run when events like onfocus and onblur fire,
 * this gives the chance to modify field state like required,
 * disabled..etc, according to specific needs or logic.
 */
type FieldProcess<F extends Field, Returns> = (props: Omit<ProcessorProps<F>, "$form"> & {
    value: any;
    getValueOf: (key: string) => any;
    getConditionOf: (key: string) => any;
    $condition: FieldCondition;
    processors: _Processors<F, Options<any>>;
}) => Returns;
type FieldProcessElement<D extends ElementDomType = ElementDomType> = (props: {
    key: string;
    element: Record<string, any>;
    value: FieldStoreObject<any>;
    isVdom: D extends "vdom" ? true : false;
    kType: ElementKeysType;
}) => void;
/**
 * Value Manual Change Mechanism (VMCM), happens when values are updated from api
 * fetch data or just manual programmatic interferrence.
 * this affects whether the updated values go through the proper
 * channels of validation, proccessing and affects on form status or just
 * updates values without affecting anything else.
 * @option normal: value updates go through the proper channeels of validation, proccessing, error handling and condition report.
 * @option bypass: value updates are not validated so no proccessing or error handling, condition is changed though.
 * @option force-valid: value updates defaults fields value condition to valid.
 * @default "normal"
 */
type FieldVMCM = "normal" | "bypass" | "force-valid";
type FieldValidateOn = "input" | "change";
type FieldOptions = (<G>(props?: G) => {
    label: string;
    value: string;
}[]) | null | undefined;
type Field<T extends FieldType = FieldType, V = any> = {
    type: T;
    /** initial value */
    value?: V;
    hidden?: boolean;
    label?: string;
    processValue?: FieldProcess<Field<any>, any> | FieldProcess<Field<any>, any>[];
    /** validate value */
    validate?: FieldValidate | FieldValidate[] | null;
    validateOn?: FieldValidateOn;
    processCondition?: FieldProcess<Field<any>, void>;
    onChange?: (props: {
        $value: FieldStoreObject<Field<any>>;
        form: FormObject<Fields>;
    }) => void;
    required?: boolean;
    disabled?: boolean;
    /** supercedes global options */
    vmcm?: FieldVMCM;
    /**
     * html bare element props passed, this is so preliminary right
     * now and requires extra work for vdom, expect breaking changes here.
     */
    processElement?: FieldProcessElement;
    /**
     * all values go through preprocessing phase, use this to prevent that
     * @default true
     */
    preprocessValue?: boolean;
    /**
     * checks for missing/required value and mark condition.value.error as
     * incomplete.
     * @default true
     */
    incompleteStatus?: boolean;
    /**
     * allowing the field value to be null
     */
    valueNullable?: boolean;
    options?: T extends "select" | "radio" ? FieldOptions : null;
    multiple?: T extends "select" ? boolean : false;
    /**
     * for when a checkbox is mandatory daah
     */
    mandatory?: T extends "checkbox" ? boolean : false;
};
type Basic = null | undefined | Field | FieldType;
type Basics = Record<string, Basic>;
type BasicToField<T extends Basic> = true extends IsUnknown<T> | IsUndefined<T> | IsNull<T> | IsEmpty<T> ? Field<FieldType, string> : T extends FieldType ? Field<T, FieldValue<T, undefined>> : T extends Field ? Field<T["type"], FieldValue<T["type"], T["value"]>> : never;
type Fields<B extends Basics = Basics> = {
    [K in keyof B]: BasicToField<B[K]>;
} extends infer G ? {
    [K in keyof G]: G[K];
} : never;
type FormStatus = "idle" | "incomplete" | "error" | "valid" | "submit";
type FormValues<T extends Fields> = {
    [K in keyof T]: T[K]["value"];
};
type FormErrors<T extends Fields> = undefined | null | {
    [K in keyof T]?: string[];
};
type FormConditions<T extends Fields> = {
    [K in keyof T]: FieldCondition;
};
type FormExtras<T extends Fields> = {
    [K in keyof T as FieldExtras<T[K]["type"]> extends never ? never : K]: FieldExtras<T[K]["type"]>;
};
type FormObject<F extends Fields> = {
    values: FormValues<F>;
    conditions: FormConditions<F>;
    errors: FormErrors<F>;
    extras: FormExtras<F>;
    incomplete: string[];
    status: FormStatus;
} extends infer G ? {
    [K in keyof G]: G[K];
} : never;
type Options<F extends Fields> = {
    hooks?: OptionHooksIn;
    onMount?: (props: {
        init: FormObject<F>;
        update: (values: any) => void | Promise<void>;
    }) => Promise<void> | void;
    onChange?: (props: {
        newValue: FormObject<F>;
        abort: () => void;
    }) => void;
    vmcm?: FieldVMCM;
    /**
     * usually all values are immediatly updated in the state,
     * by setting this to true, only valid values will be commited.
     * @default false
     */
    preventErroredValues?: boolean;
    /**
     * normal behavior of form is to consider all fields required
     * and mark optional ones through "require" setting,
     * this alters that behavior and forces all fields to be optional.
     * the user then has the ability to make required fields through
     * setting "required" to true
     * @default true
     */
    allFieldsRequired?: boolean;
    allFieldsDisabled?: boolean;
    /**
     * global options to optin or out of values preprocessing based on field type.
     * this option precedes individual ones
     */
    preprocessValues?: boolean;
    /**
     * how should the form react to updating values using form.actions.update
     * @default "silent"
     */
    onUpdateKeyNotFound?: "silent" | "warn";
    /**
     * the last step of checking for form validity is to check for
     * required fields' values and add them to the incompleteList,
     * this option allows for choosing how big or small this list goes.
     * @option true collect all incomplete fields
     * @option false don't collect any incomplete fields
     * @option number collect this many of incomplete fields
     * @default false
     */
    incompleteListCount?: boolean | number;
    /**
     * wheather the preferred method of checking values
     * is ran oninput or onchange, oninput checks for validation
     * for every change happens, onchange checks once there's a
     * state change like blur or focus.
     * field specific validateOn takes precedence here.
     * @default input
     */
    validateOn?: FieldValidateOn;
    /**
     * global element processor, gets called before or after field's specific processElement
     * based on processElementOrder option.
     */
    processElement?: FieldProcessElement;
    /**
     * determines wheather global processElement is going to take place before or after
     * field's specific processElement
     * @default "after"
     */
    processElementOrder?: "before" | "after";
    /**
     * if the desired data extracted from the form is in the
     * shape of a nested object, then this splitter is used
     * to determine which keys are nested, for now only
     * the dot is supported.
     * @default '.'
     */
    flatObjectKeysChar?: ".";
    /**
     * when no label is provided, the key is used as fallback but sometimes
     * the key is meant to be unflattened later on when fetching the form data,
     * so this offers a way to replace the key flatObjectKeysChar with any
     * character.
     * @default ' ' or empty spcace
     */
    flatLabelJoinChar?: string;
};
type OptionsMerged<G extends Options<any>, D extends Options<any>> = D & G;
type InteractionProps<F extends Field, O extends Options<any>> = {
    key: string;
    field: F;
    options: O;
    event: Event | undefined | null;
    value: any;
    preprocessValue?: boolean;
    $form: FormObject<any>;
};
type ProcessorProps<F extends Field, O extends Options<any> = Options<any>> = {
    key: string;
    field: F;
    event: Event | undefined | null;
    manualUpdate: boolean;
    $form: FormObject<any>;
};
type PluginProps<F extends Fields, O extends Options<F>> = {
    fields: F;
    options: O;
    $store: FormStore<F, O>;
};
type ElementProps<F extends Field, O extends Options<any>> = {
    key: string;
    field: F;
    options: O;
    $store: FormStore<any, O>;
};

type _model_Atoms<S extends Fields, O extends Options<S>> = Atoms<S, O>;
type _model_AtomsPrepared<S extends Fields, O extends Options<S>> = AtomsPrepared<S, O>;
type _model_Basic = Basic;
type _model_BasicToField<T extends Basic> = BasicToField<T>;
type _model_Basics = Basics;
type _model_ElementDomType = ElementDomType;
type _model_ElementKeysType = ElementKeysType;
type _model_ElementProps<F extends Field, O extends Options<any>> = ElementProps<F, O>;
type _model_ElementReturns<T extends ElementDomType, Base, Dom, VDom> = ElementReturns<T, Base, Dom, VDom>;
type _model_Field<T extends FieldType = FieldType, V = any> = Field<T, V>;
type _model_FieldAtom<F extends Field, O extends Options<any>> = FieldAtom<F, O>;
type _model_FieldCondition = FieldCondition;
type _model_FieldErrors = FieldErrors;
type _model_FieldExtras<T extends FieldType> = FieldExtras<T>;
type _model_FieldOptions = FieldOptions;
type _model_FieldProcess<F extends Field, Returns> = FieldProcess<F, Returns>;
type _model_FieldProcessElement<D extends ElementDomType = ElementDomType> = FieldProcessElement<D>;
type _model_FieldStore<F extends Field, O extends Options<any>> = FieldStore<F, O>;
type _model_FieldStoreObject<F extends Field> = FieldStoreObject<F>;
type _model_FieldType = FieldType;
type _model_FieldVMCM = FieldVMCM;
type _model_FieldValidate = FieldValidate;
type _model_FieldValidateOn = FieldValidateOn;
type _model_FieldValue<T extends FieldType, D> = FieldValue<T, D>;
type _model_Fields<B extends Basics = Basics> = Fields<B>;
type _model_FormButton<F extends Fields, O extends Options<F>, S extends FormButtonStore<F, O> = FormButtonStore<F, O>> = FormButton<F, O, S>;
type _model_FormButtonObject = FormButtonObject;
type _model_FormButtonStore<F extends Fields, O extends Options<F>> = FormButtonStore<F, O>;
type _model_FormConditions<T extends Fields> = FormConditions<T>;
type _model_FormErrors<T extends Fields> = FormErrors<T>;
type _model_FormExtras<T extends Fields> = FormExtras<T>;
type _model_FormObject<F extends Fields> = FormObject<F>;
type _model_FormState<F extends Fields> = FormState<F>;
type _model_FormStatus = FormStatus;
type _model_FormStore<F extends Fields, O extends Options<any>> = FormStore<F, O>;
type _model_FormValues<T extends Fields> = FormValues<T>;
type _model_InteractionProps<F extends Field, O extends Options<any>> = InteractionProps<F, O>;
type _model_IsEmpty<T> = IsEmpty<T>;
type _model_IsNull<T> = IsNull<T>;
type _model_IsUndefined<T> = IsUndefined<T>;
type _model_IsUnknown<T> = IsUnknown<T>;
type _model_Options<F extends Fields> = Options<F>;
type _model_OptionsMerged<G extends Options<any>, D extends Options<any>> = OptionsMerged<G, D>;
type _model_PluginProps<F extends Fields, O extends Options<F>> = PluginProps<F, O>;
type _model_ProcessorProps<F extends Field, O extends Options<any> = Options<any>> = ProcessorProps<F, O>;
type _model__Processors<F extends Field, O extends Options<any>> = _Processors<F, O>;
declare namespace _model {
  export type { _model_Atoms as Atoms, _model_AtomsPrepared as AtomsPrepared, _model_Basic as Basic, _model_BasicToField as BasicToField, _model_Basics as Basics, _model_ElementDomType as ElementDomType, _model_ElementKeysType as ElementKeysType, _model_ElementProps as ElementProps, _model_ElementReturns as ElementReturns, _model_Field as Field, _model_FieldAtom as FieldAtom, _model_FieldCondition as FieldCondition, _model_FieldErrors as FieldErrors, _model_FieldExtras as FieldExtras, _model_FieldOptions as FieldOptions, _model_FieldProcess as FieldProcess, _model_FieldProcessElement as FieldProcessElement, _model_FieldStore as FieldStore, _model_FieldStoreObject as FieldStoreObject, _model_FieldType as FieldType, _model_FieldVMCM as FieldVMCM, _model_FieldValidate as FieldValidate, _model_FieldValidateOn as FieldValidateOn, _model_FieldValue as FieldValue, _model_Fields as Fields, _model_FormButton as FormButton, _model_FormButtonObject as FormButtonObject, _model_FormButtonStore as FormButtonStore, _model_FormConditions as FormConditions, _model_FormErrors as FormErrors, _model_FormExtras as FormExtras, _model_FormObject as FormObject, _model_FormState as FormState, _model_FormStatus as FormStatus, _model_FormStore as FormStore, _model_FormValues as FormValues, _model_InteractionProps as InteractionProps, _model_IsEmpty as IsEmpty, _model_IsNull as IsNull, _model_IsUndefined as IsUndefined, _model_IsUnknown as IsUnknown, _model_Options as Options, _model_OptionsMerged as OptionsMerged, _model_PluginProps as PluginProps, _model_ProcessorProps as ProcessorProps, _model__Processors as _Processors };
}

export { type AtomsPrepared as A, type Basics as B, type Fields as F, type Options as O, type PluginProps as P, _model as _, type FieldCondition as a, type FormButton as b, PLACEHOLDERS as c, type FormObject as d, type OptionsMerged as e, type FieldType as f, type Field as g };
