import React, {
  ReactElement,
  MouseEvent,
  useState,
  useCallback,
  useEffect,
  useMemo,
} from 'react';
import getYear from 'date-fns/fp/getYear';
import addYears from 'date-fns/fp/addYears';
import subYears from 'date-fns/fp/subYears';
import setYear from 'date-fns/fp/setYear';
import isSameMonth from 'date-fns/fp/isSameMonth';

import Select from '../../Select';

import {
  Container,
  CalendarWrapper,
  Month,
  MonthNavigation,
  MonthGrid,
  MonthGridItem,
} from './StyledCalendar';

import { generateYearOptions, getMonthPickerOptions } from './utils';

interface Month {
  date: Date;
  disabled: boolean;
  text: string;
}

export interface MonthCalendarProps {
  maxDate?: Date;
  minDate?: Date;
  onSelectMonth?: (date: Date) => void;
  selectedMonth?: Date;
}

const getMonthState = ({
  month,
  selectedMonth,
  currentDate,
}: {
  currentDate: Date;
  month: Month;
  selectedMonth?: Date;
}): 'enabled' | 'disabled' | 'selected' | 'today' => {
  if (month.disabled) {
    return 'disabled';
  }

  if (selectedMonth !== undefined && isSameMonth(month.date, selectedMonth)) {
    return 'selected';
  }

  if (isSameMonth(month.date, currentDate)) {
    return 'today';
  }

  return 'enabled';
};

const MonthCalendar = ({
  selectedMonth,
  onSelectMonth,
  minDate,
  maxDate,
}: MonthCalendarProps): ReactElement => {
  const currentDate = new Date();
  const initialDate = selectedMonth !== undefined ? selectedMonth : currentDate;
  const [date, setDate] = useState<Date>(initialDate);
  const year = getYear(date);

  const calendarMinDate =
    minDate !== undefined ? minDate : subYears(100, currentDate);

  const calendarMaxDate =
    maxDate !== undefined ? maxDate : addYears(100, currentDate);

  const yearOptions = generateYearOptions({
    minDate: calendarMinDate,
    maxDate: calendarMaxDate,
  });

  const onChangeYear = useCallback(
    newYear => {
      setDate(setYear(newYear, date));
    },
    [date, setDate]
  );

  useEffect(() => {
    setDate(selectedMonth !== undefined ? selectedMonth : new Date());
  }, [selectedMonth, setDate]);

  const monthOptions = useMemo(
    () => getMonthPickerOptions({ year, minDate, maxDate }),
    [year, minDate, maxDate]
  );

  return (
    <Container>
      <MonthNavigation>
        <Select
          options={yearOptions}
          value={year}
          onChange={onChangeYear}
          data-test-id="year-select"
        />
      </MonthNavigation>
      <CalendarWrapper>
        <MonthGrid>
          {monthOptions.map(month => {
            const onClickMonth = (e: MouseEvent, clickedMonth: Month): void => {
              if (onSelectMonth !== undefined) {
                onSelectMonth(clickedMonth.date);
              }
              e.preventDefault();
            };
            const monthState = getMonthState({
              month,
              currentDate,
              selectedMonth,
            });
            return (
              <MonthGridItem key={month.text}>
                <Month
                  themeState={monthState}
                  onClick={
                    monthState !== 'disabled'
                      ? (e): void => onClickMonth(e, month)
                      : undefined
                  }
                >
                  {month.text}
                </Month>
              </MonthGridItem>
            );
          })}
        </MonthGrid>
      </CalendarWrapper>
    </Container>
  );
};

export default MonthCalendar;
