import { type AxiosError } from 'axios';
import {
  differenceInCalendarDays,
  differenceInDays,
  getMonth,
  isAfter,
  isBefore,
  isEqual,
  isMonday,
  isToday,
  isValid,
  parse,
  parseISO,
  startOfWeek,
} from 'date-fns';
import { chunk, keys, pickBy, round } from 'lodash-es';
import {
  IconBabyCarriage,
  IconBandage,
  IconBeach,
  IconBriefcase,
  IconBuildingBank,
  IconBuildingChurch,
  IconBuildingHospital,
  IconBuildingSkyscraper,
  IconBusStop,
  IconCake,
  IconCalendarEvent,
  IconCalendarOff,
  IconClipboardText,
  IconDisabled,
  IconFirstAidKit,
  IconGavel,
  IconLuggage,
  IconMapPin,
  IconMessageReport,
  IconMoodKid,
  IconMoodSadDizzy,
  IconMoodSick,
  IconOld,
  IconPlaneDeparture,
  IconPlant2,
  IconSchool,
  IconSchoolBell,
  IconTemperature,
  IconVaccine,
  IconVirus,
} from '@material-hu/icons/tabler';
import { type Theme } from '@material-hu/mui/styles';

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

import {
  HoursRequestType,
  type RequestCreateForm,
} from 'src/pages/dashboard/timeOff/hooks/useRequestCreate';
import {
  getListPolicyTypes,
  updateRedBubblesManager,
} from 'src/pages/dashboard/timeOff/queries';
import { type FormFile } from 'src/types/attachments';
import { Days } from 'src/types/date';
import { type ResponseError } from 'src/types/services';
import { type MeUser, type User } from 'src/types/user';
import {
  AllowanceType,
  type ApprovalStep,
  type BlockedDateError,
  type ConsumptionType,
  type FutureBalanceResponse,
  type HolidayByDate,
  type HolidaysInfo,
  type ListTitleTableVacations,
  type NewHiresRequestsBanErrors,
  OperationsBalance,
  type ParamsGetWidthToRenderRequestInCalendar,
  type Policy,
  type PolicyTypes,
  ReportType,
  RequestedDaysErrors,
  type RequestTimeOff,
  type RequiredPolicyTypeErrors,
  ResponseCode,
  type ResponseDetailPolicyTypes,
  type ResponseRequestTimeOff,
  type SectionsManager,
  StepStatus,
  type TimeOffRequestValues,
  UnitVacations,
  VacationStatus,
  type VacationsFilterValues,
  Visibility,
} from 'src/types/vacations';
import { processAttachment } from 'src/utils/attachments';
import { formatDateInVacations } from 'src/utils/date';

import { insertIf } from './arrays';
import { type LokaliseTFunction } from './i18n';

const TOTAL_EXTRACT_WIDTH_SAME_MONTH = 12;
const TOTAL_EXTRACT_WIDTH_DIFFERENT_MONTH = 9;

const JURISDICTION_RULES_BRAZIL_MESSAGES = 'jurisdiction_rules.brazil.messages';
const BRAZILIAN_MIN_DAYS_PER_PERIOD_REQUIRED = 5;
const BRAZILIAN_MIN_MAIN_PERIOD_REQUIRED = 14;

export const iconsPolicyTypes = {
  SickOutlined: IconMoodSick,
  BeachAccessRounded: IconBeach,
  VaccinesOutlined: IconVaccine,
  GavelOutlined: IconGavel,
  ChurchOutlined: IconBuildingChurch,
  AirplaneTicketOutlined: IconPlaneDeparture,
  ChildCareOutlined: IconMoodKid,
  ChildFriendlyOutlined: IconBabyCarriage,
  HealthandSafetyOutlined: IconBuildingHospital,
  HealingOutlined: IconBandage,
  MedicalServicesOutlined: IconFirstAidKit,
  PregnantWomanOutlined: IconBabyCarriage,
  EventOutlined: IconCalendarEvent,
  SchoolOutlined: IconSchool,
  ElderlyOutlined: IconOld,
  LuggageOutlined: IconLuggage,
  ThermostatOutlined: IconTemperature,
  SentimentVeryDissatisfiedOutlined: IconMoodSadDizzy,
  WorkOutlineOutlined: IconBriefcase,
  CakeOutlined: IconCake,
  RoomServiceOutlined: IconSchoolBell,
  SpaOutlined: IconPlant2,
  VillaOutlined: IconBuildingSkyscraper,
  BusAlertOutlined: IconBusStop,
  EventBusyOutlined: IconCalendarOff,
  CoronavirusOutlined: IconVirus,
  AccountBalanceOutlined: IconBuildingBank,
  AnnouncementOutlined: IconMessageReport,
  AssignmentOutlined: IconClipboardText,
  AccessibleForwardOutlined: IconDisabled,
  CardTravelOutlined: IconBriefcase,
  LocationOnOutlined: IconMapPin,
};

const SCREEN_SIZE_MAP = {
  sm: 2,
  md: 3,
  lg: 4,
  xs: 1,
};

// -----------------------------------------------------------------------------
// Time-off request date rules (policies: Monday start, multiples of seven)
// Shared by request form validators and date picker disabling logic.
// -----------------------------------------------------------------------------

export function getInclusiveCalendarDaySpan(from: Date, to: Date) {
  return differenceInCalendarDays(to, from) + 1;
}

export function isMultiplesOfSevenSpanValid(
  inclusiveDaySpan: number,
  currentBalance: number | undefined,
) {
  const isMultipleOf7 = inclusiveDaySpan % 7 === 0;
  const isFullBalance =
    currentBalance !== undefined && inclusiveDaySpan === currentBalance;
  return isMultipleOf7 || isFullBalance;
}

/** Nominal outcome for Monday-start rule: each variant is a full case (no generic ok + reason). */
export type MondayStartRuleOutcome =
  | 'allowed'
  | 'holiday'
  | 'must_start_on_monday';

export function isMondayStartAllowed(outcome: MondayStartRuleOutcome) {
  return outcome === 'allowed';
}

/** Shared Monday-start policy evaluation for form validators and date pickers. */
export function evaluateMondayStartDateRule(
  date: Date,
  holidays: HolidayByDate[],
  formatDateKey: (date: Date) => string,
  getNextBusinessDay: (date: Date) => Date,
) {
  const key = formatDateKey(date);
  if (holidays.some(h => h.date === key)) {
    return 'holiday';
  }
  if (isMonday(date)) {
    return 'allowed';
  }
  const mondayOfWeek = startOfWeek(date, { weekStartsOn: 1 });
  const mondayKey = formatDateKey(mondayOfWeek);
  const isMondayHoliday = holidays.some(h => h.date === mondayKey);
  if (isMondayHoliday) {
    const nextBusiness = getNextBusinessDay(mondayOfWeek);
    if (formatDateKey(nextBusiness) === key) {
      return 'allowed';
    }
  }
  return 'must_start_on_monday';
}

export const reportBalanceTableHeader = (t: LokaliseTFunction) => {
  const listReportBalancesHeader: ListTitleTableVacations[] = [
    {
      id: 'NAME',
      text: t('NAME'),
    },
    {
      id: 'USER',
      text: t('USER'),
    },
    {
      id: 'POLICY_TYPE',
      text: t('POLICY_TYPE_ONE'),
    },
    {
      id: 'TIME_USED',
      text: t('TIME_USED'),
    },
    {
      id: 'AVAILABLE_TIME',
      text: t('AVAILABLE_TIME'),
    },
    {
      id: 'ACTIONS',
      text: '',
    },
  ];
  return listReportBalancesHeader.filter(element => element);
};

