import { type FC, type RefObject, useEffect, useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { useMutation, useQueryClient } from 'react-query';
import { useNavigate, useParams } from 'react-router-dom';

import { type AxiosResponse } from 'axios';
import { differenceInSeconds } from 'date-fns';
import { styled, useTheme } from '@material-hu/mui/styles';
import Typography from '@material-hu/mui/Typography';

import useSnackbar from '@material-hu/components/design-system/Snackbar';

import { useAuth } from 'src/contexts/JWTContext';
import { useStackStepForms } from 'src/contexts/StackStepFormsContext';
import useGeneralError from 'src/hooks/useGeneralError';
import { useFormFileUpload } from 'src/pages/dashboard/forms/FormFileUploadContext';
import { createForm, editForm } from 'src/services/forms';
import {
  type FormAttributes,
  type FormItemResponseCompleted,
  FormSkipTypes,
  type FormSubmitData,
} from 'src/types/forms';
import {
  buildStepItem,
  checkEmptyForm,
  checkEmptyObject,
  findStepToGo,
  formatValues,
  getDestinationStepIds,
  getFilteredValues,
  getQuestionName,
  hasMandatory,
  isFormReadyToSubmit,
  logAmplitudeEditEvent,
  logAmplitudeEvents,
  resetValuesGoBack,
} from 'src/utils/form';
import { useLokaliseTranslation } from 'src/utils/i18n';

import AcceptCancelDialog from 'src/components/AcceptCancelDialog';
import FormContent from 'src/components/dashboard/form/formDetail/FormContent';
import FormHeader from 'src/components/dashboard/form/formDetail/FormHeader';
import FormSubmit from 'src/components/dashboard/form/formDetail/FormSubmit';
import {
  addStepPreSubmit,
  deleteFormWithSectionBracing,
  formKeys,
} from 'src/components/dashboard/form/queries';
import { formRoutes } from 'src/components/dashboard/form/routes';
import GoBackButton from 'src/components/dashboard/GoBackButton';

import useEdemsaVLookupForm from './edemsa/useEdemsaVLookupForm';
import { type EdemsaProfileValue } from './edemsa/types';
import { isEdemsaSpecialField } from './edemsa/utils';
import usePanalabSpecialForm from './panalab/usePanalabSpecialForm';
import {
  isViaticumAmountField,
  isViaticumTotalAmountField,
} from './panalab/utils';

export type FormProps = {
  formDetailData: FormAttributes;
  isLoading: boolean;
  isEdition: boolean;
  setOpenDialogError: (param: boolean) => void;
  steps?: number;
  rootRef: RefObject<any>;
};

const CustomForm = styled('form')({
  display: 'flex',
  flexDirection: 'column',
  alignItems: 'center',
  width: '100%',
  position: 'relative',
  '& > div:last-child': {
    marginBottom: '100px',
  },
});

type BodyData = FormSubmitData & { id: number };

export const Form: FC<FormProps> = props => {
  const {
    formDetailData,
    isLoading,
    setOpenDialogError,
    steps,
    isEdition,
    rootRef,
  } = props;

  // eslint-disable-next-line react/hook-use-state
  const [startTime] = useState(new Date());
  const theme = useTheme();
  const { enqueueSnackbar } = useSnackbar();
  const showGeneralError = useGeneralError();
  const { id, templateId, answerId } = useParams();
  const { instance } = useAuth();
  const { t } = useLokaliseTranslation('forms');
  const navigate = useNavigate();
  const form = useForm({});
  const queryClient = useQueryClient();
  const [currentStep, setCurrentStep] = useState(0);
  const [openDialogBack, setOpenDialogBack] = useState<boolean>(false);

  const { isUploadingAny: isUploadingAnyFile } = useFormFileUpload();
  const step = formDetailData?.steps && formDetailData.steps[currentStep];

  // Panalab custom form development
  const {
    isSpecialForm: specialFormPanalab,
    invalidExpenseAmount: invalidPanalabExpenseAmount,
  } = usePanalabSpecialForm({
    formTitle: formDetailData?.title,
    currentStep: step,
    currentStepIdx: currentStep,
    form,
  });

  // Edemsa custom form development
  const {
    userProfileDataObj,
    noUserFound,
    isLoading: isLoadingProfileData,
    isSpecialForm: specialFormEdemsa,
    handleSearchEmployeeData,
    setEmployeeId,
  } = useEdemsaVLookupForm({
    formTitle: formDetailData.title,
    currentStepIdx: currentStep,
    step,
    form,
  });

  const { stackStepForms, addStepForm, deleteStepForm, hasBranching } =
    useStackStepForms();

  const { mutate, isLoading: isLoadingCreateForm } = useMutation(
    (body: BodyData): Promise<AxiosResponse<FormItemResponseCompleted>> => {
      const { id: createId, ...formData } = body;
      return createForm(createId, formData);
    },
    {
      onSuccess: response => {
        logAmplitudeEvents(formDetailData, response);

        reset();

        enqueueSnackbar({ title: t('SEND_FORM_SUCCESS'), variant: 'success' });

        if (formDetailData?.needApproval) {
          navigate(
            formRoutes.form.approvalWorkflow(
              formDetailData!.type,
              response.data.id!,
            ),
            { replace: true },
          );
        } else {
          navigate(
            formRoutes.form.chat(formDetailData!.type, response.data.chatId),
            { replace: true },
          );
        }
      },
    },
  );

  const { mutate: mutateEdition, isLoading: isLoadingEditForm } = useMutation(
    (body: BodyData): Promise<AxiosResponse<FormItemResponseCompleted>> => {
      const { id: editId, ...formData } = body;
      return editForm(templateId, answerId, formData);
    },
    {
      onError: err => {
        showGeneralError(err, t('ERROR_FORM_EDIT'));
      },
      onSuccess: response => {
        const {
          id: filledId,
          needApproval,
          templateId: filledTemplateId,
          chatId,
          type,
        } = response.data;

        logAmplitudeEditEvent(filledId, needApproval, filledTemplateId);

        reset();

        queryClient.invalidateQueries(
          formKeys.answer(filledTemplateId, filledId!),
        );

        enqueueSnackbar({ title: t('EDIT_FORM_SUCCESS'), variant: 'success' });

        if (needApproval) {
          navigate(formRoutes.form.approvalWorkflow(type!, filledId!), {
            replace: true,
          });
        } else {
          navigate(formRoutes.form.chat(type!, chatId), { replace: true });
        }
      },
    },
  );

  useEffect(() => {
    const scrollToTop = () => {
      if (rootRef?.current?._container) {
        rootRef.current._container.scrollTop = 0;
      }
    };
    scrollToTop();
  }, [currentStep, rootRef]);

  useEffect(() => {
    if (hasBranching) {
      addStepPreSubmit(id);
      const stepItemStack = buildStepItem(formDetailData?.steps);
      addStepForm(stepItemStack);
    }
    return () => {
      if (hasBranching) {
        deleteFormWithSectionBracing(id);
      }
    };
  }, [hasBranching]);

  const {
    handleSubmit,
    reset,
    formState: { isSubmitting },
    getValues,
  } = form;

  const onAcceptBack = () => {
    setOpenDialogBack(false);
    navigate(-1);
  };

  const onCancelBack = () => {
    setOpenDialogBack(false);
  };

  const submitForm = (values: any) => {
    const endTime = new Date();
    const fillingTime = differenceInSeconds(endTime, startTime);
    const questions = formatValues(values);

    const body = {
      id: formDetailData.id,
      questions,
      fillingTime,
    };

    isEdition ? mutateEdition(body) : mutate(body);
  };

  const moveToAnotherStep = () => {
    const currentStepInStack = stackStepForms[stackStepForms?.length - 1];
    if (currentStepInStack.skip === null) {
      const stepToGo = currentStep + 1;
      const stepItemStack = buildStepItem(formDetailData?.steps, stepToGo);
      addStepForm(stepItemStack);
      setCurrentStep(stepToGo);
    } else if (currentStepInStack.skip.skipType === FormSkipTypes.STEP) {
      const stepToGo = findStepToGo(
        formDetailData?.steps,
        currentStepInStack.skip.destinationStepId,
      );
      if (stepToGo !== currentStepInStack.stepIndex) {
        const stepItemStack = buildStepItem(formDetailData?.steps, stepToGo);
        addStepForm(stepItemStack);
        const destinationStepsId = getDestinationStepIds(
          formDetailData.steps[currentStep],
        );
        resetValuesGoBack(
          form.getValues(),
          destinationStepsId,
          form.resetField,
        );
        setCurrentStep(stepToGo);
      }
    } else if (currentStepInStack.skip.skipType === FormSkipTypes.END) {
      const preSubmitIndex = (steps ?? 0) - 1;
      const stepItemStack = buildStepItem(
        formDetailData?.steps,
        preSubmitIndex,
      );
      addStepForm(stepItemStack);
      setCurrentStep(preSubmitIndex);
    }
  };

  const handleNextSectionBranching = (values: any) => {
    if (isFormReadyToSubmit(currentStep, steps, formDetailData?.steps)) {
      submitForm(values);
    } else {
      moveToAnotherStep();
    }
  };

  const handleNextWithOutSectionBranching = (values: any) => {
    if (currentStep + 1 !== steps) {
      setCurrentStep(prevcurrentStep => prevcurrentStep + 1);
    } else {
      submitForm(values);
    }
  };

  const handleNext = async (values: any) => {
    try {
      setEmployeeId('');
      if (!formDetailData.hasBranching) {
        handleNextWithOutSectionBranching(values);
      } else {
        handleNextSectionBranching(values);
      }
    } catch (err) {
      showGeneralError(err, t('SEND_FORM_ERROR'));
    }
  };

  const handleBack = () => {
    if (!formDetailData.hasBranching) {
      setCurrentStep(prevcurrentStep => prevcurrentStep - 1);
    } else {
      const penultimateStepInStack = stackStepForms[stackStepForms.length - 2];
      const stepToGo = penultimateStepInStack?.stepIndex;
      setCurrentStep(stepToGo);
      deleteStepForm();
    }
  };

  const onErrorCompleteForm = () => {
    setOpenDialogError(true);
  };

  const isLastStepPreSubmit = hasBranching && currentStep === (steps ?? 0) - 1;

  const handleGoBack = () => {
    const fieldsObj = getValues();
    const filteredFieldsObj = getFilteredValues(fieldsObj);
    const values = Object.values(filteredFieldsObj);
    if (
      checkEmptyForm(values) &&
      (typeof values !== 'object' || checkEmptyObject(values)) &&
      !isUploadingAnyFile
    ) {
      navigate(-1);
    } else {
      setOpenDialogBack(true);
    }
  };

  return (
    <>
      {!isLoading && step && (
        <>
          <FormProvider {...form}>
            <GoBackButton onClick={handleGoBack} />
            <FormHeader
              isAnonymous={formDetailData?.isAnonymous ?? undefined}
              isLastStepPreSubmit={isLastStepPreSubmit}
              title={step?.title}
              description={step?.description}
              hasMandatory={hasMandatory(step?.questions)}
            />
            <CustomForm
              onSubmit={handleSubmit(handleNext, onErrorCompleteForm)}
            >
              <FormSubmit
                handleBack={handleBack}
                currentStep={currentStep}
                steps={steps ?? 0}
                stepsList={formDetailData?.steps}
                isSubmitting={
                  isSubmitting || isLoadingCreateForm || isLoadingEditForm
                }
                disabledContinue={invalidPanalabExpenseAmount}
              />
              {step?.questions?.map((config, index) => (
                <FormContent
                  key={`${currentStep}-${index}-${getQuestionName(config)}`}
                  name={`${currentStep}-${index}-${getQuestionName(config)}-${step.id}`}
                  answerInput={isEdition ? config : null}
                  handleSearchEdemsaUserId={handleSearchEmployeeData}
                  edemsaProfileData={userProfileDataObj as EdemsaProfileValue[]}
                  isEdemsaSpecialForm={specialFormEdemsa}
                  isPanalabSpecialForm={specialFormPanalab}
                  currentSection={currentStep}
                  isSpecialFieldEdemsa={isEdemsaSpecialField(
                    config.description,
                    instance!.id,
                    currentStep,
                  )}
                  isViaticumAmountField={isViaticumAmountField(
                    config.description,
                    instance!.id,
                  )}
                  isViaticumTotalAmountField={isViaticumTotalAmountField(
                    config.description,
                    instance!.id,
                  )}
                  invalidViaticumAmount={invalidPanalabExpenseAmount}
                  InputProps={
                    (specialFormEdemsa || specialFormPanalab) &&
                    (isEdemsaSpecialField(
                      config.description,
                      instance!.id,
                      currentStep,
                    ) ||
                      isViaticumTotalAmountField(
                        config.description,
                        instance!.id,
                      )) && {
                      sx: {
                        background: theme.palette.new.background.elements.grey,
                      },
                    }
                  }
                  noUserFound={noUserFound}
                  isLoading={isLoadingProfileData}
                  {...config}
                />
              ))}
            </CustomForm>
          </FormProvider>
          {openDialogBack && (
            <AcceptCancelDialog
              open={openDialogBack}
              title={t(isEdition ? 'CANCEL_EDIT' : 'LOSE_DATA_TITLE')}
              showCancel
              onCancel={onCancelBack}
              onAccept={onAcceptBack}
            >
              <Typography color="textPrimary">{t('LOSE_DATA_INFO')}</Typography>
            </AcceptCancelDialog>
          )}
        </>
      )}
    </>
  );
};

export default Form;
