import React, {
  useEffect, useState, useRef, RefObject,
} from 'react';
import moment, {Moment} from 'moment';
import 'react-dates/initialize';
import {DayPickerSingleDateController} from 'react-dates';

import {SingleDatePickerProps} from './types';
import DatePickerControls from './DatePickerControls';
import {useOnClickOutside} from './helpers';
import {DatePicker, DatePickerSelector, CommonStyles} from './styles';

const SingleDatePicker = ({
  date,
  elementId,
  placeholder = '',
  dateFormat = 'DD/MM/YYYY',
  onChange,
  onFocus,
  minDate,
  maxDate,
  position = 'bottom',
}: SingleDatePickerProps) => {
  const [isExpanded, setExpanded] = useState<boolean>(false);
  const [value, setValue] = useState<Moment | null>(null);
  const pickerRef = useRef<HTMLDivElement>();

  useEffect(() => {
    if (moment.isMoment(date)) {
      setValue(date);
    } else if (date) {
      console.error(`${date} isn't a moment object`);
    } else {
      setValue(null);
    }
  }, [date]);
  const onDateChange = (momentValue: Moment | null): void => {
    setValue(momentValue);

    if (momentValue) onChange(momentValue);
  };

  const onSelectDate = (momentValue: Moment | null): void => {
    onDateChange(momentValue);

    setExpanded(false);
  };

  const onFocusChange = (arg: { focused: boolean }): void => {
    if (onFocus && typeof onFocus === 'function') {
      onFocus(arg.focused);
    }
  };

  const getDefaultDate = () => {
    const dateNow = moment();
    const maxMin = minDate ? moment.max(dateNow, minDate) : dateNow;

    return maxDate ? moment.min(maxMin, maxDate) : maxMin;
  };

  const checkIsInMaxMinRange = (momentDate: Moment): boolean => (
    (!minDate || moment.duration(momentDate.diff(minDate.startOf('day'))).asDays()) >= 0
    && (!maxDate || moment.duration(momentDate.diff(maxDate)).asDays() <= 0)
  );

  const isValidDate = (inputValue: string): boolean => {
    const momentDate = moment(inputValue, dateFormat, true);

    return momentDate.isValid() && checkIsInMaxMinRange(momentDate);
  };

  const onInputChange = (inputValue: string) => {
    if (isValidDate(inputValue)) {
      onDateChange(moment(inputValue, dateFormat));
    }
  };

  const onEndChange = (inputValue: string) => {
    if (isValidDate(inputValue)) {
      onDateChange(moment(inputValue, dateFormat));
    } else {
      onDateChange(getDefaultDate());
    }

    setExpanded(false);
  };

  useOnClickOutside(pickerRef, () => {
    setExpanded(false);
  });

  const isDayBlocked = (current: Moment) => {
    const currentStartOfDay = current.startOf('day');

    return !checkIsInMaxMinRange(currentStartOfDay);
  };

  return (
    <DatePicker ref={pickerRef as RefObject<HTMLDivElement>}>
      <CommonStyles />
      <DatePickerControls
        elementId={elementId}
        placeholder={placeholder}
        onStartChange={() => setExpanded(true)}
        onChange={onInputChange}
        onEndChange={onEndChange}
        value={value}
        dateFormat={dateFormat}
      />
      {isExpanded && (
        <DatePickerSelector position={position}>
          <DayPickerSingleDateController
            onDateChange={onSelectDate}
            date={value}
            focused
            initialVisibleMonth={null}
            onFocusChange={onFocusChange}
            minDate={minDate}
            maxDate={maxDate}
            isDayBlocked={isDayBlocked}
            hideKeyboardShortcutsPanel
          />
        </DatePickerSelector>
      )}
    </DatePicker>
  );
};

export default SingleDatePicker;
