import { type MutableRefObject, useState } from 'react';
import { useFormContext, useWatch } from 'react-hook-form';
import { useNavigate } from 'react-router-dom';

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

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

import useFormatDate from 'src/hooks/useFormatDate';
import { useLokaliseTranslation } from 'src/utils/i18n';
import { LogEvents, logEvent } from 'src/utils/logging';

import { type SegmentationPayload } from '../../common/types';
import { SessionStatus } from '../constants';
import { PublishSessionDialog } from '../New/components/PublishSessionDialog';
import {
  getSessionStepFormatter,
  getUpdatedSession,
  hasSessionStepChanges,
  invalidateSessionsQueries,
} from '../New/form';
import { sessionsRoutes } from '../routes';
import {
  type CreateLearningSession,
  type NewSessionFormValues,
} from '../types';
import { isOldSession } from '../utils';

import { useAssignUsersToSession } from './useAssignUsersToSession';
import { useCreateSessionDraft } from './useCreateSessionDraft';
import { useNewSessionStepper } from './useNewSessionStepper';
import { useSaveSessionStep } from './useSaveSessionStep';
import { useSessionStatus } from './useSessionStatus';
import useValidateStep from './useValidateStep';

const useCreateSession = (
  sessionId: number | undefined,
  sessionRef: MutableRefObject<NewSessionFormValues | undefined>,
) => {
  const [processingData, setProcessingData] = useState(false);

  const { isFirstStep, isLastStep, next, currentStep, setStep } =
    useNewSessionStepper();
  const validateStep = useValidateStep();
  const navigate = useNavigate();
  const { formatDate } = useFormatDate();
  const { enqueueSnackbar } = useSnackbar();
  const { t } = useLokaliseTranslation('learning');
  const { blockSessionEdition } = useSessionStatus();

  const isEditRoute = location.pathname.includes('/edit');

  const { getValues, reset, handleSubmit, control } =
    useFormContext<NewSessionFormValues>();

  const [status, isAudienceDefined] = useWatch({
    control,
    name: ['basic_information.status', 'basic_information.isAudienceDefined'],
  });

  const isEditingSession = status === SessionStatus.published && isEditRoute;

  const createSessionDraft = useCreateSessionDraft();
  const saveSessionStep = useSaveSessionStep(sessionId);
  const assignUsersToSession = useAssignUsersToSession(sessionId);

  const handleActivateSession = () => {
    const activateSession = () => {
      const [localDate, startTime, timeZone] = getValues([
        'basic_information.localDate',
        'basic_information.startTime',
        'basic_information.timeZone',
      ]);

      saveSessionStep.mutate(
        {
          status: isOldSession({ localDate, startTime, timeZone })
            ? SessionStatus.finished
            : SessionStatus.published,
        },
        {
          onSuccess: () => {
            invalidateSessionsQueries();
            navigate(sessionsRoutes.base());
            logEvent(LogEvents.NEW_SESSION_CREATED, { sessionId });
            enqueueSnackbar({
              title: t('session.publish.success.title'),
              variant: 'success',
            });
          },
        },
      );
    };

    handleSubmit(activateSession)();
  };

  const handleSubmitLastStep = () => {
    if (isEditingSession) {
      logEvent(LogEvents.SESSION_EDITED, { sessionId });
      enqueueSnackbar({
        title: t('session.edit.success.title'),
        variant: 'success',
      });
      navigate(sessionsRoutes.base());
    } else {
      publishSessionDialog.showModal();
    }
  };

  const handleNextStep = async () => {
    const currentValues = getValues();
    const step = currentStep.id as keyof NewSessionFormValues;
    const prevValues = sessionRef.current;

    if (blockSessionEdition) {
      if (isLastStep) setStep('basic_information');
      else next.handle();
      return;
    }

    if (isLastStep) return handleSubmitLastStep();

    const isValid = await validateStep(step);
    if (!isValid) return;

    const hasChanges = hasSessionStepChanges(
      prevValues,
      currentValues,
      step,
      isAudienceDefined,
    );

    if (!hasChanges) return next.handle();

    const formattedData = await formatSessionStepData(currentValues);

    const newSession = await handleStepSaving(formattedData);
    const updatedSession = getUpdatedSession(currentValues, newSession);

    invalidateSessionsQueries(updatedSession.basic_information.id);

    sessionRef.current = updatedSession;
    reset(updatedSession);
  };

  const handleStepSaving = async (
    formattedData: Partial<CreateLearningSession> | SegmentationPayload,
  ) => {
    const shouldCreateDraft = isFirstStep && !sessionId;
    const isSegmentationStep = currentStep.id === 'segmentation';

    if (shouldCreateDraft) {
      const { data: newSession } = await createSessionDraft.mutateAsync(
        formattedData as CreateLearningSession,
      );
      return newSession;
    }

    if (isSegmentationStep) {
      await assignUsersToSession.mutateAsync(
        formattedData as SegmentationPayload,
      );
    } else {
      await saveSessionStep.mutateAsync(
        formattedData as Partial<CreateLearningSession>,
      );
    }
  };

  const formatSessionStepData = async (currentValues: NewSessionFormValues) => {
    const sessionStepFormatter = getSessionStepFormatter(currentStep.id);

    setProcessingData(true);
    const formattedData = await sessionStepFormatter(currentValues, formatDate);
    setProcessingData(false);
    return formattedData;
  };

  const handleSaveOnClose = async () => {
    const currentValues = getValues();
    const step = currentStep.id as keyof NewSessionFormValues;

    const isValid = await validateStep(step);

    if (!isValid) return false;

    const formattedData = await formatSessionStepData(currentValues);
    const newSession = await handleStepSaving(formattedData);
    const updatedSession = getUpdatedSession(currentValues, newSession);

    invalidateSessionsQueries(updatedSession.basic_information.id);
    return true;
  };

  const isLoading =
    createSessionDraft.isLoading ||
    saveSessionStep.isLoading ||
    assignUsersToSession.isLoading ||
    processingData;

  const publishSessionDialog = useModal(
    PublishSessionDialog,
    { fullWidth: true, maxWidth: 'sm' },
    {
      onPublishSession: handleActivateSession,
      loading: saveSessionStep.isLoading,
    },
  );

  return {
    isLoading,
    handleNextStep,
    publishSessionDialog: publishSessionDialog.modal,
    handleSaveOnClose,
  };
};

export default useCreateSession;
