import { Button } from '..';
import { addNoScrollClass, removeNoScrollClass } from '../common';
import {
  render,
  cleanup,
  waitFor,
  screen,
  userEvent,
  waitForElementToBeRemoved,
} from '../test-utils';

import { Dimmer } from './Dimmer';

jest.mock('../common');

describe('Dimmer', () => {
  const props = {
    open: true,
    children: <div />,
  };

  afterEach(() => {
    cleanup();
    jest.clearAllMocks();
  });

  describe('closing behaviour', () => {
    it('calls onClose if the dimmer is clicked', async () => {
      const onClose = jest.fn();
      render(<Dimmer {...props} onClose={onClose} />);
      await userEvent.click(screen.getByRole('presentation'));
      expect(onClose).toHaveBeenCalledTimes(1);
    });

    it('does not call onClose if the dimmer content is clicked', async () => {
      const onClose = jest.fn();
      render(
        <Dimmer {...props} onClose={onClose}>
          <Button onClick={() => {}}>McClicky</Button>
        </Dimmer>,
      );
      await userEvent.click(screen.getByRole('button'));
      expect(onClose).toHaveBeenCalledTimes(0);
    });

    it('does not call onClose when clicked if disableClickToClose is true', async () => {
      const onClose = jest.fn();
      render(<Dimmer {...props} disableClickToClose onClose={onClose} />);
      await userEvent.click(screen.getByRole('presentation'));
      expect(onClose).toHaveBeenCalledTimes(0);
    });

    it('calls onClose if the escape key is pressed', async () => {
      const onClose = jest.fn();
      render(<Dimmer {...props} onClose={onClose} />);
      await userEvent.keyboard('{Escape}');
      expect(onClose).toHaveBeenCalledTimes(1);
    });

    it('does not call onClose if the escape key is pressed when disableClickToClose is true', async () => {
      const onClose = jest.fn();
      render(<Dimmer {...props} disableClickToClose onClose={onClose} />);
      await userEvent.keyboard('{Escape}');
      expect(onClose).toHaveBeenCalledTimes(1);
    });
  });

  describe('transparent', () => {
    it('shows backdrop by default', () => {
      render(<Dimmer {...props} />);
      expect(screen.getByRole('presentation')).not.toHaveClass('dimmer--transparent');
    });

    it('adds a class to hide the backdrop when transparent is true', () => {
      render(<Dimmer {...props} transparent />);
      expect(screen.getByRole('presentation')).toHaveClass('dimmer--transparent');
    });

    it('will not lock body scroll when transparent is true', () => {
      render(<Dimmer {...props} transparent />);

      expect(addNoScrollClass).not.toHaveBeenCalled();
    });
  });

  describe('addNoScrollClass', () => {
    it('calls addNoScrollClass on render', () => {
      expect(addNoScrollClass).not.toHaveBeenCalled();

      render(<Dimmer {...props} />);

      expect(addNoScrollClass).toHaveBeenCalledTimes(1);
    });

    it('calls removeNoScrollClass on unmount', async () => {
      expect(removeNoScrollClass).not.toHaveBeenCalled();

      const { unmount } = render(<Dimmer {...props} />);

      unmount();

      await waitFor(() => {
        expect(removeNoScrollClass).toHaveBeenCalledTimes(1);
      });
    });

    it('calls removeNoScrollClass only once when the children is unmounted', async () => {
      const { rerender, unmount } = render(<Dimmer {...props} />);

      expect(removeNoScrollClass).toHaveBeenCalledTimes(0);
      rerender(<Dimmer {...props} open={false} />);

      await waitFor(() => {
        expect(removeNoScrollClass).toHaveBeenCalledTimes(1);
      });

      unmount();

      await waitFor(() => {
        expect(removeNoScrollClass).toHaveBeenCalledTimes(1);
      });
    });
  });

  describe('animates out on exit', () => {
    it('isnt removed until the animation has finished', async () => {
      const { rerender } = render(
        <Dimmer {...props} open={false} fadeContentOnEnter fadeContentOnExit>
          <p>Content</p>
        </Dimmer>,
      );

      expect(screen.queryByText('Content')).not.toBeInTheDocument();

      rerender(
        <Dimmer {...props} open fadeContentOnEnter fadeContentOnExit>
          <p>Content</p>
        </Dimmer>,
      );

      await expect(screen.findByText('Content')).resolves.toBeInTheDocument();

      rerender(
        <Dimmer {...props} open={false} fadeContentOnEnter fadeContentOnExit>
          <p>Content</p>
        </Dimmer>,
      );

      await waitForElementToBeRemoved(screen.queryByText('Content'));
    });
  });
});