export const vacationsTableHeader = (
  t: LokaliseTFunction,
  isManagerView: boolean,
): ListTitleTableVacations[] => {
  const listVacationsHeader: ListTitleTableVacations[] = [
    ...insertIf(isManagerView, {
      id: 'NAME',
      text: t('NAME'),
    }),
    ...insertIf(isManagerView, {
      id: 'USER',
      text: t('USER'),
    }),
    {
      id: 'POLICY_TYPE',
      text: isManagerView ? t('POLICY_TYPE_ONE') : t('REQUEST_TYPE_ONE'),
    },
    {
      id: 'STATE',
      text: t('general:state'),
    },
    {
      id: 'REQUEST',
      text: t('REQUESTED_ON'),
    },
    {
      id: 'DATES',
      text: t('DATES'),
    },
    ...insertIf(isManagerView, {
      id: 'DURATION',
      text: t('DURATION'),
    }),
  ];

  return listVacationsHeader.filter(element => element);
};

export const getDaysInMonth = (month: Date) => {
  if (!month) return [];
  const amountDaysInMonth = new Date(
    month.getFullYear(),
    month.getMonth() + 1,
    0,
  ).getDate();
  return new Array(amountDaysInMonth).fill({});
};

export const getHolidaysDayInfo = (date: Date, holidays: HolidaysInfo[]) =>
  holidays?.find(h => isEqual(new Date(parseISO(h.date)), date));

export const getWidthToRenderRequestInCalendar = ({
  request,
  differentsMonth = false,
  amountDaysInMonth = 0,
  cellWidth = 0,
}: ParamsGetWidthToRenderRequestInCalendar) => {
  if (!differentsMonth) {
    const daysRequested =
      differenceInDays(
        new Date(parseISO(request.to.date)),
        new Date(parseISO(request.from.date)),
      ) + 1;

    return cellWidth * daysRequested - TOTAL_EXTRACT_WIDTH_SAME_MONTH;
  } else {
    if (amountDaysInMonth) {
      return (
        (amountDaysInMonth -
          new Date(parseISO(request.from.date)).getDate() +
          1) *
          cellWidth -
        TOTAL_EXTRACT_WIDTH_DIFFERENT_MONTH
      );
    } else {
      return (
        new Date(parseISO(request.to.date)).getDate() * cellWidth -
        TOTAL_EXTRACT_WIDTH_DIFFERENT_MONTH
      );
    }
  }
};

export const getColorOfRequestState = (
  state: VacationStatus,
  theme: Theme,
  isCollaboratorView: boolean,
) => {
  if (isCollaboratorView) {
    return {
      backgroundColor: theme.palette.new.background.feedback.highlight,
      color: theme.palette.new.text.feedback.highlight,
      border: `1px solid ${theme.palette.new.border.states.highlight}`,
    };
  }

  if (state === VacationStatus.IN_PROGRESS) {
    return {
      backgroundColor: theme.palette.new.background.layout.brand,
      color: theme.palette.new.text.neutral.brand,
      border: `1px solid ${theme.palette.new.border.neutral.brand}`,
    };
  }

  if (state === VacationStatus.PENDING) {
    return {
      backgroundColor: theme.palette.new.background.feedback.warning,
      color: theme.palette.new.text.feedback.warning,
      border: `1px solid ${theme.palette.new.border.states.warning}`,
    };
  }

  return {
    color: theme.palette.new.text.feedback.success,
    backgroundColor: theme.palette.new.background.feedback.success,
    border: `1px solid ${theme.palette.new.border.states.success}`,
  };
};

export const checkRequestInDifferentsMonth = (
  request: ResponseRequestTimeOff,
) =>
  getMonth(new Date(parseISO(request.from.date))) !==
  getMonth(new Date(parseISO(request.to.date)));

export const checkBeginRequestInDate = (
  date: Date,
  request: ResponseRequestTimeOff,
) => isEqual(date, new Date(parseISO(request.from.date)));

export const checkEndRequestInDate = (
  date: Date,
  request: ResponseRequestTimeOff,
) => isEqual(date, new Date(parseISO(request.to.date)));

export const checkDateBetweenMonthsRequest = (
  date: Date,
  request: ResponseRequestTimeOff,
) => {
  const isDateBetweenMonths =
    getMonth(date) !== getMonth(new Date(parseISO(request.from.date))) &&
    getMonth(date) !== getMonth(new Date(parseISO(request.to.date)));

  return (
    isDateBetweenMonths &&
    isBefore(new Date(parseISO(request.from.date)), date) &&
    isAfter(new Date(parseISO(request.to.date)), date)
  );
};

export const checkIsFirstOfMonth = (date: Date) => date.getDate() === 1;

export const analizeRequest = (
  request: ResponseRequestTimeOff,
  date: Date,
  amountDaysInMonth: number,
  cellWidth: number,
  theme: Theme,
  isCollaboratorView: boolean,
) => {
  if (
    !checkBeginRequestInDate(date, request) &&
    !checkEndRequestInDate(date, request) &&
    !checkDateBetweenMonthsRequest(date, request)
  ) {
    return null;
  }

  const state = getCorrectState(request);
  const { backgroundColor, color, border } = getColorOfRequestState(
    state,
    theme,
    isCollaboratorView,
  );

  if (
    !checkRequestInDifferentsMonth(request) &&
    checkBeginRequestInDate(date, request)
  ) {
    return {
      backgroundColor,
      color,
      isBegin: true,
      isEnd: null,
      isInDifferentsMonth: false,
      width: getWidthToRenderRequestInCalendar({ request, cellWidth }),
      border,
    };
  }
  if (checkRequestInDifferentsMonth(request)) {
    if (checkDateBetweenMonthsRequest(date, request)) {
      if (checkIsFirstOfMonth(date)) {
        return {
          backgroundColor,
          color,
          isBegin: null,
          isEnd: null,
          isInDifferentsMonth: true,
          width: amountDaysInMonth * cellWidth - 1,
          border,
        };
      }
    } else if (checkBeginRequestInDate(date, request)) {
      return {
        backgroundColor,
        color,
        isBegin: true,
        isEnd: null,
        isInDifferentsMonth: true,
        width: getWidthToRenderRequestInCalendar({
          request,
          differentsMonth: true,
          amountDaysInMonth,
          cellWidth,
        }),
        border,
      };
    } else if (checkEndRequestInDate(date, request)) {
      return {
        backgroundColor,
        color,
        isBegin: null,
        isEnd: true,
        isInDifferentsMonth: true,
        width: getWidthToRenderRequestInCalendar({
          request,
          differentsMonth: true,
          cellWidth,
        }),
        border,
      };
    }
  }
  return null;
};

export const getCorrectBordersInRequest = (
  isInDifferentsMonth: boolean,
  isBegin: boolean,
  isEnd: boolean,
) => {
  if (!isInDifferentsMonth) {
    return { borderRadius: '12px' };
  } else if (isBegin && !isEnd) {
    return {
      borderTopLeftRadius: '8px',
      borderBottomLeftRadius: '8px',
      borderRight: '0',
    };
  } else if (!isBegin && isEnd) {
    return {
      borderTopRightRadius: '8px',
      borderBottomRightRadius: '8px',
      borderLeft: '0',
    };
  }
  return {
    borderRight: '0',
    borderLeft: '0',
  };
};

