import { type UseFormReturn } from 'react-hook-form';
import { useMutation } from 'react-query';

import { type AxiosError } from 'axios';
import { format, isAfter, parse, parseISO } from 'date-fns';

import { type FileCardType } from '@material-hu/components/design-system/FileCard/types';
import useHuSnackbar from '@material-hu/components/design-system/Snackbar';

import { logEvent } from 'src/config/amplitude';
import { invalidateTimeOffNotifications } from 'src/hooks/queryHooks/vacations';
import useFormatDate from 'src/hooks/useFormatDate';
import useGeneralError from 'src/hooks/useGeneralError';
import { uploadAllFiles } from 'src/services/attachments';
import { createVacationRequest } from 'src/services/vacations';
import { EventName } from 'src/types/amplitude';
import { type ResponseError } from 'src/types/services';
import { type MeUser, type User } from 'src/types/user';
import {
  type ConsumptionType,
  type RequestTimeOff,
  type ResponseRequestTimeOff,
  VacationRequestSources,
} from 'src/types/vacations';
import { isValidFilesSize } from 'src/utils/attachments';
import { useLokaliseTranslation } from 'src/utils/i18n';
import {
  formatValues,
  getDefaultValueFiles,
  handleErrorResponse,
  isPolicyTypeDay,
  processFilesVacations,
} from 'src/utils/vacations';

import { updateRequestsCollaborator } from '../queries';

type Props = {
  user: MeUser;
  isManagerRequesting?: boolean;
  closePolicyTypeDrawer?: () => void;
  closeDrawer: () => void;
  form: UseFormReturn<RequestCreateForm>;
};

export type RequestCreateForm = {
  policyTypeId: { value: number; label: string };
  fromDate: Date;
  consumptionTypeFromDate: ConsumptionType;
  toDate: Date;
  consumptionTypeToDate: ConsumptionType;
  description?: string;
  files: FileCardType[];
  fromHour: string;
  fromHourTime: string;
  toHour: string;
  toHourTime: string;
  hoursRequestType: HoursRequestType | null;
  hasSellingBalance: boolean;
  amountInMoney: { value: number; label: string } | null;
  amountInTime: number;
  hasSubstituteApprover: boolean;
  substituteApprover: User | null;
};

export enum HoursRequestType {
  LESS_THAN_ONE_DAY = 'less_than_one_day',
  MORE_THAN_ONE_DAY = 'more_than_one_day',
}

export const assignDefaultValues = (policyTypeId?: {
  value: number;
  label: string;
}) => {
  return {
    policyTypeId: policyTypeId,
    fromDate: undefined,
    consumptionTypeFromDate: undefined,
    toDate: undefined,
    consumptionTypeToDate: undefined,
    description: undefined,
    files: [],
    fromHour: '',
    fromHourTime: '',
    toHour: '',
    toHourTime: '',
    hoursRequestType: undefined,
    hasSellingBalance: false,
    amountInMoney: undefined,
    hasSubstituteApprover: false,
    substituteApprover: undefined,
  };
};

export const assignEditionValues = (vacationDetail: ResponseRequestTimeOff) => {
  const fromDate = parseISO(vacationDetail.from.date);
  const toDate = parseISO(vacationDetail.to.date);
  const parsedFromHour = vacationDetail.from.time
    ? parse(vacationDetail.from.time, 'HH:mm:ss', new Date())
    : undefined;
  const parsedToHour = vacationDetail.to.time
    ? parse(vacationDetail.to.time, 'HH:mm:ss', new Date())
    : undefined;
  const isRequestMoreThanOneDay = isAfter(
    parseISO(vacationDetail.to.date),
    parseISO(vacationDetail.from.date),
  );

  return {
    policyTypeId: {
      value: vacationDetail?.policyType.id,
      label: vacationDetail?.policyType.name,
    },
    fromDate: new Date(fromDate),
    consumptionTypeFromDate: vacationDetail?.from.consumptionType,
    toDate: new Date(toDate),
    consumptionTypeToDate: vacationDetail?.to.consumptionType,
    description: vacationDetail?.description,
    files: getDefaultValueFiles(vacationDetail) || [],
    fromHour: parsedFromHour ? format(parsedFromHour, 'HH') : '',
    fromHourTime: parsedFromHour ? format(parsedFromHour, 'mm') : '',
    toHour: parsedToHour ? format(parsedToHour, 'HH') : '',
    toHourTime: parsedToHour ? format(parsedToHour, 'mm') : '',
    hoursRequestType: isPolicyTypeDay(vacationDetail?.policyType)
      ? null
      : isRequestMoreThanOneDay
        ? HoursRequestType.MORE_THAN_ONE_DAY
        : HoursRequestType.LESS_THAN_ONE_DAY,
    hasSellingBalance: !!vacationDetail?.amountInMoney,
    amountInMoney: {
      value: vacationDetail?.amountInMoney,
      label: vacationDetail?.amountInMoney.toString(),
    },
    amountInTime: vacationDetail?.amountInTime,
    hasSubstituteApprover: !!vacationDetail?.substituteApprover,
    substituteApprover: vacationDetail?.substituteApprover || null,
  };
};

export const useRequestCreate = ({
  isManagerRequesting,
  closePolicyTypeDrawer,
  form,
  user,
  closeDrawer,
}: Props) => {
  const { enqueueSnackbar } = useHuSnackbar();
  const showGeneralError = useGeneralError();
  const { t } = useLokaliseTranslation('time_off');
  const { formatDate } = useFormatDate();

  const resetAndCloseDrawers = () => {
    form.reset();
    invalidateTimeOffNotifications();
    updateRequestsCollaborator();
    closePolicyTypeDrawer?.();
    closeDrawer();
  };

  const { mutate, isLoading: isLoadingMutation } = useMutation(
    (body: RequestTimeOff) => createVacationRequest(body),
    {
      onSuccess: (data, { attachments }) => {
        resetAndCloseDrawers();
        enqueueSnackbar({
          title: t('request_successfully_sent'),
          variant: 'success',
        });

        const requestId = data?.data?.id;

        logEvent(EventName.TIME_OFF_REQUEST_SUBMITTED, {
          source: isManagerRequesting
            ? VacationRequestSources.REQUEST_ON_BEHALF
            : VacationRequestSources.MY_TIME_OFF,
          description: data?.data?.description,
          policyTypeId: data?.data?.policyType?.id,
          requestId,
        });

        if (attachments?.length) {
          logEvent(EventName.TIME_OFF_DOCUMENTATION_ATTACHED, {
            source: VacationRequestSources.REQUEST_CREATION,
            requestState: data?.data?.state,
            requestId,
          });
        }
      },
      onError: (error: AxiosError<ResponseError>) => {
        const errorMessage = handleErrorResponse(error, t);
        showGeneralError(error, errorMessage);
      },
    },
  );

  const submit = async (values: RequestCreateForm) => {
    const { files, ...rest } = values;

    const rawFiles = files.map(file => file.file!);
    const filesToUpload = await processFilesVacations(rawFiles);

    if (!isValidFilesSize(filesToUpload)) {
      enqueueSnackbar({
        title: t('error_max_attachments_size_exceeded'),
        variant: 'error',
      });
      return;
    }

    const attachments = await uploadAllFiles(filesToUpload);

    const formattedData = formatValues(
      {
        ...rest,
        attachments,
        user,
        ...(isManagerRequesting && { issuerId: user.id }),
      },
      formatDate,
    );

    mutate(formattedData);
  };

  const handleSubmit = form.handleSubmit(submit);

  return {
    handleSubmit,
    isLoadingMutation,
    form,
    mutate,
  };
};
