import * as react from 'react';
import { PropsWithChildren, ReactElement, ElementType, ReactNode } from 'react';
import { TEventProps, TFieldEvent, TEventDataProps, TFormValues, TFormValidationPayload, TFormEntry, IComponentSchemaAsFormField, IComponentSchema, OneOf, TMapper, TFormGroupOnDataEventPayload, FormCore, TFormGroup, TSchemaFormConfig, FormField, TFormGroupOnValidEventPayload, TFormGroupOnSubmitEventPayload, TValueChangeEvent } from '@bolttech/form-engine-core';

/**
 * Represents a field wrapper containing the name of the field and its index in the form.
 *
 * @property {string} name - The name of the field.
 * @property {string} formIndex - The index of the field within the form.
 */
type TFieldWrapper = {
    name: string;
    formIndex: string;
};
/**
 * Represents the content inside onData payload action.
 */
type TFormData<T> = {
    field: string;
    data: TFormValues<T>;
};
/**
 *
 * @property {(payload: TFormValues<T>) => void} onFormMount - triggered when form mounts
 * @property {(payload: TFormValues<T>) => void} onData - triggered when any field change value
 * @property {(payload: TFormValues<T>) => void} onSubmit - triggered when submit button is pressed
 * @property {(payload: TFormValues<T>) => void} onValid - triggered when form valid status changes
 * @interface
 *
 */
type TEventsCallbackProps<T> = Partial<Record<TEventProps, ((payload: TFieldEvent) => void) | null | undefined>> & Partial<Record<Extract<TEventDataProps, 'onFormMount' | 'onSubmit'>, (payload: TFormValues<T>) => void>> & Partial<Record<Extract<TEventDataProps, 'onData'>, (payload: {
    field: string;
    data: TFormValues<T>;
}) => void>> & Partial<Record<Extract<TEventDataProps, 'onValid'>, (payload: TFormValidationPayload) => void>>;

/**
 * Form props, inherits the form instance constructor implementation except the mapper
 * along with all event callback register props shared with other implementations
 * @see {@link TFormEntry}
 * @see {@link TEventsCallbackProps}
 * @interface
 * @example
 * ```typescript
 * import { Form } from '@bolttech/form-engine';
 *
 * <Form index={'foo'} schema={schema} onData={console.log} />
 * ```
 */
type TFormProps<T> = Omit<TFormEntry, 'mappers' | 'dataSubject$' | 'formValidSubject$' | 'submitSubject$'> & TEventsCallbackProps<T>;

/**
 *
 * @param {TFormProps} param form properties initializor
 * @returns {ReactElement}
 */
declare function Form<T>({ schema, index, initialValues, iVars, action, method, config, prefetchedData, onSubmit, onFormMount, onData, onBlur, onChange, onApiResponse, onClick, onFocus, onKeyDown, onKeyUp, onMount, onValid, stopEventsOnSubmit, children, }: PropsWithChildren<TFormProps<T>>): ReactElement;

/**
 * AsFormField props, inherits all schema field implementation except the children
 * property and has mappers property to pass a custom component, that will be a ReactNode
 * @see {@link IComponentSchemaAsFormField}
 */
type TAsFormFieldProps = PropsWithChildren<Omit<IComponentSchemaAsFormField<ElementType>, 'children'>>;

/**
 * Component wrapper to aid building schemas with react without writting a JSON schema
 * along with BuildAsFormFieldTree inside a Form component, FieldWrapper gets this props
 * to build the component as it does with a JSON schema, this component only works inside
 * the Form component
 *
 * @param {TAsFormFieldProps} props JSON schema props
 * @returns {ReactNode}
 */
declare const AsFormField: (props: TAsFormFieldProps) => ReactNode;

/**
 * AsFormField props, inherits all schema field implementation except the children
 * property, that will be a ReactNode
 * also gets the formIndex for form identification and the mapper to build the component
 * @property {TMapper} mapper element mapper to use
 * @property {boolean} visibility element controlled visibility
 * @property {(data: TFieldEvent) => void} onSelected element callback on selected option
 * @see {@link TMapper}
 * @see {@link IComponentSchema}
 * @see {@link TFieldWrapper}
 * @interface
 */
type TAsFormFieldBuilderProps = PropsWithChildren<Omit<IComponentSchema, 'component' | 'children' | 'name'> & Required<TFieldWrapper> & {
    onSelected?: (data: TFieldEvent) => void;
    formMounted?: boolean;
} & OneOf<{
    mapper: TMapper<ElementType>;
    component: string;
}>>;

/**
 * Component Wrapper to render form fields without the Form component, gets additional formId and mapper since
 * it won't rely on them and needs to be manually declared
 *
 * @param {TAsFormFieldBuilderProps} props JSON schema props along with FieldWrapper props and mapper props
 * @returns {ReactElement}
 */
declare const AsFormFieldBuilder: (props: TAsFormFieldBuilderProps) => ReactElement;