export const getRequestStateLegend = (t: LokaliseTFunction, theme: Theme) => {
  const listReportBalancesHeader = [
    {
      id: 'APPROVED_LEGEND',
      text: t('APPROVED_LEGEND'),
      backgroundColor: theme.palette.new.background.feedback.success,
      border: `1px solid ${theme.palette.new.border.states.success}`,
    },
    {
      id: 'IN_PROGRESS_LEGEND',
      text: t('IN_PROGRESS_LEGEND'),
      backgroundColor: theme.palette.new.background.layout.brand,
      border: `1px solid ${theme.palette.new.border.neutral.brand}`,
    },
    {
      id: 'PENDING_LEGEND',
      text: t('PENDING_LEGEND'),
      backgroundColor: theme.palette.new.background.feedback.warning,
      border: `1px solid ${theme.palette.new.border.states.warning}`,
    },
  ];
  return listReportBalancesHeader;
};

export const checkIsWorkingDay = (date: Date, workdays: string[]) => {
  if (!workdays?.length) return true;
  const dayOfWeek = Object.values(Days)[date.getDay()];
  return workdays?.some(day => day === dayOfWeek);
};

export const checkIsToday = (date: Date) => isToday(date);

export const getTitleTooltip = (
  isWorkingDay: boolean,
  isHolidaysDay: boolean,
  t: LokaliseTFunction,
) => {
  if (isHolidaysDay) return t('VACATIONS_HOLIDAYS');
  if (!isWorkingDay) return t('VACATIONS_DAYS_OFF');
  return '';
};

type ParamsFormatDateRequest = {
  date: Date;
  consumptionType: ConsumptionType;
  hourRequestType?: HoursRequestType | null;
  hour?: string;
  minutes?: string;
};

export const formatDateRequest = (
  params: ParamsFormatDateRequest,
  formatDate: (date: Date | string, pattern: string) => string,
) => {
  const { date, consumptionType, hourRequestType, hour, minutes } = params;
  return {
    date: formatDate(date, 'yyyy-MM-dd'),
    consumptionType: consumptionType,
    ...(hourRequestType === HoursRequestType.LESS_THAN_ONE_DAY && {
      time: hour && minutes ? formatHourTime(hour, minutes) : '',
    }),
  };
};

export const formatValues = (
  values: TimeOffRequestValues,
  formatDate: (date: Date | string, pattern: string) => string,
) => {
  const {
    fromDate,
    toDate,
    consumptionTypeFromDate,
    consumptionTypeToDate,
    issuerId,
    attachments,
    description,
    skipApproval,
    policyTypeId,
    fromHour,
    fromHourTime,
    toHour,
    toHourTime,
    hoursRequestType,
    hasSellingBalance,
    amountInMoney,
    substituteApprover,
    hasSubstituteApprover,
  } = values;

  const result = {
    issuerId,
    attachments,
    description,
    skipApproval,
    policyTypeId: policyTypeId?.value,
    from: undefined,
    to: undefined,
    ...(hasSubstituteApprover && {
      substituteApproverId: substituteApprover?.id,
    }),
    ...(hasSellingBalance && { amountInMoney: amountInMoney?.value }),
  } as RequestTimeOff;

  const isValidFromDate = isValid(fromDate);
  const isValidToDate = isValid(toDate);
  const theRequestIsLessThanOneDay =
    hoursRequestType === HoursRequestType.LESS_THAN_ONE_DAY;

  if (isValidFromDate) {
    result.from = formatDateRequest(
      {
        date: fromDate,
        consumptionType: consumptionTypeFromDate,
        hourRequestType: hoursRequestType,
        hour: fromHour,
        minutes: fromHourTime,
      },
      formatDate,
    );
  }

  if (isValidToDate || (theRequestIsLessThanOneDay && isValidFromDate)) {
    result.to = formatDateRequest(
      {
        date: theRequestIsLessThanOneDay ? fromDate : toDate,
        consumptionType: consumptionTypeToDate,
        hourRequestType: hoursRequestType,
        hour: toHour,
        minutes: toHourTime,
      },
      formatDate,
    );
  }

  return result;
};

export const formatEditRequest = (
  values: RequestCreateForm,
  formatDate: (date: Date | string, pattern: string) => string,
) => {
  return {
    amountInMoney: values.amountInMoney?.value,
    from: formatDateRequest(
      {
        date: values.fromDate,
        consumptionType: values.consumptionTypeFromDate,
        hourRequestType: values.hoursRequestType,
        hour: values.fromHour,
        minutes: values.fromHourTime,
      },
      formatDate,
    ),
    to: formatDateRequest(
      {
        date:
          values.hoursRequestType === HoursRequestType.LESS_THAN_ONE_DAY
            ? values.fromDate
            : values.toDate,
        consumptionType: values.consumptionTypeToDate,
        hourRequestType: values.hoursRequestType,
        hour: values.toHour,
        minutes: values.toHourTime,
      },
      formatDate,
    ),
  };
};

export const buildOptionsPolicyTypeBalance = (userId: number) => {
  const response = getListPolicyTypes(userId);
  return response?.data?.map((opt: PolicyTypes) => ({
    value: opt.id,
    label: { name: opt.name, icon: opt.icon },
  }));
};

export const getLabelByallowanceType = (policyType: PolicyTypes) => {
  if (policyType?.allowanceType === AllowanceType.BASIC_ANNUAL) {
    return 'AVAILABLE_TIME';
  } else {
    return 'TIME_USED';
  }
};

export const getTimeByUnit = (unit: UnitVacations) =>
  unit === UnitVacations.DAYS ? 'DAYS_WORD' : 'HOURS_WORD';

export const getCorrectColorStep = (state: StepStatus | VacationStatus) => {
  if (state === StepStatus.PENDING) {
    return 'warning';
  }
  if (state === StepStatus.APPROVED || state === VacationStatus.APPROVED) {
    return 'success';
  }
  if (state === StepStatus.REJECTED || state === VacationStatus.REJECTED) {
    return 'error';
  }
  return 'disabled';
};

export const getPillStep = (
  step: ApprovalStep,
  label: string,
  type: PillsProps['type'],
) => {
  if (step.state !== StepStatus.WAITING_OTHER_STEP) {
    return {
      label,
      type,
      hasIcon: false,
    };
  }
};

export const getActiveStep = (approvalSteps: ApprovalStep[]) =>
  approvalSteps.findIndex(elem => elem.state === StepStatus.APPROVED) + 1;

type ApproversAccumulator = { approvers: User[]; approversCount: number };

export const getStepToObtainApprovers = (approvalSteps: ApprovalStep[]) => {
  const { approvers, approversCount } = approvalSteps.reduce(
    (result: ApproversAccumulator, step: ApprovalStep, index: number) => {
      const wasRejectedInPrevStep = approvalSteps.some(
        (s: ApprovalStep, i: number) =>
          i < index && s.state === StepStatus.REJECTED,
      );
      return {
        approvers: wasRejectedInPrevStep
          ? result.approvers
          : result.approvers.concat(step.approver || step.potentialApprovers),
        approversCount: wasRejectedInPrevStep
          ? result.approversCount
          : result.approversCount +
            (step.approver
              ? 1
              : step.potentialApproversCount || step.potentialApprovers.length),
      };
    },
    { approvers: [], approversCount: 0 },
  );

  return {
    count: approversCount,
    approvers,
  };
};

export const getStepToEvaluate = (approvalSteps: ApprovalStep[]) =>
  approvalSteps?.find(elem => elem.state === StepStatus.PENDING);

export const getCorrectState = (
  vacation: ResponseRequestTimeOff,
): VacationStatus =>
  vacation?.stepState === StepStatus.PENDING &&
  vacation.state !== VacationStatus.CANCELLED &&
  vacation.canReview
    ? VacationStatus.PENDING
    : vacation.state;

