import * as React from 'react';
import Card from '../Card';
import Popover from '../Popover';
import ReactDatePicker from 'react-datepicker';
import Input from '../Input';
import { getMonth, getYear } from 'date-fns';
import { Shelf, Box } from '../Layout';
import Button from '../Button';
import Icon from '../Icon';
import useControlProp from '../../hooks/useControlProp';
import { Text } from '../Text';
import { Position } from '../Positioner';
import Loader from '../Loader';

import 'react-datepicker/dist/react-datepicker.css';
import styles from './DatePicker.module.scss';

const MONTHS = [
  'January',
  'February',
  'March',
  'April',
  'May',
  'June',
  'July',
  'August',
  'September',
  'October',
  'November',
  'December',
];

function isValidDateRange(value) {
  return value && typeof value === 'object' && 'startDate' in value && 'endDate' in value;
}

function parseDates(dates) {
  let start, end;

  if (Array.isArray(dates)) {
    [start, end] = dates;
  } else if (isValidDateRange(dates)) {
    ({ startDate: start, endDate: end } = dates);
  } else if (dates && typeof dates === 'object' && !(dates instanceof Date)) {
    ({ startDate: start, endDate: end } = dates);
  } else {
    start = dates instanceof Date && !isNaN(dates) ? dates : new Date();
    end = undefined;
  }

  return { start, end };
}

function useDatePickerState({
  value,
  onChange,
  defaultValue,
  selectsRange = false,
  shouldCloseOnSelect = false,
  onPopoverToggle = null,
}) {
  const [dateState, setDates] = useControlProp(value, onChange, defaultValue);
  const [month, setMonth] = React.useState(
    selectsRange && dateState?.startDate
      ? new Date(dateState.startDate)
      : dateState instanceof Date
      ? new Date(dateState)
      : new Date(),
  );

  const startDate = selectsRange ? dateState?.startDate : dateState;
  const endDate = selectsRange ? dateState?.endDate : null;

  React.useEffect(() => {
    if (startDate) {
      setMonth(new Date(startDate));
    }
  }, [startDate]);

  function handleDateChange(dates, shouldClose) {
    const { start, end } = parseDates(dates);

    if (selectsRange) {
      setDates({ startDate: start, endDate: end });
    } else {
      setDates(start);
    }

    if (start) {
      setMonth(new Date(start));
    }

    if (
      onPopoverToggle &&
      shouldClosePopover(selectsRange, end, shouldCloseOnSelect, shouldClose)
    ) {
      onPopoverToggle(false);
    }
  }

  return {
    startDate,
    endDate,
    month,
    setMonth,
    handleDateChange,
  };
}

function shouldClosePopover(selectsRange, end, shouldCloseOnSelect, explicitClose) {
  if (explicitClose) return true;
  if (selectsRange && end) return true;
  if (!selectsRange && shouldCloseOnSelect) return true;
  return false;
}

function formatInputValue(selectsRange, startDate, endDate, placeholder = 'No date selected') {
  if (!startDate) return placeholder;

  if (selectsRange) {
    return `${startDate.toDateString()} - ${endDate ? endDate.toDateString() : ''}`;
  }

  return startDate.toDateString();
}

