/* eslint-disable @typescript-eslint/no-empty-function */
import React from 'react';
import { screen, render, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

import { useOverlayTriggerState, OverlayTriggerState } from 'react-stately';
import { DOMAttributes, FocusableElement } from '@react-types/shared';
import { useOverlayTrigger } from 'react-aria';

import PreviewModal from './PreviewModal';

function PreviewModalTestWrapper({
  constructFunction,
}: {
  constructFunction: (state: OverlayTriggerState, overlayProps: DOMAttributes<FocusableElement>) => JSX.Element;
}) {
  const previewModalState = useOverlayTriggerState({ isOpen: true });
  const { overlayProps } = useOverlayTrigger({ type: 'dialog' }, previewModalState);

  return constructFunction(previewModalState, overlayProps);
}

describe('PreviewModal', () => {
  it('Modal is open by default', async () => {
    const onClose = jest.fn();

    render(
      <PreviewModalTestWrapper
        constructFunction={(state, overlayProps) => {
          return (
            <PreviewModal state={state} overlayProps={overlayProps} onClose={onClose}>
              Modal Content
            </PreviewModal>
          );
        }}
      />,
    );
    expect(screen.queryByRole('dialog')).toBeTruthy();
  });

  it('Clicking outside the modal calls onClose', async () => {
    const onClose = jest.fn();
    render(
      <div data-testid="outside">
        <PreviewModalTestWrapper
          constructFunction={(state, overlayProps) => {
            return (
              <PreviewModal state={state} overlayProps={overlayProps} onClose={onClose}>
                Modal Content
              </PreviewModal>
            );
          }}
        />
        ,
        <div style={{ height: '150vh' }} />
      </div>,
    );

    const user = userEvent.setup();

    // Modal Open
    await waitFor(() => {
      expect(screen.queryByRole('dialog')).toBeTruthy();
    });

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

    // Modal Closed
    await waitFor(() => {
      expect(onClose).toHaveBeenCalled();
    });
  });

  it('ESC closes modal', async () => {
    const onClose = jest.fn();
    render(
      <div data-testid="outside">
        <PreviewModalTestWrapper
          constructFunction={(state, overlayProps) => {
            return (
              <PreviewModal state={state} overlayProps={overlayProps} onClose={onClose}>
                Modal Content
              </PreviewModal>
            );
          }}
        />
        <div style={{ height: '150vh' }} />
      </div>,
    );

    const user = userEvent.setup();

    // Modal Open
    await waitFor(() => {
      expect(screen.queryByRole('dialog')).toBeTruthy();
    });

    user.type(screen.getByRole('dialog'), '{escape}');

    // Modal Closed
    await waitFor(() => {
      expect(onClose).toHaveBeenCalled();
    });
  });

  it('Focus is trapped within modal', async () => {
    const onClose = jest.fn();

    render(
      <div>
        <button />
        <PreviewModalTestWrapper
          constructFunction={(state, overlayProps) => {
            return (
              <PreviewModal state={state} overlayProps={overlayProps} onClose={onClose}>
                Modal Content
              </PreviewModal>
            );
          }}
        />
        <button />
        <div style={{ height: '150vh' }} />
      </div>,
    );

    const user = userEvent.setup();

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

    user.type(screen.getByRole('dialog'), '{tab}');
    user.type(screen.getByRole('dialog'), '{tab}');

    await waitFor(() => {
      expect(screen.queryByRole('button', { name: 'Close details' })).toHaveFocus();
    });
  });

  it('Focus should start on the dialog', async () => {
    const onClose = jest.fn();

    render(
      <div>
        <button />
        <PreviewModalTestWrapper
          constructFunction={(state, overlayProps) => {
            return (
              <PreviewModal state={state} overlayProps={overlayProps} onClose={onClose}>
                Modal Content
              </PreviewModal>
            );
          }}
        />
        <button />
        <div style={{ height: '150vh' }} />
      </div>,
    );

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