import { Breakpoint } from '@transferwise/neptune-tokens';
import { Position } from '../common';
import { render, screen, userEvent, mockMatchMedia } from '../test-utils';

import Popover from './Popover';

mockMatchMedia();

jest.mock('react-transition-group', () => ({
  __esModule: true,
  CSSTransition: (props: Record<string, unknown>) => (props.in ? props.children : null),
}));

describe('Popover', () => {
  const props = {
    arrow: true,
    content: 'content',
    position: Position.BOTTOM,
    title: 'title',
  };

  let container;
  let rerender;

  describe('on desktop', () => {
    describe('title', () => {
      it('renders title', async () => {
        ({ container, rerender } = render(
          <Popover {...props}>
            <button type="button">Open</button>
          </Popover>,
        ));

        await userEvent.click(screen.getByText('Open'));
        await waitForPanel();

        expect(getTitle()).toBeInTheDocument();

        rerender(
          <Popover {...props} title={undefined}>
            <button type="button">Open</button>
          </Popover>,
        );

        await userEvent.click(screen.getByText('Open'));

        expect(getTitle()).not.toBeInTheDocument();
      });

      it('should uses `title` as the accessible name', async () => {
        render(
          <Popover {...props}>
            <button type="button">Open</button>
          </Popover>,
        );

        await userEvent.click(screen.getByText('Open'));
        await waitForPanel();
      });

      describe('accessible name', () => {
        const ACCESSIBLE_NAME = 'Accessible name';

        it('should use `aria-label` as the accessible name', async () => {
          render(
            <Popover {...props} title={undefined} aria-label={ACCESSIBLE_NAME}>
              <button type="button">Open</button>
            </Popover>,
          );

          await userEvent.click(screen.getByText('Open'));
          await waitForPanel();

          expect(screen.getByRole('dialog', { name: ACCESSIBLE_NAME })).toBeInTheDocument();
        });

        it('should prioritise `aria-label` over `title` as the accessible name', async () => {
          render(
            <Popover {...props} aria-label={ACCESSIBLE_NAME}>
              <button type="button">Open</button>
            </Popover>,
          );

          await userEvent.click(screen.getByText('Open'));
          await waitForPanel();

          expect(screen.getByRole('dialog', { name: ACCESSIBLE_NAME })).toBeInTheDocument();
        });
      });
    });

    it('renders Panel onClick', async () => {
      render(
        <Popover {...props}>
          <button type="button">Open</button>
        </Popover>,
      );

      expect(getPanel()).not.toBeInTheDocument();

      await userEvent.click(screen.getByText('Open'));
      await waitForPanel();

      expect(getPanel()).toBeInTheDocument();
    });
  });

  describe('on mobile', () => {
    beforeEach(() => {
      global.innerWidth = Breakpoint.SMALL - 1;
    });

    it('renders BottomSheet onClick', async () => {
      render(
        <Popover {...props}>
          <button type="button">Open</button>
        </Popover>,
      );

      expect(getBottomSheet()).not.toBeInTheDocument();

      await userEvent.click(screen.getByText('Open'));

      expect(getBottomSheet()).toBeInTheDocument();
    });
  });

  it('onClose fires when close Popover', async () => {
    const handleOnClose = jest.fn();
    render(
      <Popover {...props} onClose={handleOnClose}>
        <button type="button">Open</button>
      </Popover>,
    );

    expect(handleOnClose).not.toHaveBeenCalled();
    await userEvent.click(screen.getByText('Open'));
    await waitForPanel();
    const dimmer = getDimmer();
    if (dimmer != null) {
      await userEvent.click(dimmer);
    }
    expect(handleOnClose).toHaveBeenCalledTimes(1);
  });

  describe('className', () => {
    describe('on desktop', () => {
      beforeEach(() => {
        global.innerWidth = Breakpoint.SMALL;
      });

      it('applies className to the panel container', async () => {
        render(
          <Popover {...props} className="custom-popover-class">
            <button type="button">Open</button>
          </Popover>,
        );

        await userEvent.click(screen.getByText('Open'));
        await waitForPanel();

        const panel = getPanel();
        expect(panel).toHaveClass('np-popover__container');
        expect(panel).toHaveClass('custom-popover-class');
      });
    });

    describe('on mobile', () => {
      beforeEach(() => {
        global.innerWidth = Breakpoint.SMALL - 1;
      });

      it('applies className to the bottom sheet container', async () => {
        render(
          <Popover {...props} className="custom-popover-class">
            <button type="button">Open</button>
          </Popover>,
        );

        await userEvent.click(screen.getByText('Open'));

        const bottomSheet = getBottomSheet();
        expect(bottomSheet).toHaveClass('np-popover__container');
        expect(bottomSheet).toHaveClass('custom-popover-class');
      });
    });

    it('does not apply className to the trigger wrapper', () => {
      const { container } = render(
        <Popover {...props} className="custom-popover-class">
          <button type="button">Open</button>
        </Popover>,
      );

      const wrapper = container.firstChild;
      expect(wrapper).toHaveClass('np-popover');
      expect(wrapper).not.toHaveClass('custom-popover-class');
    });
  });

  const waitForPanel = async () => screen.findByText('content');
  const getDimmer = () => document.querySelector('.dimmer');
  const getPanel = () => document.querySelector('.np-panel');
  const getBottomSheet = () => document.querySelector('.np-bottom-sheet');
  const getTitle = () => screen.queryByText('title');
});
