import React, {useState, useCallback, useRef} from 'react';
import {View} from 'react-native';
import {useTranslation} from 'react-i18next';
import {KeyboardAwareScrollView} from 'react-native-keyboard-controller';
import AcceptCancelDialog from '@components/AcceptCancelDialog';
import ActivityIndicator from '@components/ActivityIndicator';
import {useBackHandler} from '@hooks/useBackHandler';
import {useGoBack} from '@hooks/useGoBack';
import {
  AutocompleteQuestion,
  CheckboxAnswer,
  CheckboxGridAnswer,
  DecimalQuestion,
  InputType,
  Question,
  Row,
} from '@interfaces/questions';
import {ModalProps} from '@modules/app/interfaces';
import {FormChattable} from '@modules/chat/interfaces';
import {FormAttributes, MutateVariablesForm} from '@modules/form/interfaces';
import {usePaperBoxVLookupForm} from '@modules/form/hooks/usePaperBoxVLookupForm';
import {commonStyles} from '@shared/styles';
import {getLastElement, has} from '@shared/utils';
import {EMPTY_STRING} from '@shared/constants';
import {SHARED_STRINGS} from '@shared/strings';

import FormHeader from './components/FormHeader';
import FormFooter from './components/FormFooter';
import {useFetchForm} from './useFetchForm';
import Content from './components/Content';
import {useForwardForm} from './useForwardForm';
import {
  AllAnswers,
  ChangeFormQuestion,
  Input,
  LoadingFilesInputs,
} from './interfaces';
import {useHeaderConfig} from './useHeaderConfig';

const EXTRA_SCROLL_HEIGHT = 30;

interface Props {
  onSubmit: (
    params: MutateVariablesForm,
  ) => Promise<FormAttributes | FormChattable>;
  onError?: (errorCode: string) => void;
  editable?: boolean;
}

