import { forwardRef, type PropsWithChildren, useEffect, useImperativeHandle } from 'react';
import { type FieldValues, FormProvider, useForm } from 'react-hook-form';

import type { FormerProps, FormerRef } from './Former.types';

import { FormerProvider, useFormerStore } from './Former.store';
import { FormerLayout } from './FormerLayout';

const FormerInner = forwardRef<FormerRef<any>, Partial<FormerProps<any>> & PropsWithChildren>(
  function FormerInner<T extends FieldValues>(
    props: Partial<FormerProps<T>> & PropsWithChildren<T>,
    ref: any
  ) {
    const {
      getState,
      onChange,
      onClose,
      onOpen,
      opened,
      primeData,
      reset,
      save,
      setState,
      useFormProps,
      validate,
      values,
      wrapper,
    } = useFormerStore((state) => ({
      getState: state.getState,
      onChange: state.onChange,
      onClose: state.onClose,
      onOpen: state.onOpen,
      opened: state.opened,
      primeData: state.primeData,
      reset: state.reset,
      save: state.save,
      setState: state.setState,
      useFormProps: state.useFormProps,
      validate: state.validate,
      values: state.values,
      wrapper: state.wrapper,
    }));

    const formMethods = useForm<T>({
      defaultValues: primeData,
      mode: 'all',
      shouldUseNativeValidation: true,
      values: values,
      ...useFormProps,
    });

    useImperativeHandle(
      ref,
      () => ({
        close: async () => {
          //console.log('close called');
          onClose?.();
          setState('opened', false);
        },
        getValue: () => {
          return getState('values');
        },
        reset: () => {
          reset();
        },
        save: async () => {
          return await save();
        },
        setValue: (value: T) => {
          onChange?.(value);
        },
        show: async () => {
          //console.log('show called');
          setState('opened', true);
          onOpen?.();
        },
        validate: async () => {
          return await validate();
        },
      }),
      [getState, onChange, validate, save, reset, setState, onClose, onOpen]
    );

    useEffect(() => {
      setState('getFormMethods', () => formMethods);

      if (formMethods) {
        formMethods.subscribe({
          callback: ({ isDirty }) => {
            setState('dirty', isDirty);
          },
          formState: { isDirty: true },
        });
      }
    }, [formMethods]);

    return (
      <FormProvider {...formMethods}>
        {typeof wrapper === 'function' ? (
          wrapper(<FormerLayout>{props.children}</FormerLayout>, opened, onClose, onOpen, getState)
        ) : (
          <FormerLayout>{props.children || null}</FormerLayout>
        )}
      </FormProvider>
    );
  }
);

export const Former = forwardRef<FormerRef<any>, FormerProps<any> & PropsWithChildren>(
  function Former<T extends FieldValues = any>(
    props: FormerProps<T> & PropsWithChildren<T>,
    ref: any
  ) {
    //if opened is false and wrapper is defined as function, do not render anything
    if (!props.opened && typeof props.wrapper === 'function') {
      return null;
    }
    return (
      <FormerProvider {...props}>
        <FormerInner ref={ref}>{props.children}</FormerInner>
      </FormerProvider>
    );
  }
);
