// @flow
import React, { useState, useEffect, type ComponentType } from 'react';
import { injectIntl } from 'react-intl';
import type { InjectIntlProvidedProps } from 'react-intl';
import styled from 'styled-components';
import moment from 'moment';
import DayPicker from 'react-day-picker';
import { pick, omit } from 'lodash';
import type { DatepickerProps } from './datepicker.proptypes';
import { datePickerStyles } from './datepicker.css-rules';
import { Box } from '../../primitives/box';
import { TextInput } from '../../core/forms/text-input';
import { ClickOutside } from '../../core/behaviors';
import { Popper } from '../../core/overlays/popper';
import { Card } from '../../core/structures/card';
import { getPaletteColor } from '../../mixins/branding';
import { INPUT_ROLE_ARIA_PROPS } from '../../mixins/aria';

// we put the date picker styles here so it's specific to this instance rather than globally
const StyledBox = styled(Box)`
  position: relative;
  && {
    ${datePickerStyles};
  }
`;

const PickerContainer = styled(Card)`
  border: 1px solid ${getPaletteColor('greyscale-light')};
  z-index: 100;
  margin-top: 10px;
  box-shadow: 0px 5px 15px rgba(59, 59, 59, 0.2);
  &:hover {
    border-color: ${getPaletteColor('greyscale-dark')};
  }
  &:focus-within {
    border-color: ${getPaletteColor('greyscale-black')};
  }
`;

const supportedDateFormats = {
  'en-GB': 'DD/MM/YYYY',
  'en-CA': 'YYYY-MM-DD',
  'en-US': 'MM/DD/YYYY',
};

const localeMaskOptions = {
  'en-GB': {
    datePattern: ['d', 'm', 'Y'],
    delimiter: '/',
  },
  'en-CA': {
    datePattern: ['Y', 'm', 'd'],
    delimiter: '-',
  },
  'en-US': {
    datePattern: ['m', 'd', 'Y'],
    delimiter: '/',
  },
};

const maskOptions = (
  locale: string,
): {| datePattern: string[], delimiter: string |} => {
  return localeMaskOptions[locale] || localeMaskOptions['en-CA'];
};

const dateFormat = (locale: string): string => {
  return supportedDateFormats[locale] || supportedDateFormats['en-CA'];
};

const dateToString = (date: Date, locale: string): string =>
  date ? moment(date).format(dateFormat(locale)) : '';

type InternalDatePickerProps = InjectIntlProvidedProps;

const _Datepicker = ({
  intl,
  label,
  name,
  onChange,
  value,
  disabledDates = [],
  disableWeekends = false,
  disablePastDates = false,
  className,
  'data-qa': dataQa,
  'data-cy': dataCy,
  id,
  ...otherProps
}: {|
  ...InternalDatePickerProps,
  ...DatepickerProps,
|}) => {
  const [isCalendarVisible, setIsCalendarVisible] = useState<boolean>(false);
  const { locale } = intl;
  const [inputText, setInputText] = useState(dateToString(value, locale));

  useEffect(() => setInputText(dateToString(value, locale)), [value, locale]);

  const handleDayClick = (day: Date) => {
    onChange(day);
    setInputText(moment(day).format(dateFormat(locale)));
    setIsCalendarVisible(false);
  };

  // TODO: this is a global side effect? What else does it affect?
  useEffect(() => {
    if (supportedDateFormats[locale]) {
      moment.locale(locale);
    }
  }, [locale]);

  useEffect(() => {
    if (!isCalendarVisible) {
      const date = moment(inputText, dateFormat(locale), true);
      if (!date.isValid()) {
        onChange(undefined);
      }
    }
  }, [inputText, isCalendarVisible, locale, onChange]);

  const handleInputChange = (raw?: string, masked?: string) => {
    setInputText(raw);
    const date = moment(masked, dateFormat(locale), true);
    if (date.isValid()) {
      onChange(date.toDate());
    }
  };

  const handleClickOutside = () => {
    setIsCalendarVisible(false);
  };

  return (
    <StyledBox
      stacked="column"
      {...omit(otherProps, INPUT_ROLE_ARIA_PROPS)}
      data-cy={dataCy}
      data-qa={dataQa}
      id={id}
    >
      <ClickOutside onClickOutside={handleClickOutside}>
        <Popper
          reference={({ referenceProps, ref }) => (
            <TextInput
              {...referenceProps}
              onRef={ref}
              type="custom"
              onFocus={() => setIsCalendarVisible(true)}
              onChange={handleInputChange}
              value={inputText}
              label={label}
              name={name}
              placeholder={dateFormat(intl.locale)}
              customMaskOptions={{
                date: true,
                ...maskOptions(intl.locale),
              }}
              className={className}
              {...pick(otherProps, INPUT_ROLE_ARIA_PROPS)}
            />
          )}
          isVisible={isCalendarVisible}
          placement="bottom-start"
        >
          {({ ref, style }) => (
            <PickerContainer style={style} ref={ref}>
              <DayPicker
                month={value}
                selectedDays={value}
                onDayClick={handleDayClick}
                locale={intl.locale}
                disabledDays={[
                  ...disabledDates,
                  { ...(disableWeekends ? { daysOfWeek: [0, 6] } : {}) },
                  { ...(disablePastDates ? { before: new Date() } : {}) },
                ]}
                showOutsideDays
                enableOutsideDaysClick
              />
            </PickerContainer>
          )}
        </Popper>
      </ClickOutside>
    </StyledBox>
  );
};

export const Datepicker: ComponentType<DatepickerProps> = injectIntl(
  _Datepicker,
);

Datepicker.displayName = 'Datepicker';