const handleBlockedDatesError = (
  t: LokaliseTFunction,
  formatDate: (date: Date | string) => string,
  blockedDateError: BlockedDateError,
) => {
  return blockedDateError.blockedDates
    .map(date =>
      t('blocked_date_range', {
        fromDate: formatDate(date.fromDate),
        toDate: formatDate(date.toDate),
        name: date.name,
      }),
    )
    .join('\n');
};

export const getClientSideAmountPerRequestMessages = (
  t: LokaliseTFunction,
  requestedDays: number,
  policyType: PolicyTypes,
  isManagerRequesting: boolean,
) => {
  if (isManagerRequesting) {
    return [];
  }

  const messages: Array<{ title: string; description?: string }> = [];

  if (
    policyType?.minimumAmountPerRequest &&
    requestedDays < policyType.minimumAmountPerRequest
  ) {
    if (policyType.unit === UnitVacations.HOURS) {
      messages.push({
        title: t('minimum_allowed_error_hours', {
          count: requestedDays,
          amountAllowed: policyType?.minimumAmountPerRequest,
        }),
      });
    } else {
      messages.push({
        title: t('minimum_requested_days', {
          count: requestedDays,
          minimumAmount: policyType?.minimumAmountPerRequest,
        }),
      });
    }
  }

  if (
    policyType?.maximumAmountPerRequest &&
    requestedDays > policyType.maximumAmountPerRequest
  ) {
    if (policyType.unit === UnitVacations.HOURS) {
      messages.push({
        title: t('maximum_allowed_error_hours', {
          count: requestedDays,
          amountAllowed: policyType?.maximumAmountPerRequest,
        }),
      });
    } else {
      messages.push({
        title: t('maximum_requested_days', {
          count: requestedDays,
          maximumAmount: policyType?.maximumAmountPerRequest,
        }),
      });
    }
  }

  return messages;
};

const omitSuppressedPreviewErrorKeys = (
  policyType: PolicyTypes,
  record: Record<string, unknown>,
) =>
  Object.keys(record).filter(
    key =>
      !(
        policyType.immediateFutureDeduction &&
        key === RequestedDaysErrors.TIME_OFF_NEXT_CYCLE_REQUEST
      ),
  );

export const getTitleError = (
  t: LokaliseTFunction,
  requestedDays: number,
  policyType: PolicyTypes,
  errorsRequestedDays: Record<string, unknown>,
  formatDate: (date: Date | string) => string,
) => {
  const errorsToShow = [];
  // Once all errors are handled by the backend, this function should change.
  const isErrorRequestedDays =
    !!errorsRequestedDays[RequestedDaysErrors.TIME_OFF_OVERLAPPING_REQUEST];
  if (isErrorRequestedDays) {
    errorsToShow.push({
      title: t('time_off_overlapping_request'),
    });
  }

  const dateTooAdvanced =
    errorsRequestedDays[RequestedDaysErrors.TIME_OFF_FUTURE_REQUESTS_BAN] ||
    errorsRequestedDays[
      RequestedDaysErrors.TIME_OFF_FUTURE_REQUEST_TOO_ADVANCED
    ];
  if (dateTooAdvanced) {
    errorsToShow.push({
      title: t('time_off_future_request_banned'),
    });
  }

  const pendingRequests =
    errorsRequestedDays[
      RequestedDaysErrors.TIME_OFF_POLICY_PENDING_REQUESTS_AFFECT_BALANCE
    ];
  if (pendingRequests) {
    errorsToShow.push({
      title: t('time_off_policy_pending_requests_affect_balance'),
    });
  }

  const insufficientBalance =
    errorsRequestedDays[
      RequestedDaysErrors.TIME_OFF_POLICY_INSUFFICIENT_BALANCE
    ];

  if (insufficientBalance) {
    if (policyType.unit === UnitVacations.HOURS) {
      errorsToShow.push({
        title: t('no_remaining_balance_minimum_hours', {
          count: requestedDays,
          minimumBalance: policyType.minimumBalance,
        }),
      });
    } else {
      errorsToShow.push({
        title: t('no_remaining_balance_minimum', {
          count: requestedDays,
          minimumBalance: policyType.minimumBalance,
        }),
      });
    }
  }

  const newHiresRequestsBan = errorsRequestedDays[
    RequestedDaysErrors.TIME_OFF_NEW_HIRES_REQUESTS_BAN
  ] as NewHiresRequestsBanErrors;

  if (newHiresRequestsBan) {
    errorsToShow.push({
      title: t('time_off_new_hires_requests_ban', {
        value: newHiresRequestsBan.newHiresRequestsBanValue,
        unit: t(`${newHiresRequestsBan.newHiresRequestsBanUnit}_WORD`, {
          count: newHiresRequestsBan.newHiresRequestsBanValue,
        }).toLowerCase(),
      }),
    });
  }

  const futureRequestError =
    errorsRequestedDays[
      RequestedDaysErrors.TIME_OFF_FUTURE_REQUEST_NOT_ALLOWED
    ];
  if (futureRequestError) {
    errorsToShow.push({
      title: t('time_off_future_request_not_allowed'),
    });
  }

  const sellingBalanceError =
    errorsRequestedDays[
      RequestedDaysErrors.TIME_OFF_BALANCE_SALE_TIME_MUST_EXCEED_MONEY
    ];
  if (sellingBalanceError) {
    errorsToShow.push({
      title: t('time_off_request_policy_not_allowed_to_sell_balance'),
    });
  }

  const blockedDateError = errorsRequestedDays[
    RequestedDaysErrors.TIME_OFF_REQUEST_BLOCKED_DATES
  ] as BlockedDateError;

  if (blockedDateError) {
    errorsToShow.push({
      title: t('blocked_dates_error_title'),
      description: handleBlockedDatesError(t, formatDate, blockedDateError),
    });
  }

  const nextCycleRequestError =
    errorsRequestedDays[RequestedDaysErrors.TIME_OFF_NEXT_CYCLE_REQUEST];
  if (nextCycleRequestError && !policyType.immediateFutureDeduction) {
    errorsToShow.push({
      title: t('time_off_next_cycle_request_warning'),
    });
  }

  const minDaysPerPeriodError =
    errorsRequestedDays[
      RequestedDaysErrors.TIME_OFF_BRAZIL_MIN_DAYS_PER_PERIOD
    ];

  if (minDaysPerPeriodError) {
    errorsToShow.push({
      title: t(`${JURISDICTION_RULES_BRAZIL_MESSAGES}.min_days_per_period`, {
        minDays: BRAZILIAN_MIN_DAYS_PER_PERIOD_REQUIRED,
      }),
      description: t(
        `${JURISDICTION_RULES_BRAZIL_MESSAGES}.min_days_per_period_description`,
        {
          minDays: BRAZILIAN_MIN_DAYS_PER_PERIOD_REQUIRED,
        },
      ),
    });
  }

  const maxPeriodsExceededError =
    errorsRequestedDays[
      RequestedDaysErrors.TIME_OFF_BRAZIL_MAX_PERIODS_EXCEEDED
    ];
  if (maxPeriodsExceededError) {
    errorsToShow.push({
      title: t(`${JURISDICTION_RULES_BRAZIL_MESSAGES}.max_periods_exceeded`),
      description: t(
        `${JURISDICTION_RULES_BRAZIL_MESSAGES}.max_periods_exceeded_description`,
      ),
    });
  }

  const minMainPeriodRequiredError =
    errorsRequestedDays[
      RequestedDaysErrors.TIME_OFF_BRAZIL_MIN_MAIN_PERIOD_REQUIRED
    ];

  if (minMainPeriodRequiredError) {
    errorsToShow.push({
      title: t(`${JURISDICTION_RULES_BRAZIL_MESSAGES}.main_period_required`, {
        minDays: BRAZILIAN_MIN_MAIN_PERIOD_REQUIRED,
      }),
      description: t(
        `${JURISDICTION_RULES_BRAZIL_MESSAGES}.main_period_required_description`,
        { minDays: BRAZILIAN_MIN_MAIN_PERIOD_REQUIRED },
      ),
    });
  }

  if (
    omitSuppressedPreviewErrorKeys(policyType, errorsRequestedDays).length >
      0 &&
    errorsToShow.length === 0
  ) {
    errorsToShow.push({
      title: t('general_request_error'),
    });
  }

  return errorsToShow;
};

