import { render, screen } from '@testing-library/react';
import { userEvent } from '@testing-library/user-event';

import { expect } from 'vitest';
import { NJNavigationAction as Library } from '../../../../../lib/fluid-design-system-react';
import { NJNavigationAction as Source } from '../NJNavigationAction';

describe.each([
  { Tag: Source, description: 'sources' },
  { Tag: Library, description: 'global library' }
])('using $description', ({ Tag }) => {
  describe('with default rendered element', () => {
    describe('without optional properties', () => {
      it('render a button with content', async () => {
        render(<Tag>Content</Tag>);
        expect(screen.getByRole('button', { name: 'Content' })).toBeTruthy();
      });

      it('pass onClick handler', async () => {
        const handler = vi.fn();
        const user = userEvent.setup();
        render(<Tag action={{ onClick: handler }}>Content</Tag>);
        await user.click(screen.getByRole('button', { name: 'Content' }));
        expect(handler).toBeCalled();
      });

      it('pass onFocus handler', async () => {
        const handler = vi.fn();
        const user = userEvent.setup();
        render(<Tag action={{ onFocus: handler }}>Content</Tag>);
        await user.tab();
        expect(handler).toBeCalled();
      });

      it('pass onBlur handler', async () => {
        const handler = vi.fn();
        const user = userEvent.setup();
        render(<Tag action={{ onBlur: handler }}>Content</Tag>);
        await user.tab();
        await user.tab();
        expect(handler).toBeCalled();
      });
    });

    describe('with icon', () => {
      it('render icon', async () => {
        render(<Tag icon="home">Content</Tag>);
        const icon = screen.getByText('home');
        expect(icon).toBeTruthy();
        expect(icon.className).toContain('material-icons');
      });

      it('render custom icon', async () => {
        render(<Tag customIcon={<svg data-testid="custom-icon">Custom</svg>}>Content</Tag>);
        expect(screen.getByTestId('custom-icon')).toBeTruthy();
      });

      it('render custom icon even if icon is passed', async () => {
        render(
          <Tag icon="home" customIcon={<svg data-testid="custom-icon">Custom</svg>}>
            Content
          </Tag>
        );
        expect(screen.getByTestId('custom-icon')).toBeTruthy();
        expect(screen.queryByText('home')).toBeFalsy();
      });
    });

    describe('with trailing content', () => {
      it('render trailing content', async () => {
        render(<Tag trailing={<span data-testid="trailing">Trailing</span>}>Content</Tag>);
        expect(screen.getByTestId('trailing')).toBeTruthy();
      });
    });

    describe('with expandable', () => {
      it('render expandable icon', async () => {
        render(<Tag expandable>Content</Tag>);
        const icon = screen.getByText('keyboard_arrow_down');
        expect(icon).toBeTruthy();
        expect(icon.className).toContain('material-icons');
      });
    });
  });

  describe('with button as action child', () => {
    describe('without optional properties', () => {
      it('render passed button with content', async () => {
        render(
          <Tag actionAsChild>
            <button data-testid="child">Content</button>
          </Tag>
        );
        const button = screen.getByRole('button', { name: 'Content' });
        const child = screen.getByTestId('child');
        expect(child).toBeTruthy();
        expect(button).toBeTruthy();
        expect(button).toBe(child);
      });

      it('keep onClick handler', async () => {
        const handler = vi.fn();
        const user = userEvent.setup();
        render(
          <Tag actionAsChild>
            <button onClick={handler}>Content</button>
          </Tag>
        );
        await user.click(screen.getByRole('button', { name: 'Content' }));
        expect(handler).toBeCalled();
      });

      it('keep onFocus handler', async () => {
        const handler = vi.fn();
        const user = userEvent.setup();
        render(
          <Tag actionAsChild>
            <button onFocus={handler}>Content</button>
          </Tag>
        );
        await user.tab();
        expect(handler).toBeCalled();
      });

      it('keep onBlur handler', async () => {
        const handler = vi.fn();
        const user = userEvent.setup();
        render(
          <Tag actionAsChild>
            <button onBlur={handler}>Content</button>
          </Tag>
        );
        await user.tab();
        await user.tab();
        expect(handler).toBeCalled();
      });
    });

    describe('with icon', () => {
      it('render icon', async () => {
        render(
          <Tag icon="home" actionAsChild>
            <button>Content</button>
          </Tag>
        );
        const icon = screen.getByText('home');
        expect(icon).toBeTruthy();
        expect(icon.className).toContain('material-icons');
      });

      it('render custom icon', async () => {
        render(
          <Tag customIcon={<svg data-testid="custom-icon">Custom</svg>} actionAsChild>
            <button>Content</button>
          </Tag>
        );
        expect(screen.getByTestId('custom-icon')).toBeTruthy();
      });

      it('render custom icon even if icon is passed', async () => {
        render(
          <Tag icon="home" customIcon={<svg data-testid="custom-icon">Custom</svg>} actionAsChild>
            <button>Content</button>
          </Tag>
        );
        expect(screen.getByTestId('custom-icon')).toBeTruthy();
        expect(screen.queryByText('home')).toBeFalsy();
      });
    });

    describe('with trailing content', () => {
      it('render trailing content', async () => {
        render(
          <Tag trailing={<span data-testid="trailing">Trailing</span>} actionAsChild>
            <button>Content</button>
          </Tag>
        );
        expect(screen.getByTestId('trailing')).toBeTruthy();
      });
    });

    describe('with expandable', () => {
      it('render expandable icon', async () => {
        render(
          <Tag expandable actionAsChild>
            <button>Content</button>
          </Tag>
        );
        const icon = screen.getByText('keyboard_arrow_down');
        expect(icon).toBeTruthy();
        expect(icon.className).toContain('material-icons');
      });
    });
  });

  describe('with link as action child', () => {
    describe('without optional properties', () => {
      it('render passed link with content', async () => {
        render(
          <Tag actionAsChild>
            <a href="/" data-testid="child">
              Content
            </a>
          </Tag>
        );
        const button = screen.getByRole('link', { name: 'Content' });
        const child = screen.getByTestId('child');
        expect(child).toBeTruthy();
        expect(button).toBeTruthy();
        expect(button).toBe(child);
      });

      it('keep onClick handler', async () => {
        const handler = vi.fn();
        const user = userEvent.setup();
        render(
          <Tag actionAsChild>
            <a href="/" onClick={handler}>
              Content
            </a>
          </Tag>
        );
        await user.click(screen.getByRole('link', { name: 'Content' }));
        expect(handler).toBeCalled();
      });

      it('keep onFocus handler', async () => {
        const handler = vi.fn();
        const user = userEvent.setup();
        render(
          <Tag actionAsChild>
            <a href="/" onFocus={handler}>
              Content
            </a>
          </Tag>
        );
        await user.tab();
        expect(handler).toBeCalled();
      });

      it('keep onBlur handler', async () => {
        const handler = vi.fn();
        const user = userEvent.setup();
        render(
          <Tag actionAsChild>
            <a href="/" onBlur={handler}>
              Content
            </a>
          </Tag>
        );
        await user.tab();
        await user.tab();
        expect(handler).toBeCalled();
      });
    });

    describe('with icon', () => {
      it('render icon', async () => {
        render(
          <Tag icon="home" actionAsChild>
            <a href="/">Content</a>
          </Tag>
        );
        const icon = screen.getByText('home');
        expect(icon).toBeTruthy();
        expect(icon.className).toContain('material-icons');
      });

      it('render custom icon', async () => {
        render(
          <Tag customIcon={<svg data-testid="custom-icon">Custom</svg>} actionAsChild>
            <a href="/">Content</a>
          </Tag>
        );
        expect(screen.getByTestId('custom-icon')).toBeTruthy();
      });

      it('render custom icon even if icon is passed', async () => {
        render(
          <Tag icon="home" customIcon={<svg data-testid="custom-icon">Custom</svg>} actionAsChild>
            <a href="/">Content</a>
          </Tag>
        );
        expect(screen.getByTestId('custom-icon')).toBeTruthy();
        expect(screen.queryByText('home')).toBeFalsy();
      });
    });

    describe('with trailing content', () => {
      it('render trailing content', async () => {
        render(
          <Tag trailing={<span data-testid="trailing">Trailing</span>} actionAsChild>
            <a href="/">Content</a>
          </Tag>
        );
        expect(screen.getByTestId('trailing')).toBeTruthy();
      });
    });

    describe('with expandable', () => {
      it('render expandable icon', async () => {
        render(
          <Tag expandable actionAsChild>
            <a href="/">Content</a>
          </Tag>
        );
        const icon = screen.getByText('keyboard_arrow_down');
        expect(icon).toBeTruthy();
        expect(icon.className).toContain('material-icons');
      });
    });
  });
});
