import { useCallback, useEffect, useRef, useState } from 'react';
import { FormProvider, useForm, useWatch } from 'react-hook-form';

import { useModal } from '@material-hu/hooks/useModal';
import Stack from '@material-hu/mui/Stack';

import HuCircularProgress from '@material-hu/components/design-system/ProgressIndicators/Spinner';

import useGeneralError from 'src/hooks/useGeneralError';
import HorizontalStepper from 'src/pages/dashboard/Learning/Courses/New/components/HorizontalStepper';
import { getCourseSeverity } from 'src/pages/dashboard/Learning/Courses/utils';
import { type Audience } from 'src/types/segmentation';
import { useLokaliseTranslation } from 'src/utils/i18n';
import { LogEvents, logEvent } from 'src/utils/logging';

import SeverityPill from 'src/components/SeverityPill';

import useCourseStatus from '../../hooks/useCourseStatus';
import {
  type CourseDetail,
  type OnDraftProps,
  type Selected,
  Steps,
  type StepsTypes,
  TaskTypes,
} from '../../types';
import { defaultCourseValues } from '../constants';
import { newCourseFields } from '../forms';
import {
  courseHasNewContent,
  getCompletedSteps,
  getCourseDefaultValues,
  getCourseFunnel,
  isInternalId,
  sameStepsTypes,
  validateStep,
} from '../utils';

import { BasicInformation } from './BasicInformation';
import CloseModal from './CloseModal';
import Configuration from './Configuration';
import Content from './Content';
import FinalRevision from './FinalRevision';
import RefreshCourseProgressModal from './RefreshCourseProgressModal';
import ValidationModal from './ValidationModal';

export type CourseFormProps = {
  course?: CourseDetail;
  audience?: Audience;
  onSubmit: (values: StepsTypes, prevValues?: StepsTypes) => Promise<void>;
  onDraft: (props: OnDraftProps) => Promise<{
    updatedCourse: CourseDetail;
    updatedAudience?: Audience;
  }>;
  loadingMutations?: boolean;
};

