import React from 'react';
import {
  add, format, startOfDay, sub,
} from 'date-fns';
import { fireEvent, render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import DatePicker from './index';

describe('DatePicker', () => {
  describe('enforces [minDate, maxDate] range on input date through text', () => {
    const dateFormat = 'MM/dd/yyyy';
    const maxDate = startOfDay(new Date());
    const startDate = sub(maxDate, { months: 1 });
    const minDate = sub(maxDate, { years: 3 });

    it('repositions start date to minDate if a start date entered via text input is before minDate', () => {
      const onDateChange = jest.fn();
      const { container } = render(
        <DatePicker
          startDate={startDate}
          endDate={null}
          minDate={minDate}
          maxDate={maxDate}
          dateFormat={dateFormat}
          onDateChange={onDateChange}
        />,
      );
      const startDateBeforeMinDate = sub(minDate, { days: 1 });
      const startDateTextInputElement = container.querySelectorAll('[type="text"]')[0];
      userEvent.clear(startDateTextInputElement);
      userEvent.type(startDateTextInputElement, format(startDateBeforeMinDate, dateFormat));
      fireEvent.blur(startDateTextInputElement);
      expect(onDateChange).toBeCalledWith([minDate, null]);
      expect(onDateChange).toBeCalledTimes(1);
    });

    it('accepts input start date if the input start date is between the valid [minDate, maxDate] range', () => {
      const onDateChange = jest.fn();
      const { container } = render(
        <DatePicker
          startDate={startDate}
          endDate={null}
          minDate={minDate}
          maxDate={maxDate}
          dateFormat={dateFormat}
          onDateChange={onDateChange}
        />,
      );
      const validStartDate = add(startDate, { days: 1 });
      const startDateTextInputElement = container.querySelectorAll('[type="text"]')[0];
      userEvent.clear(startDateTextInputElement);
      userEvent.type(startDateTextInputElement, format(validStartDate, dateFormat));
      fireEvent.blur(startDateTextInputElement);
      expect(onDateChange).toBeCalledWith([validStartDate, null]);
      expect(onDateChange).toBeCalledTimes(1);
    });

    it('repositions start date to maxDate if a start date entered via text input is after maxDate', () => {
      const onDateChange = jest.fn();
      const { container } = render(
        <DatePicker
          startDate={startDate}
          endDate={null}
          minDate={minDate}
          maxDate={maxDate}
          dateFormat={dateFormat}
          onDateChange={onDateChange}
        />,
      );
      const startDateAfterMaxDate = add(maxDate, { days: 1 });
      const startDateTextInputElement = container.querySelectorAll('[type="text"]')[0];
      userEvent.clear(startDateTextInputElement);
      userEvent.type(startDateTextInputElement, format(startDateAfterMaxDate, dateFormat));
      fireEvent.blur(startDateTextInputElement);
      expect(onDateChange).toBeCalledWith([maxDate, null]);
      expect(onDateChange).toBeCalledTimes(1);
    });

    it('repositions start date and end date to minDate if an end date entered via text input is before minDate', () => {
      const onDateChange = jest.fn();
      const { container } = render(
        <DatePicker
          startDate={startDate}
          endDate={null}
          minDate={minDate}
          maxDate={maxDate}
          dateFormat={dateFormat}
          onDateChange={onDateChange}
        />,
      );
      const endDateBeforeMinDate = sub(minDate, { days: 1 });
      const endDateTextInputElement = container.querySelectorAll('[type="text"]')[1];
      userEvent.clear(endDateTextInputElement);
      userEvent.type(endDateTextInputElement, format(endDateBeforeMinDate, dateFormat));
      fireEvent.blur(endDateTextInputElement);
      expect(onDateChange).toBeCalledWith([minDate, minDate]);
      expect(onDateChange).toBeCalledTimes(1);
    });

    it('accepts input end date if the input end date is between the valid [minDate, maxDate] range and is after startDate', () => {
      const onDateChange = jest.fn();
      const { container } = render(
        <DatePicker
          startDate={startDate}
          endDate={null}
          minDate={minDate}
          maxDate={maxDate}
          dateFormat={dateFormat}
          onDateChange={onDateChange}
        />,
      );
      const validEndDate = add(startDate, { days: 1 });
      const endDateTextInputElement = container.querySelectorAll('[type="text"]')[1];
      userEvent.clear(endDateTextInputElement);
      userEvent.type(endDateTextInputElement, format(validEndDate, dateFormat));
      fireEvent.blur(endDateTextInputElement);
      expect(onDateChange).toBeCalledWith([startDate, validEndDate]);
      expect(onDateChange).toBeCalledTimes(1);
    });

    it('repositions end date to maxDate if an end date entered via text input is after maxDate', () => {
      const onDateChange = jest.fn();
      const { container } = render(
        <DatePicker
          startDate={startDate}
          endDate={null}
          minDate={minDate}
          maxDate={maxDate}
          dateFormat={dateFormat}
          onDateChange={onDateChange}
        />,
      );
      const endDateAfterMaxDate = add(maxDate, { days: 1 });
      const endDateTextInputElement = container.querySelectorAll('[type="text"]')[1];
      userEvent.clear(endDateTextInputElement);
      userEvent.type(endDateTextInputElement, format(endDateAfterMaxDate, dateFormat));
      fireEvent.blur(endDateTextInputElement);
      expect(onDateChange).toBeCalledWith([startDate, maxDate]);
      expect(onDateChange).toBeCalledTimes(1);
    });
  });
});