function FormLayout({onSubmit, onError, editable = false}: Props) {
  const {t} = useTranslation();
  const {goBack} = useGoBack();
  const scrollViewRef =
    useRef<Nullable<React.ElementRef<typeof KeyboardAwareScrollView>>>(null);
  const [inputs, setInputs] = useState<Input>({});
  const [seenSteps, setSeenSteps] = useState([0]);
  const [modal, setModal] = useState<Nullable<ModalProps>>(null);
  const [loadingFilesInputs, setLoadingFilesInputs] = useState<
    LoadingFilesInputs[]
  >([]);
  const [inputError, setInputError] = useState<{
    [id: number]: Nullable<string>;
  }>({});
  const currentStep = getLastElement(seenSteps) || 0;
  const {formConfig, isFormFilled, initialLoading} = useFetchForm({
    setInputs,
  });
  const isReadOnly = isFormFilled && !editable;

  const checkLoadingFiles = useCallback(
    (callback: () => void, onAcceptCallback?: () => void) => {
      loadingFilesInputs.length
        ? setModal({
            onTouchOutside: () => setModal(null),
            onAccept: () => {
              setModal(null);
              onAcceptCallback ? onAcceptCallback() : callback();
            },
            title: t('forms.files_loading'),
            text: t('forms.proceed_anyway_question'),
            showCancel: true,
          })
        : callback();
    },
    [loadingFilesInputs.length, t],
  );

  const loseProgressAction = useCallback(
    (callback: () => void) => () => {
      return modal
        ? setModal(null)
        : isReadOnly || !Object.keys(inputs).length
        ? callback()
        : setModal({
            onTouchOutside: () => setModal(null),
            onAccept: () => {
              setModal(null);
              callback();
            },
            title: t('forms.lose_data_title'),
            text: t(SHARED_STRINGS.DISCARD_CHANGES_DESCRIPTION),
            showCancel: true,
          });
    },
    [inputs, isReadOnly, modal, t],
  );

  const handleClose = useCallback(() => {
    checkLoadingFiles(loseProgressAction(goBack));
  }, [checkLoadingFiles, loseProgressAction, goBack]);

  useHeaderConfig({handleClose, title: formConfig?.title});

  const backAction = useCallback(() => {
    scrollViewRef.current?.scrollTo({x: 0, animated: false});
    if (seenSteps.length > 1) {
      setSeenSteps(prevSeenSteps =>
        prevSeenSteps.slice(0, prevSeenSteps.length - 1),
      );
    } else {
      loseProgressAction(goBack)();
    }
  }, [loseProgressAction, goBack, seenSteps.length]);

  const acceptBackAction = useCallback(() => {
    if (seenSteps.length > 1) {
      setLoadingFilesInputs([]);
      backAction();
    } else {
      goBack();
    }
  }, [goBack, backAction, seenSteps.length]);

  const handleBack = useCallback(() => {
    checkLoadingFiles(backAction, acceptBackAction);
    return true;
  }, [acceptBackAction, backAction, checkLoadingFiles]);

  useBackHandler(handleBack);

  const handleOnChange: ChangeFormQuestion<InputType, AllAnswers> = ({
    inputType,
    inputName,
    answer,
    position,
    userField,
  }) => {
    const baseInput = {
      stepIndex: currentStep,
      id: inputName,
      inputType,
      position,
    };
    const newInputs = inputs;
    switch (inputType) {
      case InputType.AUTOCOMPLETE: {
        newInputs[inputName] = {
          ...baseInput,
          ...inputs[inputName],
          ...(answer && {answer}),
          userField,
        } as AutocompleteQuestion;
        break;
      }
      case InputType.DECIMAL: {
        (answer as string)?.length
          ? (newInputs[inputName] = {
              ...baseInput,
              ...inputs[inputName],
              answer: (answer as string)?.replace(',', '.'),
            } as DecimalQuestion)
          : delete newInputs[inputName];
        break;
      }
      case InputType.CHECKBOX_GRID: {
        Array.isArray(answer) &&
        (answer as CheckboxGridAnswer).every(row => !row.answer?.length)
          ? delete newInputs[inputName]
          : (newInputs[inputName] = {
              ...baseInput,
              ...inputs[inputName],
              rows: answer as Row[],
            });
        break;
      }
      default: {
        if (
          !has(answer) ||
          (answer as string) === EMPTY_STRING ||
          (Array.isArray(answer) && !answer.length) ||
          (inputType === InputType.CHECKBOX &&
            !(answer as CheckboxAnswer[])?.some(
              (item: CheckboxAnswer) => item.checked,
            ))
        ) {
          delete newInputs[inputName];
        } else {
          newInputs[inputName] = {
            ...baseInput,
            ...inputs[inputName],
            answer,
          } as Question;
        }
      }
    }
    setInputs(newInputs);
  };

  const handleInputError = (
    inputId: number,
    errorMessage?: Nullable<string>,
  ) => {
    setInputError(prevState => {
      const errors = {...prevState};
      if (errorMessage) {
        errors[inputId] = errorMessage;
      } else if (errors[inputId]) {
        delete errors[inputId];
      }
      return errors;
    });
  };

  const {
    handleForward,
    submitMutation,
    formCompleted,
    submittedSteps,
    forwardText,
    isInLastStep,
    currentStepData,
    isOnFinishScreen,
    currentStepRequiredQuestions,
    submitting,
  } = useForwardForm({
    checkLoadingFiles,
    inputs,
    formConfig,
    onError,
    editable,
    onSubmit,
    scrollViewRef,
    isFormFilled,
    setSeenSteps,
    currentStep,
    setLoadingFilesInputs,
    isReadOnly,
    setModal,
    inputError,
  });

  const {isLoadingEmployee} = usePaperBoxVLookupForm({
    formId: formConfig?.id,
    currentStep,
    currentStepData,
    setInputs,
  });

  return (
    <View style={commonStyles.flex}>
      {!initialLoading &&
      formConfig &&
      (currentStepData || isOnFinishScreen) ? (
        <>
          <KeyboardAwareScrollView
            ref={scrollViewRef}
            bottomOffset={EXTRA_SCROLL_HEIGHT}
            bounces={false}
            alwaysBounceHorizontal={false}
            showsVerticalScrollIndicator={false}>
            <FormHeader
              hasMandatory={!!currentStepRequiredQuestions.length}
              step={{
                title: currentStepData?.title,
                description: currentStepData?.description,
                isAnonymous: !!formConfig.isAnonymous,
              }}
            />
            <Content
              isReadOnly={isReadOnly}
              formConfig={formConfig}
              currentStep={currentStep}
              currentStepData={currentStepData}
              isOnFinishScreen={isOnFinishScreen}
              isInLastStep={isInLastStep}
              handleOnChange={handleOnChange}
              submittedSteps={submittedSteps}
              inputs={inputs}
              handleInputError={handleInputError}
              loadingFilesInputs={loadingFilesInputs}
              setLoadingFilesInputs={setLoadingFilesInputs}
            />
          </KeyboardAwareScrollView>
          <FormFooter
            loading={isLoadingEmployee || submitMutation.isLoading}
            totalSteps={formConfig?.steps?.length || 0}
            currentStep={currentStep}
            formCompleted={formCompleted}
            onBackPress={handleBack}
            onForwardPress={handleForward}
            forwardText={forwardText}
            disabled={submitting}
          />
        </>
      ) : (
        <ActivityIndicator fullScreen />
      )}
      <AcceptCancelDialog
        visible={!!modal}
        onTouchOutside={modal?.onTouchOutside}
        onAccept={modal?.onAccept}
        title={modal?.title}
        text={modal?.text}
        showCancel={modal?.showCancel}
      />
    </View>
  );
}

export default FormLayout;
