import { PropsWithChildren, useMemo, useRef } from 'react';
import { useForm, FieldValues, FormProvider } from 'react-hook-form';
import { DevTool } from '@hookform/devtools';
import { createFormDispatch, FormDispatch, FormDispatchContext } from '@/hooks/useFormDispatch';
import { DisableClearErrorsOnUnmountContext, DisableRequiredRulesContext } from './context';
import type { FormProps } from './types';
import { DisabledContext } from '@/commons/hocs/withDisabled';
import { FieldNamePathContext } from '../FieldNamePathProvider';

/**
 * Компонент обёртка над формой, которая будет провайдером состояния формы для вложенных элементов.
 *
 * Использует в себе хук useForm из react-hook-form и передаёт возвращенные из него methods в FormProvider,
 * которые можно получить во вложенных компонентах с помощью useFormContext.
 */
export const Form = <
  FormValues extends FieldValues = FieldValues,
  ExtraArgs extends Record<string, unknown> = Record<string, unknown>,
>({
  mode = 'all',
  criteriaMode = 'all',
  onSubmit = () => undefined,
  onSubmitFailed,
  children,
  disableRequiredRules = false,
  debug,
  extraArgs,
  disabled,
  formStyleProps,
  disableClearErrorsOnUnmount = false,
  ...otherUseFormProps
}: PropsWithChildren<FormProps<FormValues, ExtraArgs>>) => {
  const methods = useForm<FormValues>({ mode, criteriaMode, ...otherUseFormProps });
  const formRef = useRef<HTMLFormElement>(null);

  const {
    setValue,
    setError,
    getValues,
    clearErrors,
    trigger,
    reset,
    getFieldState,
    resetField,
    formState: { isSubmitting },
  } = methods;

  const formDispatch = useMemo(
    () =>
      createFormDispatch<FormValues>(
        {
          setValue,
          setError,
          getValues,
          clearErrors,
          trigger,
          reset,
          getFieldState,
          resetField,
          submit: () => formRef.current?.requestSubmit?.(),
        },
        { ...extraArgs },
      ),
    [setValue, setError, getValues, clearErrors, trigger, reset, getFieldState, resetField, extraArgs],
  );

  const keyDownHandler = (e: React.KeyboardEvent<HTMLFormElement>) => {
    /* Проверка на instanceof сделана для того, чтобы событие переноса
     на новую строку в textarea не сбрасывалось на e.preventDefault() */
    if (e.key === 'Enter' && !(e.target instanceof HTMLTextAreaElement)) {
      e.preventDefault();
    }
  };

  return (
    <FormProvider {...methods}>
      <FormDispatchContext.Provider value={formDispatch as FormDispatch}>
        <DisableRequiredRulesContext.Provider value={disableRequiredRules}>
          <DisabledContext.Provider value={Boolean(disabled || isSubmitting)}>
            <DisableClearErrorsOnUnmountContext.Provider value={disableClearErrorsOnUnmount}>
              <FieldNamePathContext.Provider value="">
                <form
                  ref={formRef}
                  onKeyDown={e => keyDownHandler(e)}
                  onSubmit={methods.handleSubmit(onSubmit, onSubmitFailed)}
                  style={formStyleProps}
                >
                  {children}
                </form>

                {debug && <DevTool control={methods.control} />}
              </FieldNamePathContext.Provider>
            </DisableClearErrorsOnUnmountContext.Provider>
          </DisabledContext.Provider>
        </DisableRequiredRulesContext.Provider>
      </FormDispatchContext.Provider>
    </FormProvider>
  );
}; test('should call useForm hook with exact props', () => {
      const mockUseForm = jest.spyOn(RHF, 'useForm');
      render(<Form {...MOCK_USE_FORM_PROPS} />);
      // expect(mockUseForm).toHaveBeenNthCalledWith(1, MOCK_USE_FORM_PROPS);
      expect(mockUseForm).toHaveBeenCalled();
    });test failed why