export type BuildCreateRequestValidationAlertsParams = {
  t: LokaliseTFunction;
  formatDate: (date: Date | string) => string;
  requestedDays: number;
  policyType: PolicyTypes;
  errorInAmountPerRequest?: boolean;
  errorsRequestedDays?: Record<string, unknown>;
  warningsRequestedDays: Record<string, unknown>;
  isManagerRequesting: boolean;
};

const MAX_CREATE_REQUEST_VALIDATION_ALERTS = 2;

export const buildCreateRequestValidationAlerts = (
  params: BuildCreateRequestValidationAlertsParams,
) => {
  const {
    t,
    formatDate,
    requestedDays,
    policyType,
    errorInAmountPerRequest,
    errorsRequestedDays,
    warningsRequestedDays,
    isManagerRequesting,
  } = params;

  const hasServerSideErrors =
    errorsRequestedDays && Object.keys(errorsRequestedDays).length > 0;
  const hasClientSideErrors = errorInAmountPerRequest;

  const clientSideAmountMessages = getClientSideAmountPerRequestMessages(
    t,
    requestedDays,
    policyType,
    isManagerRequesting,
  );

  const warningsToShow = [
    ...getTitleError(
      t,
      requestedDays,
      policyType,
      warningsRequestedDays,
      formatDate,
    ),
    ...(!hasClientSideErrors ? clientSideAmountMessages : []),
  ];

  const errorsToShow = [
    ...(hasServerSideErrors
      ? getTitleError(
          t,
          requestedDays,
          policyType,
          errorsRequestedDays,
          formatDate,
        )
      : []),
    ...(hasClientSideErrors ? clientSideAmountMessages : []),
  ];

  const errorAlerts = errorsToShow.map(item => ({
    ...item,
    severity: 'error' as const,
  }));

  const warningAlerts = warningsToShow.map(item => ({
    ...item,
    severity: 'warning' as const,
  }));

  const alertsToShow = [...errorAlerts, ...warningAlerts];

  return alertsToShow.slice(0, MAX_CREATE_REQUEST_VALIDATION_ALERTS);
};

export const getErrorInAmountPerRequest = (
  requestedDays: number,
  policyType: PolicyTypes,
  isManagerRequesting: boolean,
) => {
  if (isManagerRequesting) {
    return false;
  }

  if (
    !policyType.minimumAmountPerRequest &&
    !policyType.maximumAmountPerRequest
  ) {
    return false;
  }

  if (
    policyType.minimumAmountPerRequest &&
    !policyType.maximumAmountPerRequest
  ) {
    return requestedDays < policyType.minimumAmountPerRequest;
  }
  if (
    !policyType.minimumAmountPerRequest &&
    policyType.maximumAmountPerRequest
  ) {
    return requestedDays > policyType.maximumAmountPerRequest;
  }

  return (
    requestedDays < (policyType.minimumAmountPerRequest || 0) ||
    requestedDays > (policyType.maximumAmountPerRequest || 0)
  );
};

export const eventOperationsOptions = (t: LokaliseTFunction) => [
  { value: OperationsBalance.ADDITION, label: t('ADD') },
  { value: OperationsBalance.SUBTRACTION, label: t('SUBTRACTION') },
];

export const calculateRemainingAvailableTime = (
  operation: OperationsBalance,
  amountRequested: number,
  currentBalance: number,
  adjustTypeAmount: number,
  allowanceType: AllowanceType,
) => {
  let result = 0;
  if (allowanceType === AllowanceType.BASIC_ANNUAL) {
    if (operation === OperationsBalance.ADDITION) {
      result = currentBalance + adjustTypeAmount;
    } else {
      result = currentBalance - adjustTypeAmount;
    }
  } else {
    if (operation === OperationsBalance.ADDITION) {
      result = amountRequested + adjustTypeAmount;
    } else {
      result = amountRequested - adjustTypeAmount;
    }
  }
  return round(result, 2);
};

export const joinTwoDates = (
  firstDate: Date | string,
  secondDate: Date | string,
  loggedUser: MeUser,
) =>
  `${formatDateInVacations({ date: firstDate, user: loggedUser, type: 'P' })} - ${formatDateInVacations({ date: secondDate, user: loggedUser, type: 'P' })}`;

export const processFilesVacations = async (
  files: File[],
): Promise<FormFile[]> => {
  const attachments = await Promise.all(
    files.map(file => processAttachment({ file })),
  );

  return attachments;
};

export const getDefaultValueFiles = (
  vacations: Pick<ResponseRequestTimeOff, 'attachments'> | null | undefined,
): FileCardType[] | undefined =>
  vacations?.attachments?.length
    ? (vacations.attachments.map(elem => ({
        attachment: {
          ...elem,
          toUpload: false,
        },
        status: 'success' as const,
      })) as FileCardType[])
    : undefined;

export const checkSameNamefiles = (files: File[], droppedFiles: File[]) =>
  files.some(f => droppedFiles.some(df => f.name === df.name));

export const getCorrectStateInStep = (
  step: ApprovalStep,
  isManagerView: boolean,
  canReview: boolean,
) => {
  if (step.state === StepStatus.REJECTED) {
    return VacationStatus.REJECTED;
  }
  if (step.state === StepStatus.APPROVED) {
    return VacationStatus.APPROVED;
  }
  if (step.state === StepStatus.WAITING_OTHER_STEP) {
    return VacationStatus.IN_PROGRESS;
  }
  if (isManagerView && canReview) {
    return StepStatus.PENDING;
  }
  return VacationStatus.IN_PROGRESS;
};

export const analizeExpanded = (
  approvalSteps: ApprovalStep[],
  step: ApprovalStep,
) => {
  if (step.position === 1 && approvalSteps[1]?.state === StepStatus.PENDING) {
    return false;
  }
  if (step.state === StepStatus.WAITING_OTHER_STEP) {
    return false;
  }
  return true;
};

export const getAllProfileFields = (t: LokaliseTFunction) => {
  return [
    {
      value: 'COMPLETE_NAME',
      label: t('COMPLETE_NAME'),
      checked: true,
    },
    {
      value: 'EMAIL',
      label: t('EMAIL'),
      checked: false,
    },
    {
      value: 'DIRECT_BOSS',
      label: t('DIRECT_BOSS'),
      checked: false,
    },
    {
      value: 'BIRTHDATE',
      label: t('BIRTHDATE'),
      checked: false,
    },
    {
      value: 'HIRING_DATE',
      label: t('HIRING_DATE'),
      checked: false,
    },
    {
      value: 'EMPLOYEE_INTERNAL_ID',
      label: t('EMPLOYEE_INTERNAL_ID'),
      checked: true,
    },
  ];
};

