import { useCallback, useEffect } from 'react';
import { useFormContext, useWatch } from 'react-hook-form';

import { addDays, isEqual } from 'date-fns';
import Stack from '@material-hu/mui/Stack';

import HuCardContainer from '@material-hu/components/design-system/CardContainer';
import HuFormDatePicker from '@material-hu/components/design-system/Inputs/DatePicker/form';
import HuFormInputSelect from '@material-hu/components/design-system/Inputs/Select/form';

import useFormatDate from 'src/hooks/useFormatDate';
import useRules, { type RulesOptions } from 'src/hooks/useRules';
import { getPolicyTypeById } from 'src/pages/dashboard/timeOff/queries';
import { type MeUser } from 'src/types/user';
import { ConsumptionType, type HolidayByDate } from 'src/types/vacations';
import { useLokaliseTranslation } from 'src/utils/i18n';
import {
  evaluateMondayStartDateRule,
  getInclusiveCalendarDaySpan,
  isMondayStartAllowed,
  isMultiplesOfSevenSpanValid,
} from 'src/utils/vacations';

import { formFieldsRequestCreate } from '../../constant';
import {
  HoursRequestType,
  type RequestCreateForm,
} from '../../hooks/useRequestCreate';
import { handleMinDate } from '../../utils/utils';

export type DateInputProps = {
  nameDateInput: string;
  nameConsumptionTypeInput: string;
  datePickerProps?: Partial<any>;
  description: string;
  rules?: RulesOptions;
  policyTypeId?: number;
  user: MeUser;
  isManagerRequesting: boolean;
  holidays: HolidayByDate[];
};

const DateInput = (props: DateInputProps) => {
  const {
    nameDateInput,
    nameConsumptionTypeInput,
    datePickerProps,
    description,
    rules,
    policyTypeId,
    user,
    isManagerRequesting,
    holidays,
  } = props;

  const form = useFormContext<RequestCreateForm>();
  const { t } = useLokaliseTranslation('time_off');
  const { formatDate } = useFormatDate();

  const [fromDate, toDate, consumptionTypeFromDate, hoursRequestType] =
    useWatch({
      name: [
        formFieldsRequestCreate.fromDate,
        formFieldsRequestCreate.toDate,
        formFieldsRequestCreate.consumptionTypeFromDate,
        formFieldsRequestCreate.hoursRequestType,
      ],
      control: form.control,
    });

  const policyType = getPolicyTypeById(policyTypeId!, user?.id);
  const requireMultiplesOfSeven = policyType?.requireMultiplesOfSeven;
  const requireMondayStart = policyType?.requireMondayStart;

  const formatDateKey = useCallback(
    (date: Date) => formatDate(date, 'yyyy-MM-dd'),
    [formatDate],
  );

  const getNextBusinessDay = useCallback(
    (date: Date): Date => {
      const next = addDays(date, 1);
      const key = formatDateKey(next);
      const isHoliday = holidays.some(h => h.date === key);
      const isWeekendDay = next.getDay() === 0 || next.getDay() === 6;

      if (isHoliday || isWeekendDay) {
        return getNextBusinessDay(next);
      }
      return next;
    },
    [holidays, formatDateKey],
  );

  const validDateRules = useRules(
    {
      checkValidFormatDate: true,
      checkYearsOfDate: true,
      requiredWithMessage: true,
      ...rules,
    },
    {
      mondayStart: (value: Date) => {
        if (isManagerRequesting) return true;
        if (!requireMondayStart) return true;
        if (nameDateInput !== formFieldsRequestCreate.fromDate) return true;

        const outcome = evaluateMondayStartDateRule(
          value,
          holidays,
          formatDateKey,
          getNextBusinessDay,
        );
        if (isMondayStartAllowed(outcome)) return true;
        if (outcome === 'holiday') {
          return t('validations:holiday_not_allowed');
        }
        return t('validations:must_start_on_monday');
      },
      multiplesOfSeven: (value: Date) => {
        if (isManagerRequesting) return true;
        if (!requireMultiplesOfSeven) return true;
        if (nameDateInput !== formFieldsRequestCreate.toDate) return true;
        if (!fromDate) return true;

        const inclusiveSpan = getInclusiveCalendarDaySpan(
          new Date(fromDate),
          value,
        );

        if (
          isMultiplesOfSevenSpanValid(inclusiveSpan, policyType?.currentBalance)
        ) {
          return true;
        }

        return t('validations:must_be_multiple_of_days', {
          days: 7,
        });
      },
    },
  );

  useEffect(() => {
    if (policyType?.allowHalfDayRequests && isEqual(toDate, fromDate)) {
      form.setValue(
        formFieldsRequestCreate.consumptionTypeToDate,
        consumptionTypeFromDate,
      );
    }
  }, [
    policyType?.allowHalfDayRequests,
    toDate,
    fromDate,
    consumptionTypeFromDate,
  ]);

  const inputOptions = Object.keys(ConsumptionType)
    .map(value => ({
      label: t('consumption_type', {
        context: value,
      }),
      value,
    }))
    .filter(value => value.value !== ConsumptionType.HOURS);

  const minDate =
    policyType?.noRetroactiveRequests && !isManagerRequesting
      ? handleMinDate(policyType)
      : undefined;

  const shouldDisableDate = useCallback(
    (date: Date) => {
      if (isManagerRequesting) return false;
      const isToDate = nameDateInput === formFieldsRequestCreate.toDate;
      const isFromDate = nameDateInput === formFieldsRequestCreate.fromDate;

      if (isToDate && requireMultiplesOfSeven && fromDate) {
        const inclusiveSpan = getInclusiveCalendarDaySpan(
          new Date(fromDate),
          date,
        );
        if (inclusiveSpan <= 0) return true;
        return !isMultiplesOfSevenSpanValid(
          inclusiveSpan,
          policyType?.currentBalance,
        );
      }

      if (isFromDate && requireMondayStart) {
        return !isMondayStartAllowed(
          evaluateMondayStartDateRule(
            date,
            holidays,
            formatDateKey,
            getNextBusinessDay,
          ),
        );
      }

      return false;
    },
    [
      holidays,
      formatDateKey,
      getNextBusinessDay,
      isManagerRequesting,
      nameDateInput,
      fromDate,
      requireMultiplesOfSeven,
      requireMondayStart,
      policyType?.currentBalance,
    ],
  );

  return (
    <HuCardContainer
      fullWidth
      color="grey"
    >
      <Stack
        sx={{
          gap: 1,
          width: '100%',
          flexDirection: 'row',
        }}
      >
        <HuFormDatePicker
          name={nameDateInput}
          inputProps={{
            helperText: description,
            minDate,
            shouldDisableDate,
            ...datePickerProps,
          }}
          rules={validDateRules}
        />
        {policyType?.allowHalfDayRequests &&
          hoursRequestType !== HoursRequestType.LESS_THAN_ONE_DAY && (
            <HuFormInputSelect
              name={nameConsumptionTypeInput}
              inputProps={{
                options: inputOptions,
                disabled:
                  nameDateInput === formFieldsRequestCreate.toDate &&
                  isEqual(toDate, fromDate),
                helperText:
                  nameDateInput === formFieldsRequestCreate.fromDate
                    ? t('duration_of_first_day')
                    : t('duration_of_last_day'),
                sx: { mt: 3 },
              }}
            />
          )}
      </Stack>
    </HuCardContainer>
  );
};

export default DateInput;
