import { useRef, useState } from 'react';
import { Helmet } from 'react-helmet-async';
import { FormProvider, useForm } from 'react-hook-form';
import { useNavigate } from 'react-router-dom';

import { zodResolver } from '@hookform/resolvers/zod';
import { isEqual } from 'lodash-es';
import Stack from '@material-hu/mui/Stack';
import { useTheme } from '@material-hu/mui/styles';

import TaskFocusHeader from '@material-hu/components/design-system/Header/TaskFocus';
import useSnackbar from '@material-hu/components/design-system/Snackbar';

import { settingsRoutes } from 'src/pages/dashboard/Learning/Settings/routes';
import { formatTitle } from 'src/utils/helmetUtils';
import { useLokaliseTranslation } from 'src/utils/i18n';
import { LogEvents, logEvent } from 'src/utils/logging';
import { handleScrollToTop } from 'src/utils/tableUtils';

import { CertificateStatus, CertificateType } from '../../constants';
import { useCreateLearningCertificate } from '../../hooks/useCreateLearningCertificate';
import { useUpdateLearningCertificate } from '../../hooks/useUpdateLearningCertificate';
import { type UpdateLearningCertificateRequest } from '../../types';
import {
  CERTIFICATE_FUNNELS,
  CERTIFICATE_STEP_ID,
  STEP_FIELDS_MAP,
} from '../constants';
import { useCertificateCreateSteps } from '../contexts/CertificateCreateStepsContext';
import useCertificateConfirmDialog from '../hooks/useCertificateConfirmDialog';
import useCertificateCreateSchema from '../hooks/useCertificateCreateSchema';
import useCreateCertificateUnsavedWarning from '../hooks/useCreateCertificateUnsavedWarning';
import {
  type CertificateCreateFormValues,
  type CertificateStepId,
} from '../types';
import {
  formatBasicInformationUpdateRequest,
  formatCreateRequest,
  formatDesignUpdateRequest,
  hasBackgroundFile,
} from '../utils';

import CertificatesCreateFooter from './CertificatesCreateFooter';
import CertificatesCreateContent from './Steps';

type CertificateWizardFormProps = {
  /** Form values the wizard mounts with: create defaults or the persisted certificate. */
  defaultValues: CertificateCreateFormValues;
  /** Whether the wizard is editing an existing certificate. */
  isEditMode: boolean;
  /** Id of the certificate being edited, or null when creating. */
  initialCertificateId: number | null;
  /** Persisted certificate name, used as the header title when editing. */
  certificateName?: string;
};

/** Last payload persisted per step, used to skip no-op requests on re-entry. */
type LastSavedPayload = {
  basicInformation?: UpdateLearningCertificateRequest;
  design?: UpdateLearningCertificateRequest;
};