export const getAllRequestFields = (
  reportType: ReportType,
  t: LokaliseTFunction,
) => {
  if (reportType === ReportType.BALANCES) {
    return [
      {
        value: 'POLICY_TYPE',
        label: t('POLICY_TYPE_ONE'),
        checked: true,
      },
      {
        value: 'POLICY',
        label: t('POLICY'),
        checked: true,
      },
      {
        value: 'CURRENT_BALANCE',
        label: t('AVAILABLE_TIME'),
        checked: true,
      },
      {
        value: 'AMOUNT_REQUESTED',
        label: t('TIME_USED'),
        checked: true,
      },
    ];
  }

  return [
    {
      value: 'POLICY_TYPE',
      label: t('POLICY_TYPE_ONE'),
      checked: true,
    },
    {
      value: 'POLICY',
      label: t('POLICY'),
      checked: true,
    },
    {
      value: 'CREATED_AT',
      label: t('REQUESTED_ON'),
      checked: true,
    },
    {
      value: 'FROM_DATE',
      label: t('FROM'),
      checked: true,
    },
    {
      value: 'TO_DATE',
      label: t('UNTIL'),
      checked: true,
    },
    {
      value: 'STATE',
      label: t('general:state'),
      checked: true,
    },
    {
      value: 'AMOUNT_REQUESTED',
      label: t('AMOUNT_REQUESTED'),
      checked: true,
    },
    {
      value: 'DESCRIPTION',
      label: t('OBSERVATIONS'),
      checked: true,
    },
    {
      value: 'DOCUMENTATION',
      label: t('DOCUMENTATION'),
      checked: true,
    },
    {
      value: 'FIRST_APPROVER',
      label: t('FIRST_APPROVER'),
      checked: true,
    },
    {
      value: 'SECOND_APPROVER',
      label: t('SECOND_APPROVER'),
      checked: true,
    },
    {
      value: 'THIRD_APPROVER',
      label: t('THIRD_APPROVER'),
      checked: true,
    },
    {
      value: 'FIRST_APPROVAL_DATETIME',
      label: t('FIRST_APPROVAL_DATETIME'),
      checked: false,
    },
    {
      value: 'SECOND_APPROVAL_DATETIME',
      label: t('SECOND_APPROVAL_DATETIME'),
      checked: false,
    },
    {
      value: 'THIRD_APPROVAL_DATETIME',
      label: t('THIRD_APPROVAL_DATETIME'),
      checked: false,
    },
  ];
};

export const uncheckAllOptions = (values: Record<string, unknown>) =>
  Object.keys(values).reduce<Record<string, boolean>>((obj, cur) => {
    obj[cur] = false;
    return obj;
  }, {});

type CheckInOption = { value: string | number; checked: boolean };

export const putCheckInOptions = (values: CheckInOption[]) =>
  values.reduce<Record<string, boolean>>((obj, cur) => {
    obj[String(cur.value)] = cur.checked;
    return obj;
  }, {});

export const getDefaultValuesInAdvancedReport = (
  requestFields: CheckInOption[],
  segmentations: { value: number; checked: boolean }[] = [],
  profileFields: { value: string; checked: boolean }[] = [],
) => ({
  requestFields: putCheckInOptions(requestFields),
  segmentations: putCheckInOptions(segmentations),
  profileFields: putCheckInOptions(profileFields),
  extensionType: 'XLSX',
  customError: null,
});

export const getOptionsSelected = (
  selectedFields: Record<string, boolean>,
  allFields: unknown[],
) => {
  const selectedIds = Object.keys(selectedFields)
    .filter(key => selectedFields[key])
    .map(Number);

  const selectedCount = selectedIds.length;

  return {
    selectedCount,
    allSelected: selectedCount !== 0 && selectedCount === allFields.length,
    someSelected: selectedCount > 0 && selectedCount < allFields.length,
  };
};

export const getHasEnabledSendReminder = (
  isManagerView: boolean,
  vacationDetail: ResponseRequestTimeOff | null | undefined,
  isAdmin: boolean,
) =>
  vacationDetail?.state === VacationStatus.IN_PROGRESS &&
  isAdmin &&
  isManagerView;

export const getHasEnabledApproveOrRejectRequest = (
  isManagerView: boolean,
  vacationDetail: ResponseRequestTimeOff | null | undefined,
  isAdmin: boolean,
) =>
  (isManagerView &&
    vacationDetail?.canReview &&
    vacationDetail?.state === VacationStatus.IN_PROGRESS &&
    vacationDetail?.stepState === StepStatus.PENDING) ||
  (isManagerView &&
    isAdmin &&
    vacationDetail?.state === VacationStatus.IN_PROGRESS);

export const wasRequestCreatedOnBehalf = (
  vacationDetail: ResponseRequestTimeOff,
) =>
  !!(
    vacationDetail?.creator &&
    vacationDetail?.issuer &&
    vacationDetail.creator.id !== vacationDetail.issuer.id
  );

export const getRequestStarter = (vacationDetail: ResponseRequestTimeOff) =>
  wasRequestCreatedOnBehalf(vacationDetail)
    ? vacationDetail?.creator
    : vacationDetail?.issuer;

export const isUserInPendingApprovalStep = (
  userId: number,
  approvalSteps: ApprovalStep[],
) => {
  const pendingStep = getStepToEvaluate(approvalSteps);
  return (
    pendingStep?.potentialApprovers?.some(approver => approver.id === userId) ??
    false
  );
};

const isRequestInProgress = (vacationDetail: ResponseRequestTimeOff) =>
  vacationDetail?.state === VacationStatus.IN_PROGRESS;

const isAdminAndCanCancelRequest = (
  vacationDetail: ResponseRequestTimeOff,
  isManagerView: boolean,
  isAdmin: boolean,
) =>
  isManagerView && vacationDetail?.state === VacationStatus.APPROVED && isAdmin;

/**
 * For on-behalf in-progress requests: hide Cancel when the starter is in the pending approval step
 * (they see only Approve/Reject). Admin is handled in getHasEnabledCancelRequest.
 */
const shouldHideCancelForInProgressRequest = (
  vacationDetail: ResponseRequestTimeOff,
  currentUserId: number,
  approvalSteps: ApprovalStep[],
): boolean => {
  if (
    !wasRequestCreatedOnBehalf(vacationDetail) ||
    vacationDetail?.state !== VacationStatus.IN_PROGRESS
  ) {
    return false;
  }
  return isUserInPendingApprovalStep(currentUserId, approvalSteps);
};

/**
 * Whether the current user can cancel this time-off request.
 *
 * - Approved requests: any admin can cancel from manager view.
 * - In progress: only the request starter can cancel. Starter = who created the request (issuer when self-request,
 *   creator when "on behalf"). So from collaborator view: can cancel if I created it; cannot cancel if created on my behalf.
 * - Manager view: starter in approval step or admin sees only Approve/Reject (no Cancel).
 */
export const getHasEnabledCancelRequest = (
  isManagerView: boolean,
  vacationDetail: ResponseRequestTimeOff,
  isAdmin: boolean,
  currentUserId: number,
  approvalSteps: ApprovalStep[] = [],
) => {
  if (!vacationDetail?.state) {
    return false;
  }

  // Approved in manager view: any admin can cancel.
  if (isAdminAndCanCancelRequest(vacationDetail, isManagerView, isAdmin)) {
    return true;
  }

  // Only the request starter can cancel: creator when "on behalf", issuer when self-request.
  // So from collaborator view: can cancel if I created it (issuer); cannot cancel if it was created on my behalf (creator created it).
  const starter = getRequestStarter(vacationDetail);
  const isCurrentUserTheStarter = !!starter && currentUserId === starter.id;

  if (!isCurrentUserTheStarter) {
    return false;
  }

  // In manager view only: admin never sees Cancel for in-progress (only Approve/Reject).
  if (isManagerView && isAdmin && isRequestInProgress(vacationDetail)) {
    return false;
  }

  // On-behalf in progress: hide when starter is in the pending approval step.
  if (
    shouldHideCancelForInProgressRequest(
      vacationDetail,
      currentUserId,
      approvalSteps,
    )
  ) {
    return false;
  }

  return isRequestInProgress(vacationDetail);
};

