import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import userEvent from '@testing-library/user-event';
import { Tabs } from './Tabs';

describe('Tabs Component', () => {
  const mockItems = [
    { id: 'tab1', label: 'Tab 1', content: <div>Content 1</div> },
    { id: 'tab2', label: 'Tab 2', content: <div>Content 2</div> },
    { id: 'tab3', label: 'Tab 3', content: <div>Content 3</div> },
  ];

  const mockOnChange = vi.fn();

  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('renders tabs with labels', () => {
    render(<Tabs items={mockItems} />);

    expect(screen.getByText('Tab 1')).toBeInTheDocument();
    expect(screen.getByText('Tab 2')).toBeInTheDocument();
    expect(screen.getByText('Tab 3')).toBeInTheDocument();
  });

  it('renders content of first tab by default', () => {
    render(<Tabs items={mockItems} />);

    expect(screen.getByText('Content 1')).toBeInTheDocument();
    expect(screen.queryByText('Content 2')).not.toBeInTheDocument();
  });

  it('renders content of defaultActiveId tab', () => {
    render(<Tabs items={mockItems} defaultActiveId="tab2" />);

    expect(screen.getByText('Content 2')).toBeInTheDocument();
    expect(screen.queryByText('Content 1')).not.toBeInTheDocument();
  });

  it('switches content when tab is clicked', async () => {
    const user = userEvent.setup();
    render(<Tabs items={mockItems} />);

    const tab2 = screen.getByText('Tab 2');
    await user.click(tab2);

    expect(screen.getByText('Content 2')).toBeInTheDocument();
    expect(screen.queryByText('Content 1')).not.toBeInTheDocument();
  });

  it('calls onChange when tab is clicked', async () => {
    const user = userEvent.setup();
    render(<Tabs items={mockItems} onChange={mockOnChange} />);

    const tab2 = screen.getByText('Tab 2');
    await user.click(tab2);

    expect(mockOnChange).toHaveBeenCalledWith('tab2');
  });

  it('uses controlled activeId when provided', () => {
    const { rerender } = render(<Tabs items={mockItems} activeId="tab2" />);

    expect(screen.getByText('Content 2')).toBeInTheDocument();

    rerender(<Tabs items={mockItems} activeId="tab3" />);

    expect(screen.getByText('Content 3')).toBeInTheDocument();
  });

  it('navigates to next tab with ArrowRight key', async () => {
    render(<Tabs items={mockItems} defaultActiveId="tab1" />);

    const tab1 = screen.getByRole('tab', { name: 'Tab 1' });
    tab1.focus();

    fireEvent.keyDown(tab1, { key: 'ArrowRight', code: 'ArrowRight' });

    await waitFor(() => {
      expect(screen.getByText('Content 2')).toBeInTheDocument();
    });
  });

  it('navigates to previous tab with ArrowLeft key', async () => {
    render(<Tabs items={mockItems} defaultActiveId="tab2" />);

    const tab2 = screen.getByRole('tab', { name: 'Tab 2' });
    tab2.focus();

    fireEvent.keyDown(tab2, { key: 'ArrowLeft', code: 'ArrowLeft' });

    await waitFor(() => {
      expect(screen.getByText('Content 1')).toBeInTheDocument();
    });
  });

  it('navigates to first tab with Home key', async () => {
    render(<Tabs items={mockItems} defaultActiveId="tab3" />);

    const tab3 = screen.getByRole('tab', { name: 'Tab 3' });
    tab3.focus();

    fireEvent.keyDown(tab3, { key: 'Home', code: 'Home' });

    await waitFor(() => {
      expect(screen.getByText('Content 1')).toBeInTheDocument();
    });
  });

  it('navigates to last tab with End key', async () => {
    render(<Tabs items={mockItems} defaultActiveId="tab1" />);

    const tab1 = screen.getByRole('tab', { name: 'Tab 1' });
    tab1.focus();

    fireEvent.keyDown(tab1, { key: 'End', code: 'End' });

    await waitFor(() => {
      expect(screen.getByText('Content 3')).toBeInTheDocument();
    });
  });

  it('wraps around when navigating with ArrowRight at last tab', async () => {
    render(<Tabs items={mockItems} defaultActiveId="tab3" />);

    const tab3 = screen.getByRole('tab', { name: 'Tab 3' });
    tab3.focus();

    fireEvent.keyDown(tab3, { key: 'ArrowRight', code: 'ArrowRight' });

    await waitFor(() => {
      expect(screen.getByText('Content 1')).toBeInTheDocument();
    });
  });

  it('wraps around when navigating with ArrowLeft at first tab', async () => {
    render(<Tabs items={mockItems} defaultActiveId="tab1" />);

    const tab1 = screen.getByRole('tab', { name: 'Tab 1' });
    tab1.focus();

    fireEvent.keyDown(tab1, { key: 'ArrowLeft', code: 'ArrowLeft' });

    await waitFor(() => {
      expect(screen.getByText('Content 3')).toBeInTheDocument();
    });
  });

  it('disables tab when disabled prop is true', () => {
    const itemsWithDisabled = [
      { id: 'tab1', label: 'Tab 1', content: <div>Content 1</div> },
      {
        id: 'tab2',
        label: 'Tab 2',
        content: <div>Content 2</div>,
        disabled: true,
      },
    ];

    render(<Tabs items={itemsWithDisabled} />);

    const tab2 = screen.getByRole('tab', { name: 'Tab 2' });
    expect(tab2).toBeDisabled();
  });

  it('skips disabled tabs when navigating with keyboard', async () => {
    const itemsWithDisabled = [
      { id: 'tab1', label: 'Tab 1', content: <div>Content 1</div> },
      {
        id: 'tab2',
        label: 'Tab 2',
        content: <div>Content 2</div>,
        disabled: true,
      },
      { id: 'tab3', label: 'Tab 3', content: <div>Content 3</div> },
    ];

    render(<Tabs items={itemsWithDisabled} defaultActiveId="tab1" />);

    const tab1 = screen.getByRole('tab', { name: 'Tab 1' });
    tab1.focus();

    fireEvent.keyDown(tab1, { key: 'ArrowRight', code: 'ArrowRight' });

    // Devrait aller à tab3, en sautant tab2 désactivé
    await waitFor(() => {
      expect(screen.getByText('Content 3')).toBeInTheDocument();
    });
  });

  it('does not switch to disabled tab when clicked', async () => {
    const user = userEvent.setup();
    const itemsWithDisabled = [
      { id: 'tab1', label: 'Tab 1', content: <div>Content 1</div> },
      {
        id: 'tab2',
        label: 'Tab 2',
        content: <div>Content 2</div>,
        disabled: true,
      },
    ];

    render(<Tabs items={itemsWithDisabled} />);

    const tab2 = screen.getByText('Tab 2');
    await user.click(tab2);

    expect(screen.getByText('Content 1')).toBeInTheDocument();
    expect(screen.queryByText('Content 2')).not.toBeInTheDocument();
  });

  it('renders tab with icon', () => {
    const itemsWithIcon = [
      {
        id: 'tab1',
        label: 'Tab 1',
        content: <div>Content 1</div>,
        icon: <span data-testid="icon">📊</span>,
      },
    ];

    render(<Tabs items={itemsWithIcon} />);

    expect(screen.getByTestId('icon')).toBeInTheDocument();
  });

  it('applies default variant styles', () => {
    render(<Tabs items={mockItems} variant="default" />);

    const tabList = screen.getByRole('tablist');
    expect(tabList).toHaveClass('border-b');
  });

  it('applies pills variant styles', () => {
    render(<Tabs items={mockItems} variant="pills" />);

    const tabList = screen.getByText('Tab 1').closest('[role="tablist"]');
    expect(tabList).toHaveClass('bg-muted');
  });

  it('applies underline variant styles', () => {
    render(<Tabs items={mockItems} variant="underline" />);

    const tabList = screen.getByText('Tab 1').closest('[role="tablist"]');
    expect(tabList).toHaveClass('border-b');
  });

  it('has correct ARIA attributes', () => {
    render(<Tabs items={mockItems} defaultActiveId="tab1" />);

    const tab1 = screen.getByRole('tab', { name: 'Tab 1' });
    expect(tab1).toHaveAttribute('role', 'tab');
    expect(tab1).toHaveAttribute('aria-selected', 'true');
    expect(tab1).toHaveAttribute('aria-controls', 'tabpanel-tab1');

    const tabpanel = screen.getByRole('tabpanel');
    expect(tabpanel).toHaveAttribute('id', 'tabpanel-tab1');
    expect(tabpanel).toHaveAttribute('aria-labelledby', 'tab-tab1');
  });

  it('sets tabIndex correctly', () => {
    render(<Tabs items={mockItems} defaultActiveId="tab1" />);

    const tab1 = screen.getByRole('tab', { name: 'Tab 1' });
    expect(tab1).toHaveAttribute('tabindex', '0');

    const tab2 = screen.getByRole('tab', { name: 'Tab 2' });
    expect(tab2).toHaveAttribute('tabindex', '-1');
  });

  it('applies custom className', () => {
    const { container } = render(
      <Tabs items={mockItems} className="custom-class" />,
    );

    const tabsContainer = container.firstChild;
    expect(tabsContainer).toHaveClass('custom-class');
  });

  it('handles empty items array', () => {
    render(<Tabs items={[]} />);

    expect(screen.queryByRole('tab')).not.toBeInTheDocument();
  });

  it('selects first enabled tab when defaultActiveId points to disabled tab', async () => {
    const itemsWithDisabled = [
      {
        id: 'tab1',
        label: 'Tab 1',
        content: <div>Content 1</div>,
        disabled: true,
      },
      { id: 'tab2', label: 'Tab 2', content: <div>Content 2</div> },
    ];

    render(<Tabs items={itemsWithDisabled} defaultActiveId="tab1" />);

    await waitFor(() => {
      expect(screen.getByText('Content 2')).toBeInTheDocument();
    });
  });
});