export const CourseForm = ({
  course,
  audience,
  onSubmit,
  onDraft,
  loadingMutations = false,
}: CourseFormProps) => {
  const { isEdit, isDuplicate } = useCourseStatus();

  const { t } = useLokaliseTranslation('learning');
  const [step, setStep] = useState(0);
  const [stepId, setStepId] = useState(Steps.BASIC_INFORMATION);
  const [showInfoAlert, setShowInfoAlert] = useState(true);
  const [moduleCount, setModuleCount] = useState(1);
  const [taskCount, setTaskCount] = useState(1);
  const [showStepperButtons, setShowStepperButtons] = useState(true);
  const [showContentValidation, setShowContentValidation] = useState(false);
  const [deletedModules, setDeletedModules] = useState<number[]>([]);
  const [deletedTasks, setDeletedTasks] = useState<number[]>([]);
  const isFormInitialized = useRef(false);
  const showGeneralError = useGeneralError();

  const lastSavedValues = useRef<StepsTypes>();

  const [completed, setCompleted] = useState(
    getCompletedSteps(isEdit, isDuplicate),
  );

  const form = useForm<StepsTypes>({
    defaultValues: defaultCourseValues(t),
    mode: 'onChange',
  });

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

  // biome-ignore lint/correctness/useExhaustiveDependencies: we dont need the showGeneralError in the dependencies
  useEffect(() => {
    const initForm = async () => {
      try {
        const formattedDefaultValues = await getCourseDefaultValues(
          course,
          audience,
          t,
        );
        reset(formattedDefaultValues);
        lastSavedValues.current = formattedDefaultValues;
      } catch (error) {
        showGeneralError(error);
      }
      isFormInitialized.current = true;
    };

    if (!isFormInitialized.current) initForm();
  }, [course, audience, t, reset]);

  const status = useWatch({
    control: form.control,
    name: 'basicInformation.status',
  });

  const handleClose = () => showCloseModal();
  const isValidForm = Object.keys(errors)?.length <= 0;

  const { showModal: showCloseModal, modal: closeModal } = useModal(
    CloseModal,
    { disableEscapeKeyDown: loadingMutations },
    {
      onSave: callback => handleSaveStepData(callback),
      loading: loadingMutations,
      stepId,
    },
  );

  const { showModal: showValidationModal, modal: validationModal } =
    useModal(ValidationModal);

  const isValidStep = useCallback(
    (currentStepId: Steps) => validateStep(currentStepId, getValues()),
    [getValues],
  );

  const handleCancelTaskType = () => setShowStepperButtons(true);
  const handleCloseInfoAlert = () => setShowInfoAlert(false);

  const handleSelectTaskType = () => {
    setShowStepperButtons(false);

    const courseName = getValues(newCourseFields.basicInformation.title());
    logEvent(LogEvents.COURSE_TASK_TYPE_SELECTED, {
      courseName,
      courseFunnel: getCourseFunnel(isEdit, isDuplicate),
    });
  };

  const handleCreateModule = () => {
    setModuleCount(moduleCount + 1);

    const courseName = getValues(newCourseFields.basicInformation.title());
    logEvent(LogEvents.COURSE_NEW_MODULE_CREATED, {
      courseName,
      courseFunnel: getCourseFunnel(isEdit, isDuplicate),
    });
  };

  const handleCreateTask = () => {
    setTaskCount(taskCount + 1);

    const courseName = getValues(newCourseFields.basicInformation.title());
    logEvent(LogEvents.COURSE_NEW_TASK_CREATED, {
      courseName,
      courseFunnel: getCourseFunnel(isEdit, isDuplicate),
    });
  };

  const handleSelected = (selected: Selected) => {
    if (selected.task < 0) setShowStepperButtons(true);
    else {
      const selectedType = getValues(
        newCourseFields.content.modules.tasks.type(
          selected.module,
          selected.task,
        ),
      );

      setShowStepperButtons(selectedType === TaskTypes.NEW);
    }
  };

  const handleDeleteModule = (module: number) => {
    const deletedModule = getValues(
      newCourseFields.content.modules.detail(module),
    );

    if (!isInternalId(deletedModule.id)) {
      // Note: ExternalIds are type number
      setDeletedModules([...deletedModules, deletedModule.id as number]);
    }
  };

  const handleDeleteTask = (module: number, task: number) => {
    const deletedTask = getValues(
      newCourseFields.content.modules.tasks.detail(module, task),
    );

    if (!isInternalId(deletedTask.id)) {
      // Note: ExternalIds are type number
      setDeletedTasks([...deletedTasks, deletedTask.id as number]);
    }
  };

  const steps = [
    {
      id: Steps.BASIC_INFORMATION,
      label: t('common.basic_information'),
      completed:
        completed[Steps.BASIC_INFORMATION] &&
        !!isValidStep(Steps.BASIC_INFORMATION),
      content: () => <BasicInformation disabled={isSubmitting} />,
    },
    {
      id: Steps.CONTENT,
      label: t('common.content'),
      completed: completed[Steps.CONTENT] && !!isValidStep(Steps.CONTENT),
      disabled: !isValidStep(Steps.BASIC_INFORMATION),
      content: () => (
        <Content
          disabled={isSubmitting}
          showInfoAlert={showInfoAlert}
          onCloseInfoAlert={handleCloseInfoAlert}
          onCreateModule={handleCreateModule}
          onCreateTask={handleCreateTask}
          onSelected={handleSelected}
          onSelectTaskType={handleSelectTaskType}
          onCancelTaskType={handleCancelTaskType}
          onDeleteModule={handleDeleteModule}
          onDeleteTask={handleDeleteTask}
          moduleCount={moduleCount}
          taskCount={taskCount}
          showValidation={showContentValidation}
        />
      ),
    },
    {
      id: Steps.CONFIGURATION,
      label: t('common.configuration'),
      completed:
        completed[Steps.CONFIGURATION] && !!isValidStep(Steps.CONFIGURATION),
      disabled:
        !isValidStep(Steps.BASIC_INFORMATION) ||
        (showContentValidation && !isValidStep(Steps.CONTENT)),
      content: () => <Configuration disabled={isSubmitting} />,
    },
    {
      id: Steps.FINAL_REVISION,
      label: t('common.revision'),
      completed:
        completed[Steps.FINAL_REVISION] && !!isValidStep(Steps.FINAL_REVISION),
      disabled:
        isDuplicate ||
        !isValidStep(Steps.BASIC_INFORMATION) ||
        (showContentValidation && !isValidStep(Steps.CONTENT)) ||
        !isValidStep(Steps.CONFIGURATION),
      content: () => <FinalRevision />,
    },
  ];

  const markCompleted = (completedId: string) => {
    const newCompleted = completed;
    newCompleted[completedId as keyof typeof completed] = true;

    setCompleted(newCompleted);
  };

  const moveToNextStep = (nextStep?: number) => {
    markCompleted(steps[step].id);
    selectStep(nextStep ? nextStep : step + 1);
  };

  const selectStep = (newStep: number) => {
    if (step === newStep) return;

    setStep(newStep);
    setStepId(steps[newStep].id);

    setShowStepperButtons(true);
  };

  const validateNextStep = () => {
    const isContentStep = steps[step].id === Steps.CONTENT;

    if (!isContentStep) return true;

    setShowContentValidation(true);
    if (!isValidStep(stepId)) {
      showValidationModal();
      return false;
    }

    return true;
  };

  const { showModal: showRefreshProgressModal, modal: refreshProgressModal } =
    useModal(RefreshCourseProgressModal, undefined, {
      onContinue: () => {
        handleSaveStepData();
        moveToNextStep();
      },
    });

  const shouldShowRefreshProgressModal = () => {
    const oldModules = lastSavedValues.current?.content?.modules || [];
    const newModules = getValues(newCourseFields.content.modules.all());
    const hasNewContent = courseHasNewContent(newModules, oldModules);
    const refreshFinishedCourseUsers = getValues(
      newCourseFields.configuration.refreshFinishedCourseUsers(),
    );

    const isContentStep = steps[step].id === Steps.CONTENT;

    return (
      hasNewContent && !refreshFinishedCourseUsers && isContentStep && isEdit
    );
  };

  const handleSaveStepData = async (callback?: Function) => {
    const values = getValues();

    const { updatedCourse, updatedAudience } = await onDraft({
      values,
      deletedModules,
      deletedTasks,
      currentStep: steps[step].id,
    });

    callback && callback();
    setDeletedTasks([]);
    setDeletedModules([]);

    const newDefaultValues = await getCourseDefaultValues(
      updatedCourse,
      updatedAudience,
      t,
    );
    lastSavedValues.current = newDefaultValues;
    reset(newDefaultValues);
  };

  const handleNextStep = async (nextStep?: number, callback?: Function) => {
    const isValid = await form.trigger();

    if (!isValid || !validateNextStep()) return;

    if (shouldShowRefreshProgressModal()) {
      showRefreshProgressModal();
      return;
    }

    const values = getValues();
    const hasChanges =
      lastSavedValues.current &&
      !sameStepsTypes(lastSavedValues.current, values);

    if (hasChanges) {
      await handleSaveStepData(callback);
    }

    moveToNextStep(nextStep);
  };

  const handleSelectStep = (newStep: number) => {
    // Move forward steps
    if (newStep > step) handleNextStep(newStep);
    // Move previous steps
    else selectStep(newStep);
  };

  const getNextButtonDisabled = () => {
    const validStep = isValidStep(stepId);

    if (loadingMutations || isSubmitting) return true;

    if (stepId === Steps.CONTENT) {
      return !validStep && showContentValidation;
    }

    return !validStep;
  };

  const nextButton = {
    onClick: () => handleNextStep(),
    disabled: getNextButtonDisabled(),
  };

  const finishButton = {
    onClick: handleSubmit(values => onSubmit(values, lastSavedValues.current)),
    disabled: !isValidForm || loadingMutations,
  };

  const backButton = { onClick: () => selectStep(step - 1) };

  const showPill = () => {
    const courseStatus = getValues('basicInformation.status');
    const isDraft = courseStatus === 'DRAFT';

    if (stepId === Steps.BASIC_INFORMATION && isDraft) return false;

    return true;
  };

  return (
    <FormProvider {...form}>
      <Stack
        component="form"
        onSubmit={event => event.preventDefault()}
        noValidate
        sx={{
          flexDirection: 'column',
          height: '100%',
          backgroundColor: ({ palette }) =>
            palette.new.background.layout.default,

          '& > div:first-of-type': {
            backgroundColor: 'white',
          },
          '& > div:last-child': {
            ...(!showStepperButtons && { display: 'none' }),
            minWidth: '100%',
            // Its hardcoded because this section is not inside HU provider.
            backgroundColor: '#F2F2F7',
            pl: { xs: 3, md: 'calc((100vw - 900px) / 2);' },
            pr: { xs: 3, md: 'calc((100vw - 900px) / 2);' },
          },
        }}
      >
        {validationModal}
        {closeModal}
        {refreshProgressModal}
        <HorizontalStepper
          activeStep={step}
          steps={steps}
          disabled={getNextButtonDisabled()}
          nonLinear
          clickable
          onSelectStep={handleSelectStep}
          backButton={showStepperButtons ? backButton : undefined}
          nextButton={showStepperButtons ? nextButton : undefined}
          finishButton={showStepperButtons ? finishButton : undefined}
          onClose={handleClose}
          closeLabel={t('general:close')}
          title={t(isEdit ? 'course.edit' : 'course.create')}
          actions={loadingMutations ? <HuCircularProgress /> : null}
          titleRightComponent={
            showPill() && (
              <SeverityPill
                severity={getCourseSeverity(status)}
                label={t(`path.status.${status?.toLowerCase()}`)}
              />
            )
          }
        />
      </Stack>
    </FormProvider>
  );
};

export default CourseForm;