export const isDisabledCheckboxRequest = (
  request: ResponseRequestTimeOff,
  isAdmin: boolean,
) =>
  (request?.state === VacationStatus.IN_PROGRESS && !request?.canReview) ||
  (request?.state === VacationStatus.APPROVED && !isAdmin) ||
  request?.state === VacationStatus.REJECTED ||
  request?.state === VacationStatus.CANCELLED;

export const countCouldBeCancelled = (
  vacationData: ResponseRequestTimeOff[],
  requests: Record<number, boolean>,
) => ({
  count: vacationData.filter(
    vd => requests[vd.id] && vd?.state === VacationStatus.APPROVED,
  ).length,
  ids: vacationData
    .filter(vd => requests[vd.id] && vd?.state === VacationStatus.APPROVED)
    .map(vd => vd.id),
});

export const countCouldBeAcceptedRejected = (
  vacationData: ResponseRequestTimeOff[],
  requests: Record<number, boolean>,
) => ({
  count: vacationData.filter(
    vd =>
      requests[vd.id] &&
      vd?.state === VacationStatus.IN_PROGRESS &&
      vd?.canReview,
  ).length,
  ids: vacationData
    .filter(
      vd =>
        requests[vd.id] &&
        vd?.state === VacationStatus.IN_PROGRESS &&
        vd?.canReview,
    )
    .map(vd => vd.id),
});

export const getCheckedForEachCheckbox = (
  obj: Record<number, boolean>,
  cur: ResponseRequestTimeOff,
  requests: Record<number, boolean>,
  selectAllRequests: boolean,
  isAdmin: boolean,
) => {
  if (selectAllRequests) {
    const isDisabled = isDisabledCheckboxRequest(cur, isAdmin);
    return { ...obj, [cur.id]: !isDisabled };
  }
  if (Object.hasOwn(requests, cur.id)) {
    return { ...obj, [cur.id]: requests[cur.id] };
  }
  return { ...obj, [cur.id]: false };
};

type UpdateNotificationsManagerParams = {
  approvalSteps: ApprovalStep[];
  currentPosition: number | undefined;
  user: MeUser | null;
  state: VacationStatus;
};

export const updateNotificationsManager = ({
  approvalSteps,
  currentPosition,
  user,
  state,
}: UpdateNotificationsManagerParams) => {
  const nextStep =
    typeof currentPosition === 'number'
      ? approvalSteps[currentPosition]
      : undefined;
  const isApproverOfNextStep =
    user == null
      ? -1
      : (nextStep?.potentialApprovers?.findIndex(el => el.id === user.id) ??
        -1);
  if (
    currentPosition === 3 ||
    !nextStep ||
    isApproverOfNextStep === -1 ||
    state === VacationStatus.REJECTED
  ) {
    updateRedBubblesManager();
  }
};
type GetAmplitudeValuesForFiltersParams = {
  section: SectionsManager;
  values: Pick<VacationsFilterValues, 'onlyPeopleWithRequests'>;
  users: User[];
  availablePolicies: Policy[];
  policyIds: Record<number, boolean>;
  policyTypes: PolicyTypes[];
  policyTypeIds: Record<number, boolean>;
  states: Partial<Record<VacationStatus, boolean>>;
  fromDate: string | null;
  toDate: string | null;
  segmentation: VacationsFilterValues['segmentation'];
};

export const getAmplitudeValuesForFilters = ({
  section,
  values,
  users,
  availablePolicies,
  policyIds,
  policyTypes,
  policyTypeIds,
  states,
  fromDate,
  toDate,
  segmentation,
}: GetAmplitudeValuesForFiltersParams) => ({
  section,
  peopleWithRequests: values.onlyPeopleWithRequests,
  people: !!users.length,
  policies: availablePolicies
    .filter((ap: Policy) => policyIds[ap.id])
    .map((ap: Policy) => ap.name),
  policyTypes: policyTypes
    .filter((pt: PolicyTypes) => policyTypeIds[pt.id])
    .map((pt: PolicyTypes) => pt.name),
  states: Object.entries(states)
    .filter(([, value]) => value)
    .map(([key]) => key),
  requestPeriodFrom: !!fromDate,
  requestPeriodTo: !!toDate,
  segmentations: Object.values(segmentation).some(Boolean),
});

export const handleErrorResponse = (
  err: AxiosError<ResponseError>,
  t: LokaliseTFunction,
) => {
  const code = err.response?.data?.code;

  if (code === ResponseCode.VACATION_APPROVER_NOT_AVAILABLE_FOR_USER) {
    return t('APPROVER_NOT_AVAILABLE');
  }

  return t('GENERAL_REQUEST_ERROR');
};

export const getAmountDaysRequestColor = (
  policyType: PolicyTypes,
  realBalance: number,
  requestedDays: number,
  isRequestFromNextCycle: boolean,
) => {
  return policyType.allowanceType === AllowanceType.UNLIMITED ||
    realBalance - requestedDays >= (policyType.minimumBalance ?? 0) ||
    isRequestFromNextCycle
    ? 'info'
    : 'warning';
};

export const getAmountDaysRequestTitle = (
  requestedDays: number,
  policyType: PolicyTypes,
  amountInMoney: number,
  amountInTime: number,
  t: LokaliseTFunction,
) => {
  if (amountInMoney > 0) {
    return t('request_in_money_and_time_alert', {
      time: amountInTime,
      money: amountInMoney,
    });
  }

  if (policyType.unit === UnitVacations.DAYS) {
    return t('user_request', { count: requestedDays });
  }
  return t('user_request_hour', { count: requestedDays });
};

export const resolveRequestBalance = (
  currentCycleBalance: number,
  isRequestFromNextCycle: boolean,
  futureBalance: FutureBalanceResponse | undefined,
): number =>
  isRequestFromNextCycle && futureBalance
    ? futureBalance.totalAvailable
    : currentCycleBalance;

export const getAmountDaysRequestDescription = (
  policyType: PolicyTypes,
  realBalance: number,
  requestedDays: number,
  t: LokaliseTFunction,
  user: MeUser,
  isRequestFromNextCycle?: boolean,
  futureBalance?: FutureBalanceResponse,
) => {
  const showProjectedBalance =
    futureBalance &&
    isRequestFromNextCycle &&
    !policyType.immediateFutureDeduction;

  if (policyType.allowanceType === AllowanceType.BASIC_ANNUAL) {
    if (showProjectedBalance) {
      return (
        t(
          policyType.unit === UnitVacations.HOURS
            ? 'projected_balance_description_hours'
            : 'projected_balance_description',
          {
            count: round(realBalance - requestedDays, 2),
          },
        ) +
        ' ' +
        t('remaining_balance_when', {
          date: formatDateInVacations({
            date: futureBalance.cycle.startDate,
            user,
            type: 'P',
          }),
        })
      );
    }
    return t(
      policyType.unit === UnitVacations.HOURS
        ? 'remaining_balance_hours'
        : 'remaining_balance',
      {
        count: round(realBalance - requestedDays, 2),
      },
    );
  }
};

