import { fireEvent, render, screen } from '@testing-library/react';
import React from 'react';
import { describe, expect, it, vi } from 'vitest';
import { IconName } from '../Icons';
import { MessageBar, MessageBarProps } from './MessageBar';

const defaultProps: MessageBarProps = {
  title: 'test-message',
};

describe('MessageBar', () => {
  it('renders the component without crashing', () => {
    const { container } = render(<MessageBar {...defaultProps} />);

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

  it(`renders a message`, () => {
    render(<MessageBar {...defaultProps} />);

    const message = screen.getByTestId('message-title');

    expect(message.textContent).toBe(defaultProps.title);
  });

  describe('message types', () => {
    it.each([
      [undefined, IconName.Error, 'hasError'],
      ['error', IconName.Error, 'hasError'],
      ['info', IconName.Info, 'hasInfo'],
      ['warning', IconName.Warning, 'hasWarning'],
    ])('message type styling', (type, icon, className) => {
      const { container } = render(
        <MessageBar {...defaultProps} type={type as MessageBarProps['type']} />,
      );

      const messageElement = screen.getByTestId('message-bar');
      const messageContainer = container.querySelector('.message-container');

      expect(messageElement.getAttribute('data-test-type')).toBe(
        type || 'error',
      );
      expect(messageContainer?.classList.contains(className)).toBe(true);
    });
  });

  describe('close button', () => {
    it('close button is not rendered by default', () => {
      const { container } = render(<MessageBar {...defaultProps} />);

      const x = container.querySelector('.xIcon');

      expect(x).toBeNull();
    });

    it(`displays a close button and raises 'onClose' when set`, () => {
      const spy = vi.fn();
      const { container } = render(
        <MessageBar {...defaultProps} onClose={spy} />,
      );

      const xIcon = container.querySelector('.xIcon');
      const closeIcon = screen.getByTestId('message-bar-close-button');

      fireEvent.click(closeIcon);

      expect(xIcon).toBeTruthy();
      expect(spy).toHaveBeenCalledTimes(1);
    });
  });

  describe('retry button', () => {
    it('retry button is not rendered by default', () => {
      const { container } = render(<MessageBar {...defaultProps} />);

      const x = container.querySelector('.xIcon');

      expect(x).toBeNull();
    });

    it(`displays a retry button and raises 'onRetry' when set`, () => {
      const spy = vi.fn();
      const { container } = render(
        <MessageBar {...defaultProps} onRetry={spy} />,
      );

      const retryIcon = screen.getByTestId('message-bar-retry-button');
      const reIcon = container.querySelector('.reIcon');

      fireEvent.click(retryIcon);

      expect(reIcon).toBeTruthy();
      expect(spy).toHaveBeenCalledTimes(1);
    });
  });

  describe('message body', () => {
    it(`does not render a body nor the details icon if component has no children`, () => {
      const { container } = render(<MessageBar {...defaultProps} />);

      const body = container.querySelector('.body');
      const toggle = container.querySelector('.detailsIcon');

      expect(body).toBeNull();
      expect(toggle).toBeNull();
    });

    it(`body is not rendered by default and a details icon is rendered when body is passed in`, () => {
      const { container } = render(
        <MessageBar {...defaultProps}>Test</MessageBar>,
      );

      const body = container.querySelector('.body');
      const toggle = container.querySelector('.detailsIcon');

      expect(body).toBeNull();
      expect(toggle).toBeTruthy();
    });

    it('shows/hides body if children are passed in when details icon is clicked', () => {
      const { container } = render(
        <MessageBar {...defaultProps}>Test</MessageBar>,
      );

      let body = container.querySelector('.body');
      let toggleIcon = container.querySelector('.detIcon');

      expect(body).toBeNull();
      expect(toggleIcon?.classList.contains('open')).toBe(false);

      // Show body
      let toggle = screen.getByTestId('message-bar-details-button');

      fireEvent.click(toggle);

      body = container.querySelector('.body');
      toggleIcon = container.querySelector('.detIcon');

      expect(body).toBeTruthy();
      expect(toggleIcon?.classList.contains('open')).toBe(true);

      // Hide body
      toggle = screen.getByTestId('message-bar-details-button');

      fireEvent.click(toggle);

      body = container.querySelector('.body');
      toggleIcon = container.querySelector('.detIcon');

      expect(body).toBeNull();
      expect(toggleIcon?.classList.contains('open')).toBe(false);
    });

    it(`wraps children in 'div' tags if children is of type 'string'`, () => {
      const { container } = render(
        <MessageBar {...defaultProps}>Test</MessageBar>,
      );

      const toggle = screen.getByTestId('message-bar-details-button');

      fireEvent.click(toggle);

      const wrappedChild = container.querySelector('.body > div.textChild');

      expect(wrappedChild).toBeTruthy();
    });

    it(`does not wrap children in div tags if children is a react node`, () => {
      const { container } = render(
        <MessageBar {...defaultProps}>
          <h1>Test</h1>
        </MessageBar>,
      );

      const toggle = screen.getByTestId('message-bar-details-button');

      fireEvent.click(toggle);

      const wrappedChild = container.querySelector('.body > div.textChild');
      const node = container.querySelector('.body > h1');

      expect(wrappedChild).toBeNull();
      expect(node).toBeTruthy();
    });

    it('body is not rendered if children is an empty string', () => {
      const mockBody = '';
      const { container } = render(
        <MessageBar {...defaultProps}>{mockBody}</MessageBar>,
      );

      const toggle = container.querySelector('.detailsIcon');

      expect(toggle).toBeNull();
    });

    it('body is not rendered if children is undefined', () => {
      const { container } = render(<MessageBar {...defaultProps}></MessageBar>);

      const toggle = container.querySelector('.detailsIcon');

      expect(toggle).toBeNull();
    });
  });
});