type TRepeaterProps = {
    formIndex: string;
    index: number;
};
type TRepeaterFooterProps = Pick<TRepeaterProps, 'formIndex'>;
/**
 * AsFormFieldRepeater props needed to build a repeater to build forms
 * @property {ElementType<TRepeaterProps>} RepeaterComponent element to use as repeater
 * @property {string} addFieldName field name button that will add an element on a position on a repeater
 * @property {string} removeFieldName field name button that will remove an element on a position on a repeater
 * @property {Record<string, unknown>[]} existingElements elements to restore a repeater list
 * @property {number} initialElements elements to be pre-rendered when the form is presented
 * @property {(payload: TFormGroupOnDataEventPayload<unknown>) => void} stateUpdater similar to onData for the group of form managed by the repeater
 * @property {string} formPrefix prefix to use on the repeated forms ex: (person- will go person-1, person-2, ...)
 * @property {ElementType<TRepeaterFooterProps>} RepeaterFooter component with a button that will add an element to the bottom of the list
 * @see {@link TMapper}
 * @see {@link IComponentSchema}
 * @see {@link TFieldWrapper}
 * @interface
 * @example
 * ```typescript
 * import { AsFormFieldRepeater } from '@bolttech/form-engine';
 *
 * <AsFormFieldRepeater
 *   RepeaterComponent={FormElement}
 *   RepeaterFooter={FormElementFooter}
 *   addFieldName="addForm"
 *   removeFieldName="removeForm"
 *   formPrefix="insured"
 *   stateUpdater={(payload) => {
 *     console.log(payload);
 *   }}
 * />
 * ```
 */
type TAsFormFieldRepeaterProps = {
    RepeaterComponent: ElementType<TRepeaterProps>;
    addFieldName?: string;
    removeFieldName?: string;
    stateUpdater?: (payload: TFormGroupOnDataEventPayload<unknown>) => void;
    formPrefix?: string;
    RepeaterFooter: ElementType<TRepeaterFooterProps>;
} & OneOf<{
    existingElements?: Record<string, unknown>[];
    initialElements?: number;
}>;

/**
 * Adapter do manage repeated list elements on form
 *
 * @param {TAsFormFieldRepeaterProps} props Repeater properties to configure the elements repeater
 * @returns {ReactElement}
 */
declare const AsFormFieldRepeater: ({ RepeaterComponent, addFieldName, removeFieldName, existingElements, initialElements, stateUpdater, formPrefix, RepeaterFooter, }: TAsFormFieldRepeaterProps) => ReactNode;

/**
 * Represents the context for managing forms within a form group.
 *
 * @property {function(string): void} addFormWithIndex - Adds a form to the form group by its index.
 * @property {function({ key: string, formInstance: TFormCore }): void} addForm - Adds a form to the form group using a payload containing the key and form instance.
 * @property {function({ key: string }): (FormCore | undefined)} getForm - Retrieves a form from the form group using a payload containing the key.
 * @property {function({ key: string }): void} removeForm - Removes a form from the form group using a payload containing the key.
 * @property {TFormGroup} formGroupInstance - The instance of the form group.
 * @property {TMapper<ElementType>[]} [mappers] - Optional array of mappers for elements.
 * @property {function(): void} printFormGroupInstance - Prints the form group instance.
 * @property {function(string[]): TFormValues} submitMultipleFormsByIndex - Submits multiple forms by their indexes and returns the form values.
 * @property {boolean} debugMode - Indicates if debug mode is active.
 * @property {boolean} active - Indicates if the form context is active (if the context provider is defined above).
 *
 * @example
 * ```typescript
 * import React from 'react';
 * import { useFormGroupContext } from '@bolttech/form-engine';
 *
 * const FormComponent = (): React.ReactElement => {
 *   const {
 *     addForm,
 *     removeForm,
 *     getForm,
 *     printFormGroupInstance,
 *     submitMultipleFormsByIndex,
 *     debugMode
 *   } = useFormGroupContext();
 *
 *   // (...)
 * };
 * ```
 *
 */
type TFormContext = {
    addFormWithIndex: (index: string) => void;
    addForm: (payload: {
        key: string;
        params: TFormEntry;
    }) => void;
    getForm: (payload: {
        key: string;
    }) => FormCore | undefined;
    removeForm: (payload: {
        key: string;
    }) => void;
    formGroupInstance: TFormGroup<ElementType>;
    mappers?: TMapper<ElementType>[];
    printFormGroupInstance: () => void;
    submitMultipleFormsByIndex: <T>(indexes: string[], callback?: (payload: TFormValues<T>) => void) => void;
    debugMode: boolean;
    active: boolean;
    config?: TSchemaFormConfig;
};
/**
 * Represents the props for a form context provider.
 *
 * @property {TMapper<ElementType>[]} [mappers] - Optional array of mappers for elements.
 * @property {boolean} [debugMode] - Optional flag indicating if debug mode should be enabled.
 * @property {TSchemaFormConfig} config - Optional config settings for debounce times and client logging
 *
 * * @example
 * ```tsx
 * import { FormGroupContextProvider } from '@bolttech/form-engine';
 *
 * <FormGroupContextProvider mappers={mappers} debugMode={true}>
 *   {children}
 * </FormGroupContextProvider>
 * ```
 */