export const handleCustomErrors = (
  errors: Record<string, unknown>,
  t: LokaliseTFunction,
) => {
  if (errors[RequestedDaysErrors.TIME_OFF_POLICY_MUST_BE_USE_FIRST]) {
    const requiredPolicy = errors[
      RequestedDaysErrors.TIME_OFF_POLICY_MUST_BE_USE_FIRST
    ] as RequiredPolicyTypeErrors;

    return {
      title: t(`TIME_OFF_POLICY_MUST_BE_USE_FIRST`, {
        policyName: requiredPolicy.policy.name,
      }),
    };
  }

  if (errors[RequestedDaysErrors.TIME_OFF_ONLY_ADMIN_REQUESTS]) {
    return {
      title: t('ONLY_ADMIN_REQUESTS_ERROR'),
      description: t('ONLY_ADMIN_REQUESTS_ERROR_DESCRIPTION'),
    };
  }

  return null;
};

export const groupPolicyTypes = (
  policyTypes: PolicyTypes[],
  screenSize: 'sm' | 'md' | 'lg' | 'xs',
) => {
  return chunk(policyTypes, SCREEN_SIZE_MAP[screenSize]);
};

//This flag is used to determine if the report is large and should be processed by the browser or by the server
export const getResponseType = (count: number) =>
  count > 4500 ? 'json' : 'blob';

export const formatSegmentationFilters = (
  segmentation: VacationsFilterValues['segmentation'] = {},
) => {
  return keys(pickBy(segmentation, Boolean)).map(key =>
    key.replace('selected_', ''),
  );
};

export const formatHourTime = (hour: string, time: string) => {
  return `${hour}:${time}`;
};

export const fromHourIsBeforeToHour = (fromHour: string, toHour: string) => {
  const fromHourParsed = parse(fromHour, 'HH:mm', new Date());
  const toHourParsed = parse(toHour, 'HH:mm', new Date());

  return isBefore(fromHourParsed, toHourParsed);
};

export const hoursOptions = Array.from({ length: 24 }, (_, index) => {
  const hour = index;
  const time = `${hour.toString().padStart(2, '0')}`;
  return { value: time, label: time };
});

export const minutesOptions = Array.from({ length: 12 }, (_, index) => {
  const time = (index * 5).toString().padStart(2, '0');

  return { value: time, label: time };
});

export const timeToMinutes = (hour: string, minute: string) => {
  return parseInt(hour.toString()) * 60 + parseInt(minute.toString());
};

export const getTimeOptions = (minutes: number, interval: number) => {
  const HOUR_IN_MINUTES = 60;
  const DAY_IN_MINUTES = 24 * HOUR_IN_MINUTES;

  const slotsCount = Math.floor((DAY_IN_MINUTES - minutes) / interval);

  const validSlots = Array.from({ length: slotsCount }, (_, index) => {
    const totalMinutes = minutes + (index + 1) * interval;
    return {
      hour: Math.floor(totalMinutes / HOUR_IN_MINUTES),
      minute: totalMinutes % HOUR_IN_MINUTES,
    };
  });

  const uniqueHours = [...new Set(validSlots.map(slot => slot.hour))];
  const uniqueMinutes = [...new Set(validSlots.map(slot => slot.minute))];

  const hourValues = uniqueHours.map(hour => ({
    value: hour.toString().padStart(2, '0'),
    label: hour.toString().padStart(2, '0'),
  }));

  const minuteValues = uniqueMinutes
    .sort((a, b) => a - b)
    .map(minute => ({
      value: minute.toString().padStart(2, '0'),
      label: minute.toString().padStart(2, '0'),
    }));

  return { hourValues, minuteValues };
};

export const getVisibilityOptions = (
  isAdmin: boolean,
  isBalances: boolean,
  t: LokaliseTFunction,
) => [
  ...insertIf(isAdmin || !isBalances, {
    title: isAdmin ? t('see_all_collaborators') : t('default_visibility'),
    description: isAdmin
      ? t('see_all_collaborators_description')
      : t('default_visibility_description'),
    name: 'visibility.all',
  }),
  {
    title: t('see_direct_team'),
    description: t('see_direct_team_description'),
    name: 'visibility.directTeam',
  },
  {
    title: t('see_indirect_team'),
    description: t('see_indirect_team_description'),
    name: 'visibility.indirectTeam',
  },
];

export const getVisibility = (visibility: Record<string, boolean>) => {
  if (visibility.directTeam) {
    return Visibility.DIRECT_TEAM;
  }
  if (visibility.indirectTeam) {
    return Visibility.INDIRECT_TEAM;
  }

  return null;
};

export const getUnitTranslation = (
  policyTypeDetail: ResponseDetailPolicyTypes,
  count: number,
  t: LokaliseTFunction,
) =>
  t(policyTypeDetail.unit === UnitVacations.DAYS ? 'days_word' : 'hours_word', {
    count: count,
  }).toLowerCase() as string;

export const getVisibilityCount = (
  visibility: Record<string, boolean>,
  isAdmin: boolean,
  isBalances: boolean,
) => {
  if (isAdmin) {
    return visibility.all ? 0 : 1;
  }

  if (isBalances) {
    return visibility.indirectTeam ? 1 : 0;
  }

  return visibility.all ? 0 : 1;
};

export const getVisibilityDescription = (
  visibility: Record<string, boolean>,
  isAdmin: boolean,
  t: LokaliseTFunction,
) => {
  if (visibility.directTeam) {
    return t('see_direct_team');
  }
  if (visibility.indirectTeam) {
    return t('see_indirect_team_description');
  }
  if (isAdmin) {
    return t('see_all_collaborators');
  }

  return t('default_visibility');
};

export const getSymbolByAmount = (amount: number, futureRequest?: boolean) => {
  if (futureRequest && amount > 0) {
    return '-';
  }
  if (amount === 0) {
    return '';
  }

  return '+';
};

export type SellingBalanceRegime = 'colombia' | 'brazil';

type GetMaxSellableBalanceDaysParams = {
  currentBalance: number;
  amountInTime: number;
  minimumBalance?: number | null;
  allowanceAmount?: number;
  regime: SellingBalanceRegime;
};

export const getMaxSellableBalanceDays = ({
  currentBalance,
  amountInTime,
  minimumBalance,
  allowanceAmount,
  regime,
}: GetMaxSellableBalanceDaysParams): number => {
  const availableAfterRequest =
    currentBalance - amountInTime - (minimumBalance ?? 0);

  const colombiaCap = amountInTime - 1;
  const brazilCap = Math.floor((allowanceAmount ?? 0) / 3);

  if (regime === 'colombia') {
    return Math.max(0, Math.min(availableAfterRequest, colombiaCap));
  }

  return Math.max(0, Math.min(availableAfterRequest, brazilCap));
};

export const getSellingBalanceOptions = (allowedToSell: number) => {
  if (allowedToSell <= 0) {
    return [
      {
        label: '0',
        value: 0,
      },
    ];
  }

  const options = Array.from(
    { length: allowedToSell },
    (_, index) => index + 1,
  ).map(option => ({
    value: option,
    label: option.toString(),
  }));

  return options;
};

export const formatVacationDuration = (
  duration: number,
  unit: UnitVacations,
  t: LokaliseTFunction,
) => {
  const durationRounded = round(duration, 2);
  return unit === UnitVacations.DAYS
    ? t('total_days', { count: durationRounded })
    : t('total_hours', { count: durationRounded });
};

export const isPolicyTypeDay = (policyType: PolicyTypes) =>
  policyType.unit === UnitVacations.DAYS;

export const isBasicAllowance = (allowanceType: AllowanceType) =>
  allowanceType === AllowanceType.BASIC_ANNUAL;

export const areDatesValid = (fromDate: Date, toDate: Date) => {
  if (!fromDate || !toDate) return false;

  return isBefore(fromDate, toDate) || isEqual(fromDate, toDate);
};
