import React from 'react';

import '@testing-library/jest-dom';
import { fireEvent, render } from '@testing-library/react';
import { addMonths, format } from 'date-fns';

import { noop } from 'utils/common';

import { DatePicker, className as calendarClassName } from './DatePicker';
import { captionClassName, inputClassName, sidebarClassName } from './components';
import { DATE_PICKER_TYPE, SingleDatePickerProps, RangeDatePickerProps } from './types';
import { DATE_VIEW_FORMAT, DEFAULT_PREFERENCES, ONE } from './constants';
import { stringifyDate, stringifyDateRange } from './helpers';

const singleDatePickerProps: SingleDatePickerProps = {
  type: DATE_PICKER_TYPE.Single,
  onApply: noop,
};

const rangeDatePickerProps: RangeDatePickerProps = {
  type: DATE_PICKER_TYPE.Range,
  onApply: noop,
};

describe('Single date picker', () => {
  test('render date picker with initial value', () => {
    const initialValue = new Date('01/01/1970');
    const { container } = render(
      <DatePicker
        {...singleDatePickerProps}
        initialValue={initialValue}
      />,
    );

    const inputField = container.querySelector(`.${inputClassName('field')}`);
    const stringifiedValue = stringifyDate(DEFAULT_PREFERENCES, initialValue);

    expect(inputField).toHaveValue(stringifiedValue);
  });

  test('render date picker without initial value', () => {
    const { container } = render(<DatePicker {...singleDatePickerProps} />);

    const inputField = container.querySelector(`.${inputClassName('field')}`);

    if (inputField) {
      fireEvent.click(inputField);

      expect(inputField).toHaveValue('');
    } else {
      throw new Error('Input is not exist');
    }
  });

  test('render date picker calendar with current month on input click', () => {
    const { container } = render(<DatePicker {...singleDatePickerProps} />);

    const inputField = container.querySelector(`.${inputClassName('field')}`);

    if (inputField) {
      fireEvent.click(inputField);

      const calendar = container.querySelector(`.${calendarClassName('calendar')}`);
      const caption = container.querySelector(`.${captionClassName('label')}`);

      expect(calendar).not.toBeNull();
      expect(caption).toHaveTextContent(format(new Date(), DATE_VIEW_FORMAT));
    } else {
      throw new Error('Input is not exist');
    }
  });

  test('render disabled date picker', () => {
    const { container } = render(
      <DatePicker
        {...singleDatePickerProps}
        isDisabled
      />,
    );

    const input = container.querySelector(`.${inputClassName(null)}`);
    const inputField = container.querySelector(`.${inputClassName('field')}`);

    if (input) {
      fireEvent.click(input);

      const calendar = container.querySelector(`.${calendarClassName('calendar')}`);

      expect(input).toHaveClass(inputClassName(null, { isDisabled: true }));
      expect(inputField).toBeDisabled();
      expect(calendar).toBeNull();
    } else {
      throw new Error('Input is not exist');
    }
  });

  test('render error on applying invalid date', () => {
    const { container } = render(<DatePicker {...singleDatePickerProps} />);

    const inputField = container.querySelector(`.${inputClassName('field')}`);

    if (inputField) {
      fireEvent.click(inputField);
      fireEvent.change(inputField, { target: { value: '13/13/13' } });
      fireEvent.keyDown(inputField, { code: 'Enter' });

      const error = container.querySelector(`.${calendarClassName('error')}`);

      expect(error).not.toBeNull();
      expect(error).toHaveTextContent('Invalid Date.');
    } else {
      throw new Error('Input is not exist');
    }
  });

  test('apply date value', () => {
    const { container } = render(<DatePicker {...singleDatePickerProps} />);

    const date = '12/12/12';
    const inputField = container.querySelector(`.${inputClassName('field')}`);

    if (inputField) {
      fireEvent.change(inputField, { target: { value: date } });
      fireEvent.keyDown(inputField, { code: 'Enter' });

      const error = container.querySelector(`.${calendarClassName('error')}`);

      expect(error).toBeNull();
      expect(inputField).toHaveValue(date);
    } else {
      throw new Error('Input is not exist');
    }
  });

  test('prevent applying empty value for required value', () => {
    const { container } = render(
      <DatePicker
        {...singleDatePickerProps}
        isRequired
      />,
    );

    const inputField = container.querySelector(`.${inputClassName('field')}`);

    if (inputField) {
      fireEvent.click(inputField);
      fireEvent.change(inputField, { target: { value: '' } });
      fireEvent.keyDown(inputField, { code: 'Enter' });

      const error = container.querySelector(`.${calendarClassName('error')}`);

      expect(error).not.toBeNull();
      expect(inputField).toHaveValue('');
    } else {
      throw new Error('Input is not exist');
    }
  });
});

