import { render, screen, userEvent } from '../test-utils';
import Tabs, { TabsProps } from './Tabs';

const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTimeAsync });

if (typeof performance === 'undefined') {
  global.performance = { now: () => Date.now() } as Performance;
}

const defaultTabsStatus = [false, true, false];

function generateTabs(tabsStatus = defaultTabsStatus) {
  return tabsStatus.map((disabled, index) => ({
    disabled,
    title: `Title ${index}`,
    content: <p data-testid={`content-${index}`}>Content {index}</p>,
  }));
}

describe('Tabs', () => {
  const initialProps: TabsProps = {
    tabs: generateTabs(),
    changeTabOnSwipe: true,
    name: 'testName',
    headerWidth: 'block',
    selected: 0,
    onTabSelect: jest.fn(),
    className: 'custom-class-name',
  };

  beforeEach(() => {
    jest.clearAllMocks();
    jest.useFakeTimers();
  });

  afterEach(async () => {
    jest.runOnlyPendingTimers();
    jest.useRealTimers();
  });

  it('should render a tablist', () => {
    render(<Tabs {...initialProps} />);
    expect(screen.getByRole('tablist')).toBeInTheDocument();
  });

  it('should respect className', () => {
    render(<Tabs {...initialProps} />);
    expect(screen.getByRole('tablist').parentNode).toHaveClass(String(initialProps.className));
  });

  it('should respect `name` prop', () => {
    render(<Tabs {...initialProps} />);
    const controlsTarget = screen
      .getByRole('tab', { selected: true })
      .getAttribute('aria-controls');
    expect(screen.getByRole('tabpanel')).toHaveAttribute('id', controlsTarget);
  });

  it('should render correct number of tabs', () => {
    render(<Tabs {...initialProps} />);
    expect(screen.getAllByRole('tab')).toHaveLength(defaultTabsStatus.length);
    initialProps.tabs.forEach(({ title }) => {
      expect(screen.getByText(title)).toBeInTheDocument();
    });
  });

  it('should show first tab content by default', () => {
    render(<Tabs {...initialProps} />);
    expect(screen.getAllByRole('tabpanel')).toHaveLength(1);
    expect(screen.getByTestId('content-0')).toBeInTheDocument();
  });

  describe('tab switch', () => {
    it('should call onTabSelecton on click', async () => {
      render(<Tabs {...initialProps} />);
      const thirdTab = screen.getAllByRole('tab')[2];
      await user.click(thirdTab);

      // It's called on mount first, that's why the count is at 2
      expect(initialProps.onTabSelect).toHaveBeenCalledTimes(2);
      expect(initialProps.onTabSelect).toHaveBeenCalledWith(2);
    });

    it('should call onTabSelecton when navigating with keyboard', async () => {
      render(<Tabs {...initialProps} />);
      await user.keyboard('{Tab}{Tab}{Tab}{Enter}');

      // It's called on mount first, that's why the count is at 2
      expect(initialProps.onTabSelect).toHaveBeenCalledTimes(2);
      expect(initialProps.onTabSelect).toHaveBeenCalledWith(2);
    });

    it('should call onTabSelecton on swipe', async () => {
      render(<Tabs {...initialProps} />);
      await user.keyboard('{Tab}{Tab}{Tab}{Enter}');

      // It's called on mount first, that's why the count is at 2
      expect(initialProps.onTabSelect).toHaveBeenCalledTimes(2);
      expect(initialProps.onTabSelect).toHaveBeenCalledWith(2);
    });

    it('should highlight selected tab', async () => {
      const { rerender } = render(<Tabs {...initialProps} />);
      rerender(<Tabs {...initialProps} selected={2} />);

      expect(screen.getByRole('tab', { selected: true })).toHaveTextContent('Title 2');
    });

    it('should switch content tab', async () => {
      const { rerender } = render(<Tabs {...initialProps} />);
      rerender(<Tabs {...initialProps} selected={2} />);
      expect(screen.getByRole('tabpanel')).toHaveTextContent('Content 2');
    });
  });
});
