import { fireEvent, render } from '@testing-library/react';
import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import { describe, expect, it, vi } from 'vitest';
import { IconName } from '../../Icons';
import { ButtonContext } from '../Button.model';
import { Button } from './Button';

const navigationButtonProps = {
  path: '/test',
};

const CustomIcon: React.FC = () => (
  <svg
    version="1.1"
    xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 40 40"
  ></svg>
);

describe('Button (with icon)', () => {
  describe('ContextButtonElement', () => {
    it('renders the component without crashing', () => {
      const { container } = render(<Button />);

      expect(container.querySelector('button')).toBeInTheDocument();
    });

    it('raises the onButtonClicked event', () => {
      const spy = vi.fn();
      const { container } = render(<Button onButtonClicked={spy} />);

      const button = container.querySelector('button')!;
      fireEvent.click(button);

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

    it('creates a class based off of the className prop', () => {
      const mockClassName = 'test-class';
      const { container } = render(<Button className={mockClassName} />);

      const button = container.querySelector('button')!;

      expect(button.classList.contains(mockClassName)).toBe(true);
    });

    it(`button 'type' must be 'button' by default`, () => {
      const { container } = render(<Button />);

      const button = container.querySelector('button')!;

      expect(button.type).toBe('button');
    });

    it('accepts type html attribute and width/height styles', () => {
      const mockType = 'submit';
      const mockHeight = '75px';
      const mockWidth = '80px';

      const { container, rerender } = render(<Button />);

      let button = container.querySelector('button')!;

      expect(button.type).toBe('button');
      expect(button.style.height).toBe('');
      expect(button.style.width).toBe('');

      rerender(
        <Button type={mockType} height={mockHeight} width={mockWidth} />,
      );

      button = container.querySelector('button')!;

      expect(button.type).toBe(mockType);
      expect(button.style.height).toBe(mockHeight);
      expect(button.style.width).toBe(mockWidth);
    });

    it('allows the button to be enabled by default', () => {
      const spy = vi.fn();
      const { container } = render(<Button onButtonClicked={spy} />);

      const button = container.querySelector('button')!;

      fireEvent.click(button);

      expect(button.disabled).toBe(false);
      expect(spy).toHaveBeenCalledTimes(1);
    });

    it('allows button to be disabled', () => {
      const spy = vi.fn();
      const { container } = render(
        <Button disabled={true} onButtonClicked={spy} />,
      );

      const button = container.querySelector('button')!;

      fireEvent.click(button);

      expect(button.disabled).toBe(true);
      expect(spy).not.toHaveBeenCalled();
    });

    it('default should have a active class', () => {
      const { container } = render(<Button />);

      const button = container.querySelector('button')!;

      expect(button.classList.contains('active')).toBe(true);
    });

    it('context button type should have a context class', () => {
      const { container } = render(
        <Button buttonContext={ButtonContext.Context} />,
      );

      const button = container.querySelector('button')!;

      expect(button.classList.contains('context')).toBe(true);
    });

    it('context button type should have a icon class', () => {
      const { container } = render(
        <Button buttonContext={ButtonContext.Icon} />,
      );

      const button = container.querySelector('button')!;

      expect(button.classList.contains('icon')).toBe(true);
    });

    it('active button type should have a active class', () => {
      const { container } = render(
        <Button buttonContext={ButtonContext.Active} />,
      );

      const button = container.querySelector('button')!;

      expect(button.classList.contains('active')).toBe(true);
    });

    it(`renders an icon as it's image from the Icons component`, () => {
      const { container } = render(<Button icon={IconName.ChevronRight} />);

      const icon = container.querySelector('svg');

      expect(icon).toBeInTheDocument();
    });

    it('renders `customIcon` when provided', () => {
      const { container } = render(<Button customIcon={<CustomIcon />} />);
      const customIcon = container.querySelector('svg');

      expect(customIcon).toBeInTheDocument();
    });

    it('renders `customIcon` instead of `icon` when both are provided', () => {
      const { container } = render(
        <Button icon={IconName.ChevronRight} customIcon={<CustomIcon />} />,
      );

      const svgs = container.querySelectorAll('svg');

      expect(svgs).toHaveLength(1);
    });
  });

  describe('NavigationButtonElement', () => {
    it('renders the component without crashing', () => {
      const { container } = render(
        <Router>
          <Button {...navigationButtonProps} />
        </Router>,
      );

      expect(container.querySelector('a')).toBeInTheDocument();
    });

    it('creates a class based on the className prop', () => {
      const mockClassName = 'test-class';
      const { container } = render(
        <Router>
          <Button {...navigationButtonProps} className={mockClassName} />
        </Router>,
      );

      const link = container.querySelector('a')!;

      expect(link.classList.contains(mockClassName)).toBe(true);
    });

    it('renders the correct path in the Link component', () => {
      const mockPath = '/test';
      const { container } = render(
        <Router>
          <Button path={mockPath} />
        </Router>,
      );

      const link = container.querySelector('a')!;

      expect(link.getAttribute('href')).toBe(mockPath);
    });

    it('accepts width/height styles', () => {
      const mockHeight = '75px';
      const mockWidth = '80px';

      const { container } = render(
        <Router>
          <Button
            {...navigationButtonProps}
            height={mockHeight}
            width={mockWidth}
          />
        </Router>,
      );

      const link = container.querySelector('a')!;

      expect(link.style.height).toBe(mockHeight);
      expect(link.style.width).toBe(mockWidth);
    });

    it('allows the button to be enabled by default', () => {
      const { container } = render(
        <Router>
          <Button {...navigationButtonProps} />
        </Router>,
      );

      const link = container.querySelector('a')!;

      expect(link.classList.contains('disabled')).toBe(false);
    });

    it('allows button to be disabled', () => {
      const { container } = render(
        <Router>
          <Button {...navigationButtonProps} disabled={true} />
        </Router>,
      );

      const link = container.querySelector('a')!;

      expect(link.classList.contains('disabled')).toBe(true);
    });

    it('renders the correct data-test-id attribute', () => {
      const mockTestId = 'test-button';
      const { container } = render(
        <Router>
          <Button {...navigationButtonProps} dataTestId={mockTestId} />
        </Router>,
      );

      const link = container.querySelector('a')!;

      expect(link.getAttribute('data-test-id')).toBe(mockTestId);
    });

    it('renders the Icons component with the correct default icon', () => {
      const { container } = render(
        <Router>
          <Button {...navigationButtonProps} />
        </Router>,
      );

      const svg = container.querySelector('svg');

      expect(svg).toBeInTheDocument();
    });

    it('renders the Icons component with the correct default icon when `openInNewTab` is set to `true`', () => {
      const { container } = render(
        <Router>
          <Button {...navigationButtonProps} openInNewTab={true} />
        </Router>,
      );

      const svg = container.querySelector('svg');

      expect(svg).toBeInTheDocument();
    });

    it('opens link in new tab when `openInNewTab` is set to `true`', () => {
      const { container } = render(
        <Router>
          <Button {...navigationButtonProps} openInNewTab={true} />
        </Router>,
      );

      const link = container.querySelector('a')!;

      expect(link.getAttribute('target')).toBe('_blank');
    });

    it('does not open link in new tab by default', () => {
      const { container } = render(
        <Router>
          <Button {...navigationButtonProps} />
        </Router>,
      );

      const link = container.querySelector('a')!;

      expect(link.getAttribute('target')).toBeNull();
    });

    it('renders the Icons component with the correct icon', () => {
      const mockIcon = IconName.Copy;
      const { container } = render(
        <Router>
          <Button {...navigationButtonProps} icon={mockIcon} />
        </Router>,
      );

      const svg = container.querySelector('svg');

      expect(svg).toBeInTheDocument();
    });

    it('renders `customIcon` when provided', () => {
      const { container } = render(
        <Router>
          <Button {...navigationButtonProps} customIcon={<CustomIcon />} />
        </Router>,
      );
      const customIcon = container.querySelector('svg');

      expect(customIcon).toBeInTheDocument();
    });

    it('renders `customIcon` instead of `icon` when both are provided', () => {
      const { container } = render(
        <Router>
          <Button
            {...navigationButtonProps}
            icon={IconName.ChevronRight}
            customIcon={<CustomIcon />}
          />
        </Router>,
      );

      const svgs = container.querySelectorAll('svg');

      expect(svgs).toHaveLength(1);
    });
  });
});