describe('Range date picker', () => {
  test('render date picker with initial value', () => {
    const initialValue = {
      from: new Date('01/01/1970'),
      to: new Date('01/01/1971'),
    };
    const { container } = render(
      <DatePicker
        {...rangeDatePickerProps}
        initialValue={initialValue}
      />,
    );

    const inputField = container.querySelector(`.${inputClassName('field')}`);
    const formattedValue = stringifyDateRange(DEFAULT_PREFERENCES, initialValue);

    expect(inputField).toHaveValue(formattedValue);
  });

  test('render date picker without initial value', () => {
    const { container } = render(<DatePicker {...rangeDatePickerProps} />);

    const inputField = container.querySelector(`.${inputClassName('field')}`);

    expect(inputField).toHaveValue('');
  });

  test('render date picker calendar with current month on input click', () => {
    const { container } = render(<DatePicker {...rangeDatePickerProps} />);

    const inputField = container.querySelector(`.${inputClassName('field')}`);

    if (inputField) {
      fireEvent.click(inputField);

      const calendar = container.querySelector(`.${calendarClassName('calendar')}`);
      const caption = container.querySelector(`.${captionClassName('label')}`);

      expect(calendar).not.toBeNull();
      expect(caption).toHaveTextContent(format(new Date(), DATE_VIEW_FORMAT));
    } else {
      throw new Error('Input is not exist');
    }
  });

  test('render disabled date picker', () => {
    const { container } = render(
      <DatePicker
        {...rangeDatePickerProps}
        isDisabled
      />,
    );

    const input = container.querySelector(`.${inputClassName(null)}`);
    const inputField = container.querySelector(`.${inputClassName('field')}`);

    if (input) {
      fireEvent.click(input);

      const calendar = container.querySelector(`.${calendarClassName('calendar')}`);

      expect(input).toHaveClass(inputClassName(null, { isDisabled: true }));
      expect(inputField).toBeDisabled();
      expect(calendar).toBeNull();
    } else {
      throw new Error('Input is not exist');
    }
  });

  test('render error on applying invalid date', () => {
    const { container } = render(<DatePicker {...rangeDatePickerProps} />);

    const inputField = container.querySelector(`.${inputClassName('field')}`);

    if (inputField) {
      fireEvent.click(inputField);
      fireEvent.change(inputField, { target: { value: '13/13/13 - 14/14/14' } });
      fireEvent.keyDown(inputField, { code: 'Enter' });

      const error = container.querySelector(`.${calendarClassName('error')}`);

      expect(error).not.toBeNull();
      expect(error).toHaveTextContent('Invalid Date Period.');
    } else {
      throw new Error('Input is not exist');
    }
  });

  test('apply date value', () => {
    const { container } = render(<DatePicker {...rangeDatePickerProps} />);

    const date = '11/11/11 - 12/12/12';
    const inputField = container.querySelector(`.${inputClassName('field')}`);

    if (inputField) {
      fireEvent.change(inputField, { target: { value: date } });
      fireEvent.keyDown(inputField, { code: 'Enter' });

      const error = container.querySelector(`.${calendarClassName('error')}`);

      expect(error).toBeNull();
      expect(inputField).toHaveValue(date);
    } else {
      throw new Error('Input is not exist');
    }
  });

  test('prevent applying empty value for required value', () => {
    const { container } = render(
      <DatePicker
        {...rangeDatePickerProps}
        isRequired
      />,
    );

    const inputField = container.querySelector(`.${inputClassName('field')}`);

    if (inputField) {
      fireEvent.click(inputField);
      fireEvent.change(inputField, { target: { value: '' } });
      fireEvent.keyDown(inputField, { code: 'Enter' });

      const error = container.querySelector(`.${calendarClassName('error')}`);

      expect(error).not.toBeNull();
      expect(inputField).toHaveValue('');
    } else {
      throw new Error('Input is not exist');
    }
  });

  test('enable sidebar if "presets" props passed', () => {
    const { container } = render(
      <DatePicker
        {...rangeDatePickerProps}
        presets={[]}
      />,
    );

    const inputField = container.querySelector(`.${inputClassName('field')}`);

    if (inputField) {
      fireEvent.click(inputField);

      const sidebar = container.querySelector(`.${calendarClassName('sidebar')}`);

      expect(sidebar).not.toBeNull();
    } else {
      throw new Error('Input is not exist');
    }
  });

  test('select range preset on click', () => {
    const monthPreset = {
      title: 'Select Month',
      value: {
        from: new Date(),
        to: addMonths(new Date(), ONE),
      },
    };

    const { container } = render(
      <DatePicker
        {...rangeDatePickerProps}
        presets={[monthPreset]}
      />,
    );

    const inputField = container.querySelector(`.${inputClassName('field')}`);

    if (inputField) {
      fireEvent.click(inputField);

      const sidebar = container.querySelector(`.${calendarClassName('sidebar')}`);
      const sidebarPreset = sidebar?.querySelector(`.${sidebarClassName('preset')}`);

      if (sidebarPreset) {
        fireEvent.click(sidebarPreset);

        expect(inputField).toHaveValue(stringifyDateRange(DEFAULT_PREFERENCES, monthPreset.value));
      } else {
        throw new Error('Preset is not exist');
      }

      expect(sidebar).not.toBeNull();
    } else {
      throw new Error('Input is not exist');
    }
  });
});
