import * as React from 'react';
import {
  render,
  screen,
  cleanup,
} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Button } from '@tesseract-design/web-base';
import {
  vi,
  describe,
  it,
  expect,
  afterEach,
} from 'vitest';
import matchers from '@testing-library/jest-dom/matchers';
import {
  ActionButton,
  ActionButtonDerivedElement,
} from '.';

expect.extend(matchers);

describe('ActionButton', () => {
  afterEach(() => {
    cleanup();
  });

  it('renders a button', () => {
    render(
      <ActionButton />,
    );
    const button = screen.getByRole('button');
    expect(button).toBeInTheDocument();
    expect(button).toHaveProperty('type', 'button');
  });

  it('renders a subtext', () => {
    render(
      <ActionButton
        subtext="subtext"
      />,
    );
    const subtext = screen.getByTestId('subtext');
    expect(subtext).toBeInTheDocument();
  });

  it('renders a badge', () => {
    render(
      <ActionButton
        badge="badge"
      />,
    );
    const badge = screen.getByTestId('badge');
    expect(badge).toBeInTheDocument();
  });

  it('renders as a menu item', () => {
    render(
      <ActionButton
        menuItem
      />,
    );
    const menuItemIndicator = screen.getByTestId('menuItemIndicator');
    expect(menuItemIndicator).toBeInTheDocument();
  });

  it('handles click events', async () => {
    const onClick = vi.fn().mockImplementationOnce(
      (e: React.MouseEvent<ActionButtonDerivedElement>) => {
        e.preventDefault();
      },
    );

    render(
      <ActionButton
        onClick={onClick}
      />,
    );
    const button = screen.getByRole('button');
    await userEvent.click(button);
    expect(onClick).toBeCalled();
  });

  it('renders a compact button', () => {
    render(
      <ActionButton
        compact
      />,
    );

    const button = screen.getByRole('button');
    expect(button).toHaveClass('pl-2 gap-2 pr-2');
  });

  describe.each`
    size        | className
    ${'small'}  | ${'h-10'}  
    ${'medium'} | ${'h-12'}  
    ${'large'}  | ${'h-16'}  
  `('on $size size', ({
    size,
    className,
  }: { size: Button.Size, className: string }) => {
    it('renders button styles', () => {
      render(
        <ActionButton
          size={size}
        />,
      );

      const button = screen.getByRole('button');
      expect(button).toHaveClass(className);
    });

    it('renders badge styles', () => {
      render(
        <ActionButton
          size={size}
          badge="badge"
        />,
      );

      const badge = screen.getByTestId('badge');
      expect(badge).toBeInTheDocument();
    });
  });

  it.each`
    variant      | className
    ${'bare'}    | ${'bg-negative'}
    ${'outline'} | ${'border-2'}
    ${'filled'}  | ${'bg-primary'}
  `('renders a button with $variant variant', ({
    variant,
    className,
  }: { variant: Button.Variant, className: string }) => {
    render(
      <ActionButton
        variant={variant}
      />,
    );

    const button = screen.getByRole('button');
    expect(button).toHaveClass(className);
  });

  it('renders a block button', () => {
    render(
      <ActionButton
        block
      />,
    );

    const button = screen.getByRole('button');
    expect(button).toHaveClass('w-full flex');
  });

  it('renders children', () => {
    render(
      <ActionButton>
        Foo
      </ActionButton>,
    );

    const children = screen.getByTestId('children');
    expect(children).toHaveTextContent('Foo');
  });

  it.each(Button.AVAILABLE_TYPES)('renders a button with type %s', (buttonType) => {
    render(
      <ActionButton
        type={buttonType}
      />,
    );
    const button = screen.getByRole('button');
    expect(button).toHaveProperty('type', buttonType);
  });

  it('renders a disabled button', () => {
    render(
      <ActionButton
        disabled
      />,
    );
    const button = screen.getByTestId('button');
    expect(button).toBeDisabled();
  });
});
