import React, { forwardRef } from 'react'
import { addDays, startOfDay } from 'date-fns'
import { fi, enUS } from 'date-fns/locale'
import { useTranslation } from 'react-i18next'
import { DateRange, DayPicker } from 'react-day-picker'
import { useMediaQuery } from 'react-responsive'

import { CalendarTypes } from './CalendarTypes'
import { IconsSvg } from '../../../assets/IconsSvg'
import {
  calendarSelectionRules,
  checkForContinuousSelection,
  handleCalendarModifiers,
  disabledDatesByPage as handleDisabledDatesByPage,
  handleRangeContextDisabledDates,
} from './utils'
import {
  useCalendarLoadingSpinner,
  useCalendarTooltips,
  useUpdateDisabledDates,
} from './hooks'
import { useAutoFocus } from '../../hooks'

import 'react-day-picker/dist/style.css'
import './Calendar.css'

export const Calendar = forwardRef<HTMLDivElement, CalendarTypes>(
  (
    {
      selectedPath,
      calendarRange,
      setCalendarRange,
      language,
      disableCalendarDates,
      requestDates,
      disabledDates,
      updateCalendarMonthNavigation,
      setUpdateCalendarMonthNavigation,
      updateCalendarDefaultMonth,
      loadingData,
      showFeedback,
      palette,
      setCalendarHasError,
      setUpdatedForSubmit,
      rangeContext,
      calendarHasError,
      autoFocus,
    }: CalendarTypes,
    ref
  ) => {
    // Translations
    const { t } = useTranslation('common')

    const calendarContainerRef = useAutoFocus<HTMLDivElement>(autoFocus)

    const isTablet = useMediaQuery({ maxWidth: 960 })
    const today = startOfDay(new Date())
    const selectedStartDate = calendarRange?.from
    const rangeContextStartDate = rangeContext?.from

    // Handle initial disable dates including overlapping availableDates.lastCheckOut and disabledDates.start
    const { newDisableCalendarDates, overlappingDate, lastPossibleCheckout } =
      useUpdateDisabledDates({
        disableCalendarDates,
        calendarRange,
        updateCalendarMonthNavigation,
        updateCalendarDefaultMonth,
      })

    // Handle disable dates by page
    const disabledDatesByPage = handleDisabledDatesByPage({
      today,
      selectedPath,
      newDisableCalendarDates,
    })

    // Handle tooltip
    useCalendarTooltips({
      showFeedback,
    })

    // Handle loading spinner
    useCalendarLoadingSpinner({
      loadingData,
    })

    // Handle the date selection and availability for selection logic.
    const handleOnSelect = (range?: DateRange) => {
      setCalendarHasError && calendarHasError && setCalendarHasError(false)

      calendarSelectionRules({
        range,
        newDisableCalendarDates,
        setCalendarRange,
        calendarRange,
        overlappingDate,
        setCalendarHasError,
        rangeContext,
      })

      setUpdatedForSubmit && setUpdatedForSubmit(true)
    }

    // Handle disabled dates for range context
    const {
      findFirstPossibleRangeContextCheckIn,
      findLastPossibleRangeContextCheckOut,
      firstPossibleRangeContextCheckIn,
      lastPossibleRangeContextCheckOut,
      currentSelectionAvailability,
    } = handleRangeContextDisabledDates({
      rangeContext,
      availableDates: newDisableCalendarDates?.availableDates,
      calendarRange,
    })

    // Handle check for continuous selection in the range context
    checkForContinuousSelection({
      setCalendarHasError,
      rangeContext,
      calendarRange,
      calendarHasError,
      disabledDates: newDisableCalendarDates?.disabledDates,
    })

    const disabledInsideSelectableRange = () => {
      if (
        // Range end already selected
        calendarRange?.to ||
        // No current check-in availability
        !currentSelectionAvailability ||
        // No gap between check-in and first possible check-out, nothing to disable
        addDays(currentSelectionAvailability.checkIn, 1) >=
          currentSelectionAvailability.firstCheckOut
      ) {
        return []
      }
      // Disable dates between current check-in and first possible check-out
      return [
        {
          from: addDays(currentSelectionAvailability.checkIn, 1),
          to: addDays(currentSelectionAvailability.firstCheckOut, -1),
        },
      ]
    }

    const base = disabledDatesByPage.length
      ? disabledDatesByPage
      : disabledDates?.length
        ? disabledDates
        : newDisableCalendarDates?.disabledDates || []

    const disabled = disabledDatesByPage.length
      ? base
      : [
          lastPossibleCheckout,
          ...base,
          ...firstPossibleRangeContextCheckIn,
          ...lastPossibleRangeContextCheckOut,
          ...disabledInsideSelectableRange(),
        ]

    return (
      <div className="will-filter-bar-calendar" ref={ref}>
        <div
          className="will-calendar-filter-container"
          ref={calendarContainerRef}
        >
          <DayPicker
            key={updateCalendarDefaultMonth}
            id="will-calendar"
            mode="range"
            locale={language === 'en' ? enUS : fi}
            numberOfMonths={!isTablet ? 2 : 1}
            weekStartsOn={1}
            selected={calendarRange}
            onSelect={(range) => handleOnSelect(range)}
            captionLayout="dropdown-buttons"
            defaultMonth={
              calendarRange?.from ||
              selectedStartDate ||
              rangeContextStartDate ||
              (newDisableCalendarDates?.disabledDates?.length
                ? newDisableCalendarDates.disabledDates[0].from
                : today)
            }
            disabled={disabled}
            fromMonth={today}
            onMonthChange={(val) => {
              requestDates?.(val)
              setUpdateCalendarMonthNavigation?.((prev) => !prev)
            }}
            classNames={{
              day_range_start: calendarRange?.from ? 'rdp-day_range_start' : '',
              day_range_end: calendarRange?.to ? 'rdp-day_range_end' : '',
            }}
            modifiersClassNames={{
              today: 'my-today',
              booked: 'booked',
              disabledAfterCheckIn: 'disabled-after-check-in',
              overlappingDate: 'overlapping-date',

              noActiveSelectionStart: 'no-active-selection-start',
              noActiveSelectionMid: 'no-active-selection-mid',
              noActiveSelectionEnd: 'no-active-selection-end',

              checkoutOptionsMid:
                'rdp-day_selected rdp-day_range_middle checkout-option',

              checkInOnly: 'check-in-only',
              checkOutOnly: 'check-out-only',
            }}
            modifiers={
              // This function handles conditions for applying the modifiersClassNames
              handleCalendarModifiers({
                calendarRange,
                disabledDates: disabled,
                overlappingDate,
                rangeContext,
                findFirstPossibleRangeContextCheckIn,
                findLastPossibleRangeContextCheckOut,
                currentSelectionLastCheckoutDate: currentSelectionAvailability,
              })
            }
          />

          <div className={'will-calendar-tooltip'}>
            <div>{t('noCheckIn')}</div>
          </div>
          <div className={'will-calendar-tooltip-check-out'}>
            <div>{t('noCheckOut')}</div>
          </div>
          <div className={'will-calendar-tooltip-overlapping-date'}>
            <div>{t('checkOutOnly')}</div>
          </div>
          <div className={'will-calendar-tooltip-check-in-only'}>
            <div>{t('checkInOnly')}</div>
          </div>
          <div className={'will-calendar-tooltip-check-out-only'}>
            <div>{t('checkOutOnly')}</div>
          </div>
          <div className={'will-calendar-spinner'}>
            <IconsSvg
              fill={palette?.primary || 'inherit'}
              size={50}
              icon="spinner"
            />
          </div>
        </div>
      </div>
    )
  }
)