const CertificateWizardForm = ({
  defaultValues,
  isEditMode,
  initialCertificateId,
  certificateName,
}: CertificateWizardFormProps) => {
  const { t } = useLokaliseTranslation('learning');
  const navigate = useNavigate();
  const { palette } = useTheme();
  const { enqueueSnackbar } = useSnackbar();
  const schema = useCertificateCreateSchema();
  const {
    isFirstStep,
    currentStepId,
    goNext,
    goBack,
    setCustomBackgroundError,
    clearCustomBackgroundError,
  } = useCertificateCreateSteps();

  const form = useForm<CertificateCreateFormValues>({
    defaultValues,
    resolver: zodResolver(schema),
    mode: 'onSubmit',
  });

  const {
    formState: { isDirty },
  } = form;

  const { modal: UnsavedChangesModal, navigateWithoutBlocking } =
    useCreateCertificateUnsavedWarning({ isDirty });

  const certificateCreatedRef = useRef(false);

  const [certificateId, setCertificateId] = useState<number | null>(
    initialCertificateId,
  );

  // Seeded from the mounted values: in edit mode they are already persisted, so
  // re-entering a step without changes skips the PATCH.
  const lastSavedPayloadRef = useRef<LastSavedPayload>(
    isEditMode
      ? {
          basicInformation: formatBasicInformationUpdateRequest(
            defaultValues,
            t,
          ),
          design: formatDesignUpdateRequest(defaultValues),
        }
      : {},
  );

  const createCertificate = useCreateLearningCertificate();
  const updateCertificate = useUpdateLearningCertificate();
  const isSubmitting =
    createCertificate.isLoading || updateCertificate.isLoading;

  const goToList = () => navigate(settingsRoutes.certificates.base());

  const handleExitWizard = (stepId: CertificateStepId) => {
    if (!certificateCreatedRef.current) {
      logEvent(LogEvents.NEW_CERTIFICATE_EXIT, {
        certificateStep: stepId,
        certificateFunnel: CERTIFICATE_FUNNELS.creation,
      });
    }
    goToList();
  };

  const handleConfirmCertificate = async (): Promise<boolean> => {
    if (!certificateId) return false;

    const { type, name } = form.getValues();
    try {
      await updateCertificate.mutateAsync({
        certificateId,
        data: { status: CertificateStatus.ACTIVE },
      });
    } catch {
      return false;
    }

    certificateCreatedRef.current = true;
    logEvent(LogEvents.NEW_CERTIFICATE_CREATED, {
      templateType: type,
    });
    enqueueSnackbar({
      title: t('settings.certificates.create_flow.success'),
      description: t('settings.certificates.create_flow.success_description', {
        certificateName: name,
      }),
      variant: 'success',
    });
    navigateWithoutBlocking(goToList);
    return true;
  };

  const { openConfirmDialog } = useCertificateConfirmDialog({
    onConfirm: handleConfirmCertificate,
  });

  /**
   * Edit flow finish: every step was already persisted via PATCH, so the final
   * action just confirms with a snackbar and returns to the list (no dialog, no
   * status change).
   */
  const handleSaveCertificateEdit = () => {
    if (certificateId) {
      logEvent(LogEvents.CERTIFICATE_EDITED, { certificateId });
    }
    enqueueSnackbar({
      title: t('general:saved'),
      variant: 'success',
    });
    navigateWithoutBlocking(goToList);
  };

  const handleClose = () => handleExitWizard(currentStepId);

  const handleBack = () => {
    if (isFirstStep) {
      handleExitWizard(currentStepId);
    } else {
      goBack();
      handleScrollToTop('dashboard-layout-content');
    }
  };

  const validateBasicInformationStep = (): boolean => {
    const { type, certificateCustomBackgroundFile } = form.getValues();
    const hasBackground = hasBackgroundFile(certificateCustomBackgroundFile);
    if (type === CertificateType.CUSTOM_BACKGROUND && !hasBackground) {
      setCustomBackgroundError(true);
      return false;
    }
    clearCustomBackgroundError();
    return true;
  };

  const STEP_VALIDATORS: Partial<Record<CertificateStepId, () => boolean>> = {
    [CERTIFICATE_STEP_ID.basicInformation]: validateBasicInformationStep,
  };

  /**
   * Leaving basic information: POST the draft, or PATCH it on re-entry when it
   * already exists. Returns false on failure so the wizard stays on the step.
   */
  const submitBasicInformationStep = async (): Promise<boolean> => {
    const values = form.getValues();
    const updateBody = formatBasicInformationUpdateRequest(values, t);

    if (!certificateId) {
      try {
        const { data } = await createCertificate.mutateAsync(
          formatCreateRequest(values, t),
        );
        setCertificateId(data.id);
        lastSavedPayloadRef.current.basicInformation = updateBody;
        return true;
      } catch {
        return false;
      }
    }

    if (isEqual(updateBody, lastSavedPayloadRef.current.basicInformation)) {
      return true;
    }

    try {
      await updateCertificate.mutateAsync({ certificateId, data: updateBody });
      lastSavedPayloadRef.current.basicInformation = updateBody;
      return true;
    } catch {
      return false;
    }
  };

  /** Leaving design: PATCH color, alignment, signatures and logos. */
  const submitDesignStep = async (): Promise<boolean> => {
    if (!certificateId) return true;

    const data = formatDesignUpdateRequest(form.getValues());
    if (isEqual(data, lastSavedPayloadRef.current.design)) return true;

    try {
      await updateCertificate.mutateAsync({ certificateId, data });
      lastSavedPayloadRef.current.design = data;
      return true;
    } catch {
      return false;
    }
  };

  const STEP_SUBMITTERS: Partial<
    Record<CertificateStepId, () => Promise<boolean>>
  > = {
    [CERTIFICATE_STEP_ID.basicInformation]: submitBasicInformationStep,
    [CERTIFICATE_STEP_ID.design]: submitDesignStep,
  };

  const handleNext = async () => {
    const stepValidator = STEP_VALIDATORS[currentStepId];
    const isStepValid = stepValidator ? stepValidator() : true;

    const fields = STEP_FIELDS_MAP[currentStepId];
    const isValid = await form.trigger(fields);
    if (!isValid || !isStepValid) {
      handleScrollToTop('dashboard-layout-content');
      return;
    }

    const submitStep = STEP_SUBMITTERS[currentStepId];
    if (submitStep) {
      const didSubmit = await submitStep();
      if (!didSubmit) {
        handleScrollToTop('dashboard-layout-content');
        return;
      }
    }

    goNext();
    handleScrollToTop('dashboard-layout-content');
  };

  const headerTitle =
    isEditMode && certificateName
      ? certificateName
      : t('settings.certificates.create');

  return (
    <FormProvider {...form}>
      <Helmet>
        <title>{formatTitle(headerTitle)}</title>
      </Helmet>
      <Stack
        component="form"
        onSubmit={e => e.preventDefault()}
        sx={{
          minHeight: '100%',
          backgroundColor: palette.new.background.layout.default,
        }}
      >
        <TaskFocusHeader
          title={headerTitle}
          onClose={handleClose}
        />
        <Stack sx={{ flex: 1, height: '100%' }}>
          <CertificatesCreateContent />
          <CertificatesCreateFooter
            onBack={handleBack}
            onNext={handleNext}
            onConfirm={
              isEditMode ? handleSaveCertificateEdit : openConfirmDialog
            }
            isSubmitting={isSubmitting}
            isEditMode={isEditMode}
          />
        </Stack>
      </Stack>
      {UnsavedChangesModal.modal}
    </FormProvider>
  );
};

export default CertificateWizardForm;
