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 { CompositeButton } from './CompositeButton';

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

describe('CompositeButton', () => {
  describe('Composite context button', () => {
    it('renders the component without crashing', () => {
      const { container } = render(<CompositeButton />);

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

    it('raises the onButtonClicked event', () => {
      const spy = vi.fn();
      const { container } = render(<CompositeButton 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(
        <CompositeButton 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(<CompositeButton />);

      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(<CompositeButton />);

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

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

      rerender(
        <CompositeButton
          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(<CompositeButton 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(
        <CompositeButton 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 context class', () => {
      const { container } = render(<CompositeButton />);

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

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

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

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

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

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

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

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

    it(`renders value from 'text' prop`, () => {
      const mockText = 'mock-text';

      const { container } = render(<CompositeButton text={mockText} />);

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

      expect(button.textContent).toBe(mockText);
    });

    it(`defaults to an empty string is 'text' prop is not set`, () => {
      const { container } = render(<CompositeButton />);

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

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

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

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

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

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

  describe('Composite navigation button', () => {
    it('renders the component without crashing', () => {
      const { container } = render(
        <Router>
          <CompositeButton path="/example" />
        </Router>,
      );

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

    it('renders a Link component with the provided path', () => {
      const mockPath = '/example';
      const { container } = render(
        <Router>
          <CompositeButton path={mockPath} />
        </Router>,
      );

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

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

    it('applies the provided className to the component', () => {
      const mockClassName = 'test-class';
      const { container } = render(
        <Router>
          <CompositeButton className={mockClassName} path="/example" />
        </Router>,
      );

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

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

    it('applies the disabled class when disabled prop is true', () => {
      const { container } = render(
        <Router>
          <CompositeButton disabled={true} path="/example" />
        </Router>,
      );

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

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

    it('does not apply the disabled class when disabled prop is false', () => {
      const { container } = render(
        <Router>
          <CompositeButton disabled={false} path="/example" />
        </Router>,
      );

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

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

    it('sets the height and width styles based on the provided props', () => {
      const mockHeight = '50px';
      const mockWidth = '100px';
      const { container } = render(
        <Router>
          <CompositeButton
            height={mockHeight}
            width={mockWidth}
            path="/example"
          />
        </Router>,
      );

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

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

    it('sets the direction style to "rtl" when iconPosition is "left"', () => {
      const { container } = render(
        <Router>
          <CompositeButton iconPosition="left" path="/example" />
        </Router>,
      );

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

      expect(link.style.direction).toBe('rtl');
    });

    it('sets the direction style to "ltr" when iconPosition is "right"', () => {
      const { container } = render(
        <Router>
          <CompositeButton iconPosition="right" path="/example" />
        </Router>,
      );

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

      expect(link.style.direction).toBe('ltr');
    });

    it('renders the text inside a div', () => {
      const mockText = 'Example Button';
      const { container } = render(
        <Router>
          <CompositeButton text={mockText} path="/example" />
        </Router>,
      );

      const textDiv = container.querySelector('div')!;

      expect(textDiv.textContent).toBe(mockText);
    });

    it('renders the icon inside a div when icon prop is provided', () => {
      const mockIcon = IconName.Calendar;
      const { container } = render(
        <Router>
          <CompositeButton icon={mockIcon} path="/example" />
        </Router>,
      );

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

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

    it('renders the Icons component with the correct default icon', () => {
      const { container } = render(
        <Router>
          <CompositeButton path="/example" />
        </Router>,
      );

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

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

    it('renders `External` icon by default when none provided and `openInNewTab: true`', () => {
      const { container } = render(
        <Router>
          <CompositeButton path="/example" openInNewTab={true} />
        </Router>,
      );

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

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

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

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

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

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

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

    it('renders the default data-test-id when dataTestId prop is not provided', () => {
      const { container } = render(
        <Router>
          <CompositeButton path="/example" />
        </Router>,
      );

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

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

    it('renders the provided data-test-id when dataTestId prop is provided', () => {
      const mockDataTestId = 'test-id';
      const { container } = render(
        <Router>
          <CompositeButton dataTestId={mockDataTestId} path="/example" />
        </Router>,
      );
      const link = container.querySelector('a')!;

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