import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import React from 'react';
import { describe, expect, it, vi } from 'vitest';
import { Modal } from './Modal';

describe('Actions', () => {
  it('renders the component without crashing', () => {
    const { container } = render(<Modal isShown={true} />);

    expect(container).toBeTruthy();
  });

  it(`emits 'onBackDropClicked' when the back-drop is clicked`, () => {
    const backDropSpy = vi.fn();
    const { container } = render(
      <Modal isShown={true} onBackDropClicked={backDropSpy} />,
    );

    const modalContainer = container.querySelector('.modal-container');

    if (modalContainer) {
      fireEvent.click(modalContainer);
    }

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

  it(`does not emit 'onBackDropClicked' when a child element is clicked`, () => {
    const backDropSpy = vi.fn();
    render(
      <Modal onBackDropClicked={backDropSpy} isShown={true}>
        <div data-test-id="child">Test</div>
      </Modal>,
    );

    const child = screen.getByTestId('child');

    fireEvent.click(child);

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

  it(`emits 'onOpen' when the modal is shown`, async () => {
    const onOpenSpy = vi.fn();

    const { rerender } = render(<Modal isShown={false} onOpen={onOpenSpy} />);

    rerender(<Modal isShown={true} onOpen={onOpenSpy} />);

    await waitFor(
      () => {
        expect(onOpenSpy).toHaveBeenCalledTimes(1);
      },
      { timeout: 500 },
    );
  });

  it(`emits 'onClose' when the modal is hidden`, async () => {
    const onCloseSpy = vi.fn();

    const { rerender } = render(<Modal isShown={true} onClose={onCloseSpy} />);

    rerender(<Modal isShown={false} onClose={onCloseSpy} />);

    await waitFor(
      () => {
        expect(onCloseSpy).toHaveBeenCalledTimes(1);
      },
      { timeout: 500 },
    );
  });

  it(`accepts padding styles which are 'undefined' by default`, () => {
    const mockPaddingTop = '50px';
    const mockPaddingLeft = '51px';
    const mockPaddingBottom = '52px';
    const mockPaddingRight = '53px';

    const { container, rerender } = render(<Modal isShown={true} />);

    let modalContainer = container.querySelector(
      '.modal-container',
    ) as HTMLElement;

    expect(modalContainer?.style.paddingTop).toBeFalsy();
    expect(modalContainer?.style.paddingLeft).toBeFalsy();
    expect(modalContainer?.style.paddingBottom).toBeFalsy();
    expect(modalContainer?.style.paddingRight).toBeFalsy();

    rerender(
      <Modal
        isShown={true}
        paddingTop={mockPaddingTop}
        paddingLeft={mockPaddingLeft}
        paddingBottom={mockPaddingBottom}
        paddingRight={mockPaddingRight}
      />,
    );

    modalContainer = container.querySelector('.modal-container') as HTMLElement;

    expect(modalContainer?.style.paddingTop).toBe(mockPaddingTop);
    expect(modalContainer?.style.paddingRight).toBe(mockPaddingRight);
    expect(modalContainer?.style.paddingBottom).toBe(mockPaddingBottom);
    expect(modalContainer?.style.paddingLeft).toBe(mockPaddingLeft);
  });

  it(`if 'width' prop is set, passes 'width' value to child container`, () => {
    const mockWidth = '50px';

    const { rerender } = render(<Modal isShown={true} />);

    let modalElement = screen.getByTestId('modal') as HTMLElement;

    expect(modalElement?.style.width).toBeFalsy();

    rerender(<Modal isShown={true} width={mockWidth} />);

    modalElement = screen.getByTestId('modal') as HTMLElement;

    expect(modalElement?.style.width).toBe(mockWidth);
  });

  it(`data-test-state attribute is set to "open" when fully opened`, async () => {
    const { rerender } = render(<Modal isShown={false} />);

    expect(screen.queryByTestId('modal')).toBeNull();

    rerender(<Modal isShown={true} />);

    const modalElement = screen.getByTestId('modal');

    expect(modalElement.getAttribute('data-test-state')).toBe('closed');

    await waitFor(
      () => {
        expect(modalElement.getAttribute('data-test-state')).toBe('open');
      },
      { timeout: 500 },
    );
  });

  it(`element with data-test-id="modal" is hidden when closed`, async () => {
    const { rerender } = render(<Modal isShown={true} />);

    expect(screen.getByTestId('modal')).toBeTruthy();

    rerender(<Modal isShown={false} />);

    await waitFor(
      () => {
        expect(screen.queryByTestId('modal')).toBeNull();
      },
      { timeout: 500 },
    );
  });
});
