import {useCallback, useMemo, useRef, useState} from 'react';
import {useTranslation} from 'react-i18next';
import z, {ZodTypeAny} from 'zod';
import {FieldValues, useForm} from 'react-hook-form';
import {zodResolver} from '@hookform/resolvers/zod';
import isEqual from 'lodash.isequal';
import {InputType, Question} from '@interfaces/questions';
import {
  FormAttributes,
  FormSkipType,
  MutateVariablesForm,
} from '@modules/form/interfaces';
import {getValueFromType} from '@modules/form/layouts/FormLayout/components/AutocompleteField/utils';
import {useAppSelector} from '@redux/utils';

import {
  buildMutateVariablesForm,
  createQuestionSchema,
  getSkipData,
  parseAnswer,
} from '../utils';
import {LegacyAnswer, SkipKey} from '../interfaces';

interface UseLegacyFormsProps {
  form: FormAttributes;
  createFormMutation: (data: MutateVariablesForm) => void;
  onStepChange?: () => void;
}

export const useLegacyForms = ({
  createFormMutation,
  form,
  onStepChange,
}: UseLegacyFormsProps) => {
  const {t} = useTranslation();
  const user = useAppSelector(({user: storedUser}) => storedUser);
  const stepIndexRef = useRef<number>(0);
  const [history, setHistory] = useState<number[]>([
    form.steps[stepIndexRef.current].id,
  ]);
  const [answers, setAnswers] = useState<Nullable<LegacyAnswer>>(null);

  const currentStep = useMemo(
    () => form.steps.find(step => step.id === history[history.length - 1]),
    [form.steps, history],
  );

  const questions: Question[] = useMemo(() => {
    if (!currentStep) {
      return [];
    }
    if (Array.isArray(currentStep.questions)) {
      return currentStep.questions;
    }
    return Object.values(currentStep.questions);
  }, [currentStep]);

  const {formSchema, values, dirtyCheckKeys, skipKeys} = questions.reduce(
    (
      acc: {
        formSchema: Record<string, ZodTypeAny>;
        values: Record<string, unknown>;
        dirtyCheckKeys: string[];
        skipKeys: SkipKey[];
      },
      component,
    ) => {
      const name = component.id.toString();
      const componentSchema = createQuestionSchema(t, component);
      if (componentSchema) {
        acc.formSchema[name] = componentSchema;
      }
      if (component.answer !== null) {
        acc.values[name] = parseAnswer(component);
      }
      if (component.inputType === InputType.AUTOCOMPLETE) {
        const autocompleteValue = getValueFromType({
          userField: component.userField,
          user,
          answer: component.answer as string | undefined,
          autoCompleteBoss: component.autoCompleteBoss,
        });
        if (autocompleteValue !== undefined && autocompleteValue !== null) {
          acc.values[name] = autocompleteValue;
        }
      }
      if (currentStep && answers && answers[currentStep.id]?.[name]) {
        acc.values[name] = answers[currentStep.id][name];
      }
      if (component.inputType !== InputType.AUTOCOMPLETE) {
        acc.dirtyCheckKeys.push(name);
      }
      if (
        component.inputType === InputType.DROPDOWN ||
        component.inputType === InputType.CHECKBOX
      ) {
        component.options.forEach(option => {
          option.skip &&
            acc.skipKeys.push({
              id: option.id,
              destinationStepId: option.skip.destinationStepId,
              skipType: option.skip.skipType as FormSkipType,
            });
        });
      }
      return acc;
    },
    {formSchema: {}, values: {}, dirtyCheckKeys: [], skipKeys: []},
  );

  const formMethods = useForm({
    resolver: zodResolver(z.object(formSchema || {})),
    values,
    mode: 'onSubmit',
  });

  const onFinish = useCallback(
    (newAnswers: LegacyAnswer) => {
      const formattedAnswers = buildMutateVariablesForm(
        form,
        newAnswers,
        history,
      );
      createFormMutation(formattedAnswers);
    },
    [form, history, createFormMutation],
  );

  const onPressContinue = useCallback(
    (fieldValues: FieldValues) => {
      if (!currentStep) {
        return;
      }

      const newAnswers: LegacyAnswer = answers
        ? {...answers, [currentStep.id]: fieldValues}
        : {[currentStep.id]: fieldValues};

      setAnswers(newAnswers);

      const skipData =
        skipKeys.length > 0 ? getSkipData(skipKeys, fieldValues) : undefined;
      if (skipData?.skipType === FormSkipType.END) {
        onFinish(newAnswers);
        return;
      }

      const isLastStep = stepIndexRef.current === form.steps.length - 1;
      const stepEnds =
        currentStep.skip?.skipType === FormSkipType.END || isLastStep;

      const nextStepId =
        skipData?.destinationStepId ??
        (!stepEnds
          ? currentStep.skip?.destinationStepId ??
            form.steps[stepIndexRef.current + 1].id
          : null);

      if (nextStepId == null) {
        onFinish(newAnswers);
        return;
      }

      setHistory(prev => [...prev, nextStepId]);
      stepIndexRef.current = form.steps.findIndex(
        step => step.id === nextStepId,
      );
      onStepChange?.();
    },
    [answers, currentStep, form.steps, onFinish, onStepChange, skipKeys],
  );

  const onPressBack = useCallback(() => {
    if (!currentStep) {
      return;
    }

    const currentValues: FieldValues = formMethods.watch();

    const currentAnswers: LegacyAnswer = answers
      ? {...answers, [currentStep.id]: currentValues}
      : {[currentStep.id]: currentValues};

    setAnswers(currentAnswers);

    setHistory(prev => {
      const nextHistory = prev.slice(0, -1);
      const previousStepId = nextHistory[nextHistory.length - 1];
      stepIndexRef.current = form.steps.findIndex(
        step => step.id === previousStepId,
      );
      return nextHistory;
    });
    onStepChange?.();
  }, [answers, currentStep, form.steps, formMethods, onStepChange]);

  const isDirty = dirtyCheckKeys.some(key => {
    const value = formMethods.watch(key);
    return Array.isArray(value) && value.length === 0
      ? !isEqual(undefined, values[key])
      : !isEqual(value, values[key]);
  });

  const isCurrentStepAnswered = currentStep?.id && answers?.[currentStep?.id];

  return {
    ...formMethods,
    extra: {
      isDirty,
      currentStep: {...currentStep, questions},
      isCurrentStepAnswered,
    },
    onPressContinue,
    onPressBack,
  };
};
