import React from 'react';
import { waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import renderWithTheme from '../../../testUtils/renderWithTheme';

import DateRangePicker from '../DateRangePicker';

describe('rendering', () => {
  it('renders two inputs and calendar', async () => {
    const { getByDisplayValue, getAllByText, getByTestId } = renderWithTheme(
      <DateRangePicker
        value={{
          startDate: '02/05/2021',
          endDate: '10/05/2021',
        }}
      />
    );

    await waitFor(() => {
      expect(getByDisplayValue('02/05/2021')).toBeInTheDocument();
      expect(getByDisplayValue('10/05/2021')).toBeInTheDocument();
    });

    userEvent.click(getByDisplayValue('02/05/2021'));

    await waitFor(() => {
      const firstMonthSelect = getByTestId('first-month-select');
      const firstYearSelect = getByTestId('first-year-select');
      const secondMonthSelect = getByTestId('second-month-select');
      const secondYearSelect = getByTestId('second-year-select');

      expect(firstMonthSelect).toSelectItem('May');
      expect(firstYearSelect).toSelectItem('2021');
      expect(secondMonthSelect).toSelectItem('Jun');
      expect(secondYearSelect).toSelectItem('2021');
      expect(getAllByText('10').length).toBe(3);
    });
  });

  it('renders two inputs with correct format', async () => {
    const { getByDisplayValue } = renderWithTheme(
      <DateRangePicker
        value={{
          startDate: '2021/05/02',
          endDate: '2021/05/10',
        }}
        format="yyyy/MM/dd"
      />
    );

    await waitFor(() => {
      expect(getByDisplayValue('2021/05/02')).toBeInTheDocument();
      expect(getByDisplayValue('2021/05/10')).toBeInTheDocument();
    });
  });
});

describe('interaction', () => {
  it('allows to blur', async () => {
    const onBlur = jest.fn();

    const { getByPlaceholderText } = renderWithTheme(
      <DateRangePicker
        value={{
          startDate: '02/05/2021',
          endDate: '10/05/2021',
        }}
        onBlur={onBlur}
        placeholder={{ startDate: 'Start', endDate: 'End' }}
      />
    );

    userEvent.click(getByPlaceholderText('Start'));
    userEvent.click(document.body);
    expect(onBlur).toHaveBeenCalledTimes(1);
  });
  it('allows to pick start date and then end date', async () => {
    const onChange = jest.fn();
    const { getByPlaceholderText, getAllByText, queryByText } = renderWithTheme(
      <DateRangePicker
        value={{
          startDate: '02/05/2021',
          endDate: '10/05/2021',
        }}
        onChange={onChange}
        placeholder={{ startDate: 'Start', endDate: 'End' }}
      />
    );

    userEvent.click(getByPlaceholderText('Start'));

    await waitFor(() => {
      // Calendar is open
      expect(getAllByText('Su')[0]).toBeVisible();
    });

    userEvent.click(getAllByText('9')[0]);

    await waitFor(() => {
      expect(onChange).toHaveBeenCalledTimes(1);
      expect(onChange).toHaveBeenCalledWith({
        startDate: '09/05/2021',
        endDate: '10/05/2021',
      });
      // Calendar is still open
      expect(getAllByText('Su')[1]).toBeVisible();
    });

    userEvent.click(getAllByText('15')[0]);

    await waitFor(() => {
      expect(onChange).toHaveBeenCalledTimes(2);
      expect(onChange).toHaveBeenCalledWith({
        startDate: '02/05/2021',
        endDate: '15/05/2021',
      });
      // Calendar is closed
      expect(queryByText('Su')).not.toBeInTheDocument();
    });
  });

  it('allows to pick end date and then start date', async () => {
    const onChange = jest.fn();
    const { getByPlaceholderText, getAllByText, queryByText } = renderWithTheme(
      <DateRangePicker
        value={{
          startDate: '02/05/2021',
          endDate: '10/05/2021',
        }}
        onChange={onChange}
        placeholder={{ startDate: 'Start', endDate: 'End' }}
      />
    );

    userEvent.click(getByPlaceholderText('End'));

    await waitFor(() => {
      // Calendar is open
      expect(getAllByText('Su')[0]).toBeVisible();
    });

    userEvent.click(getAllByText('9')[0]);

    await waitFor(() => {
      expect(onChange).toHaveBeenCalledTimes(1);
      expect(onChange).toHaveBeenCalledWith({
        startDate: '02/05/2021',
        endDate: '09/05/2021',
      });
      // Calendar is still open
      expect(getAllByText('Su')[1]).toBeVisible();
    });

    userEvent.click(getAllByText('8')[0]);

    await waitFor(() => {
      expect(onChange).toHaveBeenCalledTimes(2);
      expect(onChange).toHaveBeenCalledWith({
        startDate: '08/05/2021',
        endDate: '10/05/2021',
      });
      // Calendar is closed
      expect(queryByText('Su')).not.toBeInTheDocument();
    });
  });

  it('resets end date when new start date is after end date', async () => {
    const onChange = jest.fn();
    const { getByPlaceholderText, getAllByText } = renderWithTheme(
      <DateRangePicker
        value={{
          startDate: '02/05/2021',
          endDate: '10/05/2021',
        }}
        onChange={onChange}
        placeholder={{ startDate: 'Start', endDate: 'End' }}
      />
    );

    userEvent.click(getByPlaceholderText('Start'));

    await waitFor(() => {
      // Calendar is open
      expect(getAllByText('Su')[0]).toBeVisible();
    });

    userEvent.click(getAllByText('12')[0]);

    await waitFor(() => {
      expect(onChange).toHaveBeenCalledTimes(1);
      expect(onChange).toHaveBeenCalledWith({
        startDate: '12/05/2021',
        endDate: undefined,
      });
      // Calendar is still open
      expect(getAllByText('Su')[1]).toBeVisible();
    });
  });

  it('resets start date when new end date is before start date', async () => {
    const onChange = jest.fn();
    const { getByPlaceholderText, getAllByText } = renderWithTheme(
      <DateRangePicker
        value={{
          startDate: '02/05/2021',
          endDate: '10/05/2021',
        }}
        onChange={onChange}
        placeholder={{ startDate: 'Start', endDate: 'End' }}
      />
    );

    userEvent.click(getByPlaceholderText('End'));

    await waitFor(() => {
      // Calendar is open
      expect(getAllByText('Su')[0]).toBeVisible();
    });

    userEvent.click(getAllByText('1')[0]);

    await waitFor(() => {
      expect(onChange).toHaveBeenCalledTimes(1);
      expect(onChange).toHaveBeenCalledWith({
        startDate: undefined,
        endDate: '01/05/2021',
      });
      // Calendar is still open
      expect(getAllByText('Su')[1]).toBeVisible();
    });
  });

  it('does not allow to pick a date out of range', async () => {
    const onChange = jest.fn();
    const { getByPlaceholderText, getAllByText } = renderWithTheme(
      <DateRangePicker
        value={{
          startDate: '11/05/2021',
          endDate: '15/05/2021',
        }}
        minDate={new Date(2021, 4, 10)}
        maxDate={new Date(2021, 4, 20)}
        onChange={onChange}
        placeholder={{ startDate: 'Start', endDate: 'End' }}
      />
    );

    userEvent.click(getByPlaceholderText('Start'));

    await waitFor(() => {
      // Calendar is open
      expect(getAllByText('Su')[0]).toBeVisible();
    });

    await waitFor(() => {
      expect(() => userEvent.click(getAllByText('1')[0])).toThrowError(
        'unable to click element as it has or inherits pointer-events set to "none"'
      );
    });

    await waitFor(() => {
      expect(() => userEvent.click(getAllByText('21')[0])).toThrowError(
        'unable to click element as it has or inherits pointer-events set to "none"'
      );
    });
  });

  it('does not allow to edit disabled DateRangePicker', async () => {
    const { queryByText, getByDisplayValue } = renderWithTheme(
      <DateRangePicker
        value={{
          startDate: '11/05/2021',
          endDate: '15/05/2021',
        }}
        disabled
      />
    );

    userEvent.click(getByDisplayValue('11/05/2021'));

    await waitFor(() => {
      expect(queryByText('Su')).not.toBeInTheDocument();
    });
  });

  it('does not allow invalid value', () => {
    jest.spyOn(console, 'error').mockImplementation(() => '');

    expect(() =>
      renderWithTheme(
        <DateRangePicker
          value={{
            startDate: '1111/05/2021',
            endDate: '15111/05/2021',
          }}
        />
      )
    ).toThrowError('Invalid time value');
  });

  it('does not allow to clear value when clearable is false', () => {
    const { getByTestId, queryByTestId } = renderWithTheme(
      <DateRangePicker
        value={{
          startDate: '11/05/2021',
          endDate: '15/05/2021',
        }}
        data-test-id="date-range-picker"
        clearable={false}
      />
    );

    expect(queryByTestId('remove-icon')).not.toBeInTheDocument();

    userEvent.hover(getByTestId('date-range-picker'));

    expect(queryByTestId('remove-icon')).not.toBeInTheDocument();
  });

  it('does not allow to clear value when value is undefined', () => {
    const { getByTestId, queryByTestId } = renderWithTheme(
      <DateRangePicker data-test-id="date-range-picker" clearable={false} />
    );

    expect(queryByTestId('remove-icon')).not.toBeInTheDocument();

    userEvent.hover(getByTestId('date-range-picker'));

    expect(queryByTestId('remove-icon')).not.toBeInTheDocument();
  });

  it('allows to clear value when startDate is not undefined', () => {
    const onChange = jest.fn();
    const { getByTestId, queryByTestId } = renderWithTheme(
      <DateRangePicker
        value={{
          startDate: '11/05/2021',
          endDate: undefined,
        }}
        data-test-id="date-range-picker"
        onChange={onChange}
      />
    );

    expect(queryByTestId('remove-icon')).not.toBeInTheDocument();

    userEvent.hover(getByTestId('date-range-picker'));

    expect(queryByTestId('remove-icon')).toBeInTheDocument();
  });

  it('allows to clear value when endDate is not undefined', () => {
    const onChange = jest.fn();
    const { getByTestId, queryByTestId } = renderWithTheme(
      <DateRangePicker
        value={{
          startDate: undefined,
          endDate: '11/05/2021',
        }}
        data-test-id="date-range-picker"
        onChange={onChange}
      />
    );

    expect(queryByTestId('remove-icon')).not.toBeInTheDocument();

    userEvent.hover(getByTestId('date-range-picker'));

    expect(queryByTestId('remove-icon')).toBeInTheDocument();
  });

  it('allows to clear value', async () => {
    const onChange = jest.fn();
    const { getByTestId, queryByTestId, container } = renderWithTheme(
      <DateRangePicker
        value={{
          startDate: '11/05/2021',
          endDate: '15/05/2021',
        }}
        data-test-id="date-range-picker"
        onChange={onChange}
      />
    );

    expect(queryByTestId('remove-icon')).not.toBeInTheDocument();
    expect(container.querySelector('.hero-icon-calendar')).toBeInTheDocument();

    userEvent.hover(getByTestId('date-range-picker'));

    expect(container.querySelector('.hero-icon-calendar')).toBeNull();
    userEvent.click(getByTestId('remove-icon'));

    await waitFor(() => {
      expect(onChange).toHaveBeenCalledWith({
        startDate: undefined,
        endDate: undefined,
      });
    });
  });
});
