// @flow
import React, {
  useState,
  useCallback,
  useMemo,
  type ComponentType,
} from 'react';
import styled from 'styled-components';
import DayPicker, { DateUtils } from 'react-day-picker';
import { injectIntl } from 'react-intl';
import type { InjectIntlProvidedProps, IntlShape } from 'react-intl';
import { Card } from '../../core/structures/card';
import { Popper } from '../../core/overlays/popper';
import { ClickOutside } from '../../core/behaviors/click-outside';
import { UnstyledButton } from '../../core/behaviors/unstyled-button';
import { getPaletteColor } from '../../mixins/branding/index';
import type { DateRangePickerProps } from './date-range-picker.proptypes';
import { getDateRangePickerStyles } from './date-range-picker.css-rules';

const DateRangeButton = styled(UnstyledButton)`
  border: 1px solid ${getPaletteColor('greyscale-light')};
  padding: 10px;
  width: 100%;
  text-align: center;
`;

const DayPickerContainer = styled(Card)`
  z-index: 30;
  background-color: ${getPaletteColor('white')};
  margin-bottom: 4px !important;
  margin-top: 4px;
  ${getDateRangePickerStyles}
`;

type InternalDateRangePickerProps = InjectIntlProvidedProps;

function isSelectingFirstDay(from: ?Date, to: ?Date, day: ?Date) {
  const isBeforeFirstDay = from && DateUtils.isDayBefore(day, from);
  const isRangeSelected = from && to;
  return !from || isBeforeFirstDay || isRangeSelected;
}

function getDateCard(from: ?Date, to: ?Date, intl: IntlShape) {
  if (!from && !to) {
    return null;
  }

  if (from && !to) {
    return `${intl.formatDate(from, {
      month: 'short',
      day: 'numeric',
    })} -`;
  }

  if (from && to) {
    if (DateUtils.isSameDay(from, to)) {
      return intl.formatDate(from, {
        month: 'short',
        day: 'numeric',
      });
    }

    return `${intl.formatDate(from, {
      month: 'short',
      day: 'numeric',
    })} - ${intl.formatDate(to, {
      month: 'short',
      day: 'numeric',
    })}`;
  }
}

const _DateRangePicker = ({
  range: { from, to },
  placeholder,
  onRangeChange,
  hideAfterSelection = false,
  // Internal Props
  intl,
}: DateRangePickerProps & InternalDateRangePickerProps) => {
  const [isVisible, setIsVisible] = useState(false);

  const toggleDatePicker = useCallback(() => {
    setIsVisible(vis => !vis);
  }, []);
  const hideDatePicker = useCallback(() => {
    setIsVisible(false);
  }, []);

  // Keep track of the last day for mouseEnter.
  const [enteredTo, setEnteredTo] = useState(to);

  const memoizedIsSelectingFirstDay = useMemo(
    () => day => isSelectingFirstDay(from, to, day),
    [from, to],
  );

  const handleDayClick = useCallback(
    day => {
      if (from && to && day >= from && day <= to) {
        onRangeChange({ from: undefined, to: undefined });
        setEnteredTo(undefined);
      } else if (memoizedIsSelectingFirstDay(day)) {
        const convertedDate = new Date(day.setHours(0, 0, 0, 0)); // we set the from to the start of the day

        onRangeChange({ from: convertedDate, to: undefined });
        setEnteredTo(undefined);
      } else {
        const convertedDate = new Date(day.setHours(23, 59, 59, 999)); // set the from to the very end of the day

        if (hideAfterSelection) {
          hideDatePicker();
        }
        onRangeChange({ from, to: convertedDate });
        setEnteredTo(day);
      }
    },
    [
      from,
      to,
      memoizedIsSelectingFirstDay,
      onRangeChange,
      hideAfterSelection,
      hideDatePicker,
    ],
  );

  const handleDayMouseEnter = useCallback(
    day => {
      if (!memoizedIsSelectingFirstDay(day)) {
        setEnteredTo(day);
      }
    },
    [memoizedIsSelectingFirstDay],
  );

  const cardDate =
    useMemo(() => getDateCard(from, to, intl), [from, to, intl]) || placeholder;

  const modifiers = { start: from, end: enteredTo };
  const disabledDays = { before: from };
  const selectedDays = [from, { from, to: enteredTo }];

  return (
    <ClickOutside onClickOutside={hideDatePicker}>
      <Popper
        reference={({ referenceProps, ref }) => (
          <DateRangeButton
            {...referenceProps}
            ref={ref}
            borderRadius={6}
            onClick={toggleDatePicker}
          >
            {cardDate}
          </DateRangeButton>
        )}
        isVisible={isVisible}
        placement="bottom"
      >
        {({ ref, style }) => (
          <DayPickerContainer style={style} ref={ref}>
            <DayPicker
              className="Range"
              numberOfMonths={2}
              selectedDays={selectedDays}
              disabledDays={disabledDays}
              modifiers={modifiers}
              onDayClick={handleDayClick}
              onDayMouseEnter={handleDayMouseEnter}
            />
          </DayPickerContainer>
        )}
      </Popper>
    </ClickOutside>
  );
};

export const DateRangePicker: ComponentType<DateRangePickerProps> = injectIntl(
  _DateRangePicker,
);

DateRangePicker.displayName = 'DateRangePicker';
