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

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

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

import { queryClient } from 'src/config/react-query';
import { useLokaliseTranslation } from 'src/utils/i18n';
import { LogEvents, logEvent } from 'src/utils/logging';

import { type SegmentationPayload } from '../../common/types';
import {
  getStepFormatter,
  validateStep,
} from '../New/components/PathForm/formConfig';
import { PublishPathDialog } from '../New/components/PublishPathDialog';
import { hasStepChanges, updatePath } from '../New/form';
import { pathsKeys } from '../queries';
import { pathsRoutes } from '../routes';
import {
  type CreateLearningPath,
  type CreatePathPostType,
  type NewPathFormValues,
  type PathDetail,
} from '../types';

import useAssignUsersToPathMutation from './useAsignUsers';
import useCreateDraftMutation from './useCreateDraftMutation';
import useGetAudience from './useGetAudience';
import { useNewPathStepper } from './useNewPathStepper';
import { useUpdatePartialPathMutation } from './useUpdatePartialPathMutation';
import { useUpdatePathStatus } from './useUpdatePathStatus';

export const useCreatePath = (
  pathId: number | null,
  pathRef: MutableRefObject<NewPathFormValues | null>,
) => {
  const { isFirstStep, isLastStep, next, currentStep } = useNewPathStepper();
  const form = useFormContext<NewPathFormValues>();
  const [processingData, setProcessingData] = useState(false);
  const navigate = useNavigate();
  const { enqueueSnackbar } = useHuSnackbar();
  const { t } = useLokaliseTranslation('learning');
  const isEditingPath = form.getValues('basic_information.status') === 'ACTIVE';

  const createDraftMutation = useCreateDraftMutation();
  const updatePartialPathMutation = useUpdatePartialPathMutation(pathId);
  const updatePathStatusMutation = useUpdatePathStatus(pathId);
  const assignUsersToPathMutation = useAssignUsersToPathMutation(pathId);
  const { data: audienceData } = useGetAudience(pathId);
  const isAudienceDefined = !!audienceData?.isAudienceDefined;

  const handleActivatePath = () => {
    const activatePath = async () => {
      try {
        await updatePathStatusMutation.mutateAsync('ACTIVE');

        logEvent(LogEvents.NEW_PATH_CREATED, { pathId });

        queryClient.invalidateQueries(pathsKeys.stats());
        if (pathId) queryClient.invalidateQueries(pathsKeys.audience(pathId));

        enqueueSnackbar({
          title: t('path.publish.success'),
          variant: 'success',
        });
      } catch {
        enqueueSnackbar({
          title: t('path.change_status.error'),
          variant: 'error',
        });
      } finally {
        navigate(pathsRoutes.base());
        next.handle();
      }
    };

    form.handleSubmit(activatePath)();
  };

  const publishPathDialog = useModal(
    PublishPathDialog,
    { fullWidth: true, maxWidth: 'sm' },
    {
      onPublishPath: handleActivatePath,
      loading: updatePartialPathMutation.isLoading,
    },
  );

  const handleSubmitLastStep = () => {
    if (isEditingPath) {
      logEvent(LogEvents.PATH_EDITED, { pathId });
      enqueueSnackbar({
        title: t('path.publish.success'),
        variant: 'success',
      });
      navigate(pathsRoutes.base());
    } else {
      publishPathDialog.showModal();
    }
  };

  const processStepData = async (currentValues: NewPathFormValues) => {
    const formatStepData = getStepFormatter(currentStep.id);

    setProcessingData(true);
    const stepData = await formatStepData(currentValues);
    setProcessingData(false);

    return { stepData };
  };

  const handleCreateDraft = async (stepData: CreatePathPostType) => {
    const { data: newPath } = await createDraftMutation.mutateAsync(stepData);
    return newPath;
  };

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

    if (shouldCreateDraft) {
      const newPath = await handleCreateDraft(stepData as CreatePathPostType);
      return newPath;
    }

    if (isSegmentationStep) {
      await assignUsersToPathMutation.mutateAsync(
        stepData as SegmentationPayload,
      );
    } else {
      await updatePartialPathMutation.mutateAsync(
        stepData as Partial<CreateLearningPath>,
        { onSuccess: () => next.handle() },
      );
    }
  };

  const invalidatePathsQueries = (id?: number | null) => {
    queryClient.invalidateQueries(pathsKeys.all());
    if (id) {
      queryClient.invalidateQueries(pathsKeys.detail(id));
      queryClient.invalidateQueries(pathsKeys.audience(id));
    }
  };

  const handleSaveOnClose = async () => {
    const currentValues = form.getValues();

    const isValid = await validateStep(currentStep.id, currentValues, form);
    if (!isValid) return false;

    const { stepData } = await processStepData(currentValues);
    const newPath = await handleStepSaving(stepData);
    const updatedValues = getUpdatedValues(newPath, currentValues);

    invalidatePathsQueries(updatedValues.basic_information.id);
    return true;
  };

  const getUpdatedValues = (
    newPath: PathDetail | undefined,
    currentValues: NewPathFormValues,
  ) => {
    let updatedValues = currentValues;

    if (newPath) {
      // If its a new path, we need to update the path in the form
      updatedValues = updatePath(newPath, currentValues);
    }

    return cloneDeep(updatedValues);
  };

  const handleNextStep = async () => {
    const currentValues = form.getValues();
    const prevValues = pathRef.current;

    if (isLastStep) return handleSubmitLastStep();

    const isValid = await validateStep(currentStep.id, currentValues, form);

    if (!isValid) return;

    // We check if the step has changes to avoid unnecessary mutations
    const hasChanges = hasStepChanges(
      prevValues,
      currentValues,
      currentStep.id,
      isAudienceDefined,
    );

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

    const { stepData } = await processStepData(currentValues);

    const newPath = await handleStepSaving(stepData);

    const updatedPath = getUpdatedValues(newPath, currentValues);

    invalidatePathsQueries(updatedPath.basic_information.id);

    pathRef.current = updatedPath;
    form.reset(updatedPath);
  };

  const isLoading =
    createDraftMutation.isLoading ||
    updatePartialPathMutation.isLoading ||
    assignUsersToPathMutation.isLoading ||
    processingData;

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