import { userEvent } from '@testing-library/user-event';
import { act } from 'react';

import { render, screen, mockMatchMedia } from '../test-utils';
import DateLookup from './DateLookup';

mockMatchMedia();

jest.mock(
  '../dimmer',
  () =>
    function ({ children }) {
      return <div className="dimmer">{children}</div>;
    },
);

describe('DateLookup (events)', () => {
  const date = new Date(2018, 11, 27);
  const min = new Date(2018, 11, 26);
  const max = new Date(2018, 11, 28);

  describe('when not clearable', () => {
    /** @type {jest.Mock} */
    let handleChange;

    const setup = async (overrides = {}) => {
      handleChange = jest.fn();

      /** @type {import('@testing-library/react').RenderResult} */
      let view;
      await act(async () => {
        view = render(
          <>
            {/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
            <label id="prioritized-label">Prioritized label</label>
            <DateLookup
              value={date}
              min={min}
              max={max}
              size="lg"
              placeholder="Select date"
              label="label"
              aria-labelledby="prioritized-label"
              disabled={false}
              clearable={false}
              onChange={handleChange}
              onClick={jest.fn()}
              {...overrides}
            />
          </>,
        );
      });
      return view;
    };

    it('switches to years', async () => {
      const view = await setup();
      await openDateLookup(view);
      await clickDateButton(view);

      expect(getActiveYearButton(view)).toHaveFocus();

      await closeDateLookup(view);
    });

    it('has aria-label for 20 years', async () => {
      const view = await setup();
      await openDateLookup(view);
      await clickDateButton(view);

      expect(getButtonByAriaLabel('next 20 years')).toBeInTheDocument();
      expect(getButtonByAriaLabel('previous 20 years')).toBeInTheDocument();

      await closeDateLookup(view);
    });

    it('switches to months', async () => {
      const view = await setup();
      await openDateLookup(view);
      await clickDateButton(view);
      await userEvent.click(getActiveYearButton(view));

      expect(getActiveMonthButton(view)).toHaveFocus();

      await closeDateLookup(view);
    });

    it('has aria label for year', async () => {
      const view = await setup();
      await openDateLookup(view);
      await clickDateButton(view);
      await userEvent.click(getActiveYearButton(view));

      expect(getButtonByAriaLabel('next year')).toBeInTheDocument();
      expect(getButtonByAriaLabel('previous year')).toBeInTheDocument();

      await closeDateLookup(view);
    });

    it('switches to days', async () => {
      const view = await setup();
      await openDateLookup(view);
      await clickDateButton(view);
      await userEvent.click(getActiveYearButton(view));
      await userEvent.click(getActiveMonthButton(view));

      expect(getActiveDayButton(view)).toHaveFocus();

      await closeDateLookup(view);
    });

    it('has aria label for month', async () => {
      const view = await setup();
      await openDateLookup(view);
      await clickDateButton(view);
      await userEvent.click(getActiveYearButton(view));
      await userEvent.click(getActiveMonthButton(view));

      expect(getButtonByAriaLabel('next month')).toBeInTheDocument();
      expect(getButtonByAriaLabel('previous month')).toBeInTheDocument();

      await closeDateLookup(view);
    });

    it('updates selected date and closes', async () => {
      const view = await setup();
      await openDateLookup(view);
      const d = new Date(2018, 11, 28);
      const newDay = screen.getByText(d.getDate().toString());
      await userEvent.click(newDay);

      expect(handleChange).toHaveBeenCalledWith(d);
      expect(getOpenButton(view)).toHaveFocus();

      await closeDateLookup(view);
    });

    it('has aria label on selected date', async () => {
      const view = await setup();
      await openDateLookup(view);
      const d = new Date(2018, 11, 28);
      const newDay = screen.getByText(d.getDate().toString());
      await userEvent.click(newDay);
      await openDateLookup(view);
      expect(screen.getByRole('button', { name: /selected day/i })).toBeInTheDocument();

      await closeDateLookup(view);
    });

    it('supports custom `aria-labelledby` attribute', async () => {
      const view = await setup();
      expect(screen.getByRole('button', { name: /^Prioritized label/ })).toBeInTheDocument();
      await closeDateLookup(view);
    });

    it('reads our the HTML label as well as the input prefix and the value', async () => {
      const view = await setup();
      expect(
        screen.getByRole('button', { name: 'Prioritized label label 27 December 2018' }),
      ).toBeInTheDocument();
      await closeDateLookup(view);
    });

    it('reads our the HTML label as well as the input prefix and the placeholder', async () => {
      const view = await setup({ value: undefined });
      expect(
        screen.getByRole('button', { name: 'Prioritized label label Select date' }),
      ).toBeInTheDocument();
      await closeDateLookup(view);
    });
  });

  describe('when is clearable', () => {
    /** @satisfies {import('./DateLookup').DateLookupProps} */
    const props = {
      value: date,
      onChange: jest.fn(),
      clearable: true,
    };

    const setup = async () => {
      /** @type {import('@testing-library/react').RenderResult} */
      let view;
      await act(async () => {
        view = render(<DateLookup {...props} />);
      });
      return view;
    };

    it(`doesn't show clear button if disable is true`, async () => {
      const view = await setup();
      expect(getClearButton(view)).toBeInTheDocument();

      view.rerender(<DateLookup {...props} disabled />);

      expect(getClearButton(view)).not.toBeInTheDocument();
    });

    it('when user clicks on clear the focus returns to btn', async () => {
      const view = await setup();
      await clickClearButton(view);
      expect(getOpenButton(view)).toHaveFocus();
    });

    it('onChange gets called with null when reset button is clicked', async () => {
      const view = await setup();
      await clickClearButton(view);
      expect(props.onChange).toHaveBeenCalledWith(null);
    });
  });

  const openDateLookup = async (
    /** @type {import('@testing-library/react').RenderResult} */ view,
  ) => userEvent.click(getOpenButton(view));

  const clickDateButton = async (
    /** @type {import('@testing-library/react').RenderResult} */ view,
  ) => userEvent.click(getDateButton(view));

  // Close dateLookup and removes events attached to documents.
  const closeDateLookup = async (
    /** @type {import('@testing-library/react').RenderResult} */ view,
  ) => userEvent.click(view.container);

  const clickClearButton = async (
    /** @type {import('@testing-library/react').RenderResult} */ view,
  ) => userEvent.click(getClearButton(view));

  const getActiveYearButton = (
    /** @type {import('@testing-library/react').RenderResult} */ view,
  ) => {
    return view.container.querySelector('button.tw-date-lookup-year-option.active');
  };

  const getActiveMonthButton = (
    /** @type {import('@testing-library/react').RenderResult} */ view,
  ) => {
    return view.container.querySelector('button.tw-date-lookup-month-option.active');
  };

  const getActiveDayButton = (
    /** @type {import('@testing-library/react').RenderResult} */ view,
  ) => {
    return view.container.querySelector('button.tw-date-lookup-day-option.active');
  };

  const getButtonByAriaLabel = (/** @type {string} */ ariaLabel) => {
    return screen.getByRole('button', { name: ariaLabel });
  };
  const getClearButton = (/** @type {import('@testing-library/react').RenderResult} */ view) =>
    view.container.querySelector('.clear-btn');
  const getOpenButton = (/** @type {import('@testing-library/react').RenderResult} */ view) =>
    view.container.querySelector('button.np-date-trigger');
  const getDateButton = (/** @type {import('@testing-library/react').RenderResult} */ view) =>
    view.container.querySelector('button.tw-date-lookup-header-current');
});