function Picker({
  loading,
  startDate,
  endDate,
  onChange,
  shouldCloseOnSelect,
  selectsRange,
  footer,
  month,
  ...props
}) {
  return (
    <React.Fragment>
      <div
        className={styles.datepicker}
        style={{
          opacity: loading ? 0.5 : 1,
          pointerEvents: loading ? 'none' : 'auto',
          position: 'relative',
        }}
      >
        <ReactDatePicker
          inline
          selected={startDate}
          startDate={startDate}
          endDate={endDate}
          onChange={(date) => onChange(date, shouldCloseOnSelect)}
          selectsRange={selectsRange}
          shouldCloseOnSelect={shouldCloseOnSelect}
          month={month}
          monthsShown={1}
          renderCustomHeader={({
            date,
            decreaseMonth,
            increaseMonth,
            prevMonthButtonDisabled,
            nextMonthButtonDisabled,
          }) => (
            <Shelf justifyContent="space-between" alignItems="center">
              <Button
                icon={<Icon name="chevron-left" size={16} />}
                onClick={decreaseMonth}
                disabled={prevMonthButtonDisabled}
                appearance="minimal"
                intent="muted"
              />
              <Text>
                {MONTHS[getMonth(date)]} {getYear(date)}
              </Text>
              <Button
                icon={<Icon name="chevron-right" size={16} />}
                onClick={increaseMonth}
                disabled={nextMonthButtonDisabled}
                appearance="minimal"
                intent="muted"
              />
            </Shelf>
          )}
          {...props}
        />
        {loading && (
          <Box
            p={0}
            display="flex"
            justifyContent="center"
            alignItems="center"
            flex="1"
            position="absolute"
            top="0"
            left="0"
            right="0"
            bottom="0"
          >
            <Loader size={28} />
          </Box>
        )}
      </div>
      {typeof footer === 'function'
        ? footer({ startDate, endDate, setDates: onChange, setDate: onChange })
        : footer}
    </React.Fragment>
  );
}

function TargetPicker({
  selectsRange = false,
  shouldCloseOnSelect = false,
  onChange: onChangeProp,
  value,
  loading = false,
  width = '100%',
  disabled = false,
  position = Position.BOTTOM_RIGHT,
  defaultValue = selectsRange ? { startDate: new Date(), endDate: null } : new Date(),
  footer = null,
  placeholder = 'No date selected',
  customTarget = null,
  ...props
}) {
  const [popoverOpen, togglePopoverOpen] = React.useState(false);

  const { startDate, endDate, month, handleDateChange } = useDatePickerState({
    value,
    onChange: onChangeProp,
    defaultValue,
    selectsRange,
    shouldCloseOnSelect,
    onPopoverToggle: togglePopoverOpen,
  });

  const defaultTarget = (
    <div style={{ position: 'relative', width }}>
      <Input
        disabled={disabled}
        readOnly
        value={formatInputValue(selectsRange, startDate, endDate, placeholder)}
        icon={loading ? null : 'calendar'}
        iconColor="Mid Neutral 2"
        style={{ width: '100%' }}
      />
      {loading && (
        <div
          style={{
            position: 'absolute',
            top: 0,
            right: 0,
            bottom: 0,
            display: 'flex',
            alignItems: 'center',
            paddingRight: '12px',
          }}
        >
          <Loader size={16} color="Mid Neutral 2" />
        </div>
      )}
    </div>
  );

  const targetElement = customTarget
    ? typeof customTarget === 'function'
      ? customTarget({
          value: formatInputValue(selectsRange, startDate, endDate, placeholder),
          disabled,
          loading,
          onClick: () => togglePopoverOpen(!popoverOpen),
        })
      : customTarget
    : defaultTarget;

  return (
    <Popover
      shown={popoverOpen}
      onToggle={togglePopoverOpen}
      position={position}
      target={targetElement}
    >
      <Card>
        <Picker
          loading={loading}
          startDate={startDate}
          endDate={endDate}
          onChange={handleDateChange}
          shouldCloseOnSelect={shouldCloseOnSelect}
          selectsRange={selectsRange}
          footer={footer}
          month={month}
          {...props}
        />
      </Card>
    </Popover>
  );
}

function SimplePicker({
  value,
  onChange: onChangeProp,
  defaultValue,
  selectsRange = false,
  ...props
}) {
  const { startDate, endDate, month, handleDateChange } = useDatePickerState({
    value,
    onChange: onChangeProp,
    defaultValue,
    selectsRange,
  });

  return (
    <Picker
      startDate={startDate}
      endDate={endDate}
      onChange={handleDateChange}
      selectsRange={selectsRange}
      month={month}
      {...props}
    />
  );
}

function DatePicker({ noTarget = false, ...props }) {
  if (noTarget) {
    return <SimplePicker {...props} />;
  }

  return <TargetPicker {...props} />;
}

export default DatePicker;