type TFormContextProvider = {
    mappers?: TMapper<ElementType>[];
    debugMode?: boolean;
    config?: TSchemaFormConfig;
};

declare const FormGroupContext: react.Context<TFormContext>;
/**
 * context provider to wrap form-engine adapter elements
 *
 * @param {PropsWithChildren<TFormContextProvider>} param context parameters
 * @returns {ReactElement}
 */
declare const FormGroupContextProvider: ({ children, mappers, debugMode, config, }: PropsWithChildren<TFormContextProvider>) => ReactElement;
/**
 * FormGroup context hook to handle context or isolated context implementations
 *
 * @param {TFormContextProvider} props form group context parameters
 * @returns {TFormContext}
 */
declare const useFormGroupContext: (props?: TFormContextProvider) => TFormContext;

/**
 * Represents the props for a field wrapper component, including children.
 *
 * @property {Record<string, unknown>} [props] - Additional properties for the field.
 * @property {TFormContext | null} [context] - The context of the form, which may be null.
 * @property {React.ReactNode} children - The child elements of the component.
 * @see {@link TFieldWrapper}
 */
type TFieldWrapperProps = PropsWithChildren<TFieldWrapper & {
    props?: Record<string, unknown>;
    context?: TFormContext | null;
    mounted?: boolean;
    mapper?: TMapper<ElementType>;
    component?: string;
    visibility?: boolean;
}>;
/**
 * Represents the props for rendering a field wrapper component, including children.
 *
 * @property {Record<string, unknown>} props - Additional properties for the field.
 * @property {FormField} [fieldInstance] - The instance of the form field, which may be undefined.
 * @property {React.ReactNode} children - The child elements of the component.
 */
type TFieldWrapperComponentRenderProps = PropsWithChildren<{
    props: Record<string, unknown>;
    fieldInstance?: FormField;
    mapper?: TMapper<ElementType>;
}>;

/**
 * Represents the properties for the useForm hook, including an ID and event callback properties.
 *
 * @property {string} id - The unique identifier for the form. Deprecated, use 'index' instead
 * @property {string} index - The unique identifier for the form.
 *
 * @see {@link TEventsCallbackProps}
 * @interface
 */
type TUseFormProps<T> = Pick<TFormEntry, 'index' | 'initialValues' | 'iVars' | 'stopEventsOnSubmit'> & TEventsCallbackProps<T> & OneOf<{
    id: string;
    index: string;
}>;

/**
 * Hook to register events callback functions
 */
declare function useForm<T>({ id, index, onData, onSubmit, onFormMount, onValid, iVars, initialValues, stopEventsOnSubmit, ...rest }: TUseFormProps<T>, deps?: React.DependencyList): void;

/**
 * Represents the properties for the useFormGroup hook, including a list of ID's and event callback properties.
 *
 * @property {string} ids - The unique identifier for the forms.
 * @property {payload: TFormGroupOnDataEventPayload<T>} onData - callback event occurring when a value is changing via input or logic.
 * @property {payload: TFormGroupOnValidEventPayload} onData - callback event occurring when validation status changes on the form.
 * @property {payload: TFormGroupOnSubmitEventPayload<T>} onData - event occurring when form submission is trigger.
 *
 */
type TUseFormGroup<T> = {
    ids: string[];
    onData?: (payload: TFormGroupOnDataEventPayload<T>) => void;
    onValid?: (payload: TFormGroupOnValidEventPayload) => void;
    onSubmit?: (payload: TFormGroupOnSubmitEventPayload<T>) => void;
};

declare function useFormGroup<T>({ ids, onData, onValid, onSubmit }: TUseFormGroup<T>, deps?: React.DependencyList): void;

declare const defaultChangeEvent: TValueChangeEvent;
declare const checkedChangeEvent: TValueChangeEvent;
declare const valueChangeEvent: TValueChangeEvent;
declare const numberInputChangeEvent: TValueChangeEvent;
declare const datepickerChangeEvent: TValueChangeEvent;
declare const dropdownChangeEvent: TValueChangeEvent;

export { AsFormField, AsFormFieldBuilder, AsFormFieldRepeater, Form, FormGroupContext, FormGroupContextProvider, checkedChangeEvent, datepickerChangeEvent, defaultChangeEvent, dropdownChangeEvent, numberInputChangeEvent, useForm, useFormGroup, useFormGroupContext, valueChangeEvent };
export type { TAsFormFieldBuilderProps, TAsFormFieldProps, TAsFormFieldRepeaterProps, TEventsCallbackProps, TFieldWrapper, TFieldWrapperComponentRenderProps, TFieldWrapperProps, TFormContext, TFormContextProvider, TFormData, TFormProps, TRepeaterFooterProps, TRepeaterProps };
