import React from 'react';
import { screen, render, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

import { ModalTrigger } from './ModalTrigger';

describe('Modal', () => {
  it('Modal is closed by default', async () => {
    render(
      <div>
        <ModalTrigger label={'Open testing modal'} showLabel scope="squiz-gb-scope">
          {(onClose, titleProps) => (
            <div data-testingid="modal">
              <div {...titleProps}>Testing</div>
              <button type="button" onClick={onClose}>
                Close
              </button>
            </div>
          )}
        </ModalTrigger>
        <div style={{ height: '150vh' }} />
      </div>,
    );
    expect(screen.queryByTestId('modal')).toBeFalsy();
    expect(screen.queryAllByLabelText('Open testing modal')).toHaveLength(0);
  });

  it('Modal should render label', async () => {
    render(
      <div>
        <ModalTrigger label={'Open testing modal'} showLabel={false} scope="squiz-gb-scope">
          {(onClose, titleProps) => (
            <div data-testingid="modal">
              <div {...titleProps}>Testing</div>
              <button type="button" onClick={onClose}>
                Close
              </button>
            </div>
          )}
        </ModalTrigger>
        <div style={{ height: '150vh' }} />
      </div>,
    );
    expect(screen.getByLabelText('Open testing modal')).toBeTruthy();
  });

  it('Modal opens when triggered', async () => {
    render(
      <div>
        <ModalTrigger label={'Open testing modal'} showLabel scope="squiz-gb-scope">
          {(onClose, titleProps) => (
            <div data-testid="modal">
              <div {...titleProps}>Testing</div>
              <button type="button" onClick={onClose}>
                Close
              </button>
            </div>
          )}
        </ModalTrigger>
        <div style={{ height: '150vh' }} />
      </div>,
    );

    const user = userEvent.setup();
    user.click(screen.getByText('Open testing modal'));

    await waitFor(() => {
      expect(screen.queryByTestId('modal')).toBeTruthy();
    });
  });

  it('Clicking outside the modal closes it', async () => {
    render(
      <div data-testid="outside">
        <ModalTrigger label={'Open testing modal'} showLabel scope="squiz-gb-scope">
          {(onClose, titleProps) => (
            <div data-testid="modal">
              <div {...titleProps}>Testing</div>
              <button type="button" onClick={onClose}>
                Close
              </button>
            </div>
          )}
        </ModalTrigger>
        <div style={{ height: '150vh' }} />
      </div>,
    );

    const user = userEvent.setup();
    user.click(screen.getByText('Open testing modal'));

    await waitFor(() => {
      expect(screen.queryByTestId('modal')).toBeTruthy();
    });

    user.click(screen.getByTestId('outside'));

    await waitFor(() => {
      expect(screen.queryByTestId('modal')).toBeFalsy();
    });
  });

  it('ESC closes modal', async () => {
    render(
      <div data-testid="outside">
        <ModalTrigger label={'Open testing modal'} showLabel scope="squiz-gb-scope">
          {(onClose, titleProps) => (
            <div data-testid="modal">
              <div {...titleProps}>Testing</div>
              <button type="button" onClick={onClose}>
                Close
              </button>
            </div>
          )}
        </ModalTrigger>
        <div style={{ height: '150vh' }} />
      </div>,
    );

    const user = userEvent.setup();
    user.click(screen.getByText('Open testing modal'));

    await waitFor(() => {
      expect(screen.queryByTestId('modal')).toBeTruthy();
    });

    user.type(screen.getByTestId('modal'), '{escape}');

    await waitFor(() => {
      expect(screen.queryByTestId('modal')).toBeFalsy();
    });
  });

  it('Invoking onClose function closes modal', async () => {
    render(
      <div>
        <ModalTrigger label={'Open testing modal'} showLabel scope="squiz-gb-scope">
          {(onClose, titleProps) => (
            <div data-testid="modal">
              <div {...titleProps}>Testing</div>
              <button data-testid="closeButton" type="button" onClick={onClose}>
                Close
              </button>
            </div>
          )}
        </ModalTrigger>
        <div style={{ height: '150vh' }} />
      </div>,
    );

    const user = userEvent.setup();
    user.click(screen.getByText('Open testing modal'));

    await waitFor(() => {
      expect(screen.queryByTestId('modal')).toBeTruthy();
    });

    user.click(screen.getByTestId('closeButton'));

    await waitFor(() => {
      expect(screen.queryByTestId('modal')).toBeFalsy();
    });
  });

  it('Modal does not open if modal trigger button is disabled', async () => {
    render(
      <div>
        <ModalTrigger label={'Open testing modal'} showLabel isDisabled={true} scope="squiz-gb-scope">
          {(onClose, titleProps) => (
            <div data-testid="modal">
              <div {...titleProps}>Testing</div>
              <button type="button" onClick={onClose}>
                Close
              </button>
            </div>
          )}
        </ModalTrigger>
        <div style={{ height: '150vh' }} />
      </div>,
    );

    const user = userEvent.setup();
    user.click(screen.getByText('Open testing modal'));

    await waitFor(() => {
      expect(screen.queryByTestId('modal')).not.toBeTruthy();
    });
  });

  it('Focus is trapped within modal', async () => {
    render(
      <div>
        <button />
        <ModalTrigger label={'Open testing modal'} showLabel scope="squiz-gb-scope">
          {(onClose, titleProps) => (
            <div data-testid="modal">
              <div {...titleProps}>Testing</div>
              <button data-testid="closeButton" type="button" onClick={onClose}>
                Close
              </button>
            </div>
          )}
        </ModalTrigger>
        <button />
        <div style={{ height: '150vh' }} />
      </div>,
    );

    const user = userEvent.setup();
    user.click(screen.getByText('Open testing modal'));

    await waitFor(() => {
      expect(screen.queryByTestId('modal')).toBeTruthy();
    });

    user.type(screen.getByTestId('modal'), '{tab}');
    user.type(screen.getByTestId('modal'), '{tab}');

    await waitFor(() => {
      expect(screen.queryByTestId('closeButton')).toHaveFocus();
    });
  });

  it('Focus should start on the dialog', async () => {
    render(
      <div>
        <button />
        <ModalTrigger label={'Open testing modal'} showLabel scope="squiz-gb-scope">
          {(onClose, titleProps) => (
            <div data-testid="modal">
              <div data-testid="title" {...titleProps}>
                Testing
              </div>
              <button data-testid="closeButton" type="button" onClick={onClose}>
                Close
              </button>
            </div>
          )}
        </ModalTrigger>
        <button />
        <div style={{ height: '150vh' }} />
      </div>,
    );

    const user = userEvent.setup();
    user.click(screen.getByText('Open testing modal'));

    await waitFor(() => {
      expect(screen.queryByTestId('modal')).toBeTruthy();
      expect(screen.queryByRole('dialog')).toHaveFocus();
    });
  });

  it('Title props provides id for dialog link', async () => {
    let titleId: string | undefined = '';
    render(
      <div>
        <button />
        <ModalTrigger label={'Open testing modal'} showLabel scope="squiz-gb-scope">
          {(onClose, titleProps) => {
            titleId = titleProps.id;

            return (
              <div data-testid="modal">
                <div data-testid="title" {...titleProps}>
                  Testing
                </div>
                <button data-testid="closeButton" type="button" onClick={onClose}>
                  Close
                </button>
              </div>
            );
          }}
        </ModalTrigger>
        <button />
        <div style={{ height: '150vh' }} />
      </div>,
    );

    const user = userEvent.setup();
    user.click(screen.getByText('Open testing modal'));

    await waitFor(() => {
      expect(screen.queryByTestId('modal')).toBeTruthy();
      expect(screen.queryByTestId('title')).toHaveAttribute('id'); // Title was provided an id by 'titleProps'
      expect(screen.queryByRole('dialog')).toHaveAttribute('aria-labelledby', titleId); // 'titleProps' id is added to the dialog label
    });
  });
});
