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

interface TestData {
  id: string; // UUID from backend
  name: string;
  age: number;
  email: string;
}

describe('Table Component', () => {
  const mockData: TestData[] = [
    { id: '1', name: 'John Doe', age: 30, email: 'john@example.com' },
    { id: '2', name: 'Jane Smith', age: 25, email: 'jane@example.com' },
    { id: '3', name: 'Bob Johnson', age: 35, email: 'bob@example.com' },
  ];

  const mockColumns: TableColumn<TestData>[] = [
    { key: 'name', header: 'Name', sortable: true },
    { key: 'age', header: 'Age', sortable: true },
    { key: 'email', header: 'Email', sortable: false },
  ];

  const mockOnSort = vi.fn();
  const mockOnRowClick = vi.fn();
  const mockOnSelectionChange = vi.fn();

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

  it('renders table with data', () => {
    render(<Table columns={mockColumns} data={mockData} />);

    expect(screen.getByText('John Doe')).toBeInTheDocument();
    expect(screen.getByText('Jane Smith')).toBeInTheDocument();
    expect(screen.getByText('Bob Johnson')).toBeInTheDocument();
  });

  it('renders table headers', () => {
    render(<Table columns={mockColumns} data={mockData} />);

    expect(screen.getByText('Name')).toBeInTheDocument();
    expect(screen.getByText('Age')).toBeInTheDocument();
    expect(screen.getByText('Email')).toBeInTheDocument();
  });

  it('renders empty message when data is empty', () => {
    render(<Table columns={mockColumns} data={[]} />);

    expect(screen.getByText('Aucune donnée disponible')).toBeInTheDocument();
  });

  it('renders custom empty message', () => {
    render(
      <Table columns={mockColumns} data={[]} emptyMessage="No data found" />,
    );

    expect(screen.getByText('No data found')).toBeInTheDocument();
  });

  it('calls onSort when sortable column header is clicked', async () => {
    const user = userEvent.setup();
    render(<Table columns={mockColumns} data={mockData} onSort={mockOnSort} />);

    const nameHeader = screen.getByText('Name');
    await user.click(nameHeader);

    expect(mockOnSort).toHaveBeenCalledWith('name', 'asc');
  });

  it('toggles sort direction when clicking same column twice', async () => {
    const user = userEvent.setup();
    render(<Table columns={mockColumns} data={mockData} onSort={mockOnSort} />);

    const nameHeader = screen.getByText('Name');
    await user.click(nameHeader);
    await user.click(nameHeader);

    expect(mockOnSort).toHaveBeenCalledWith('name', 'desc');
  });

  it('calls onRowClick when row is clicked', async () => {
    const user = userEvent.setup();
    render(
      <Table
        columns={mockColumns}
        data={mockData}
        onRowClick={mockOnRowClick}
      />,
    );

    const firstRow = screen.getByText('John Doe').closest('tr');
    if (firstRow) {
      await user.click(firstRow);
      expect(mockOnRowClick).toHaveBeenCalledWith(mockData[0], 0);
    }
  });

  it('renders selectable checkbox column when selectable is true', () => {
    render(<Table columns={mockColumns} data={mockData} selectable />);

    const checkboxes = screen.getAllByRole('checkbox');
    expect(checkboxes.length).toBeGreaterThan(0);
  });

  it('selects row when checkbox is clicked', async () => {
    const user = userEvent.setup();
    render(
      <Table
        columns={mockColumns}
        data={mockData}
        selectable
        onSelectionChange={mockOnSelectionChange}
      />,
    );

    const checkboxes = screen.getAllByRole('checkbox');
    // First checkbox is select all, second is first row
    await user.click(checkboxes[1]);

    expect(mockOnSelectionChange).toHaveBeenCalled();
    const selectedData = mockOnSelectionChange.mock.calls[0][0];
    expect(selectedData).toHaveLength(1);
    expect(selectedData[0].id).toBe(1);
  });

  it('selects all rows when select all checkbox is clicked', async () => {
    const user = userEvent.setup();
    render(
      <Table
        columns={mockColumns}
        data={mockData}
        selectable
        onSelectionChange={mockOnSelectionChange}
      />,
    );

    const checkboxes = screen.getAllByRole('checkbox');
    // First checkbox is select all
    await user.click(checkboxes[0]);

    expect(mockOnSelectionChange).toHaveBeenCalled();
    const selectedData = mockOnSelectionChange.mock.calls[0][0];
    expect(selectedData).toHaveLength(3);
  });

  it('uses custom render function for columns', () => {
    const columnsWithRender: TableColumn<TestData>[] = [
      {
        key: 'name',
        header: 'Name',
        render: (row) => <strong>{row.name}</strong>,
      },
    ];

    render(<Table columns={columnsWithRender} data={mockData} />);

    const strongElement = screen.getByText('John Doe').closest('strong');
    expect(strongElement).toBeInTheDocument();
  });

  it('renders pagination when paginated is true', () => {
    const largeData = Array.from({ length: 25 }, (_, i) => ({
      id: i + 1,
      name: `User ${i + 1}`,
      age: 20 + i,
      email: `user${i + 1}@example.com`,
    }));

    render(
      <Table
        columns={mockColumns}
        data={largeData}
        paginated
        itemsPerPage={10}
      />,
    );

    // Pagination should be rendered
    const previousButton = screen.getByLabelText('Previous page');
    expect(previousButton).toBeInTheDocument();
  });

  it('displays correct page data when paginated', () => {
    const largeData = Array.from({ length: 25 }, (_, i) => ({
      id: i + 1,
      name: `User ${i + 1}`,
      age: 20 + i,
      email: `user${i + 1}@example.com`,
    }));

    render(
      <Table
        columns={mockColumns}
        data={largeData}
        paginated
        itemsPerPage={10}
      />,
    );

    // First page should show first 10 items
    expect(screen.getByText('User 1')).toBeInTheDocument();
    expect(screen.getByText('User 10')).toBeInTheDocument();
    expect(screen.queryByText('User 11')).not.toBeInTheDocument();
  });

  it('changes page when pagination is clicked', async () => {
    const user = userEvent.setup();
    const largeData = Array.from({ length: 25 }, (_, i) => ({
      id: i + 1,
      name: `User ${i + 1}`,
      age: 20 + i,
      email: `user${i + 1}@example.com`,
    }));

    render(
      <Table
        columns={mockColumns}
        data={largeData}
        paginated
        itemsPerPage={10}
      />,
    );

    // Click next page
    const nextButton = screen.getByLabelText('Next page');
    await user.click(nextButton);

    // Should show page 2 data
    expect(screen.getByText('User 11')).toBeInTheDocument();
    expect(screen.queryByText('User 1')).not.toBeInTheDocument();
  });

  it('uses custom getRowId function', () => {
    render(
      <Table
        columns={mockColumns}
        data={mockData}
        selectable
        getRowId={(row) => `row-${row.id}`}
      />,
    );

    // Should render without errors
    expect(screen.getByText('John Doe')).toBeInTheDocument();
  });

  it('applies custom className', () => {
    const { container } = render(
      <Table columns={mockColumns} data={mockData} className="custom-table" />,
    );

    const tableContainer = container.firstChild;
    expect(tableContainer).toHaveClass('custom-table');
  });

  it('applies custom rowClassName', () => {
    render(
      <Table
        columns={mockColumns}
        data={mockData}
        rowClassName={(row) => (row.age > 30 ? 'highlighted' : '')}
      />,
    );

    // Bob is 35, so should have highlighted class
    const bobRow = screen.getByText('Bob Johnson').closest('tr');
    expect(bobRow).toHaveClass('highlighted');
  });

  it('does not show sort icon for non-sortable columns', () => {
    render(<Table columns={mockColumns} data={mockData} />);

    // Email column is not sortable, so should not have sort icon
    const emailHeader = screen.getByText('Email').closest('th');
    expect(emailHeader).not.toHaveClass('cursor-pointer');
  });

  it('handles column alignment', () => {
    const columnsWithAlign: TableColumn<TestData>[] = [
      { key: 'name', header: 'Name', align: 'left' },
      { key: 'age', header: 'Age', align: 'center' },
      { key: 'email', header: 'Email', align: 'right' },
    ];

    render(<Table columns={columnsWithAlign} data={mockData} />);

    const ageHeader = screen.getByText('Age').closest('th');
    expect(ageHeader).toHaveClass('text-center');

    const emailHeader = screen.getByText('Email').closest('th');
    expect(emailHeader).toHaveClass('text-right');
  });

  it('maintains selection across pages when paginated', async () => {
    const user = userEvent.setup();
    const largeData = Array.from({ length: 25 }, (_, i) => ({
      id: i + 1,
      name: `User ${i + 1}`,
      age: 20 + i,
      email: `user${i + 1}@example.com`,
    }));

    render(
      <Table
        columns={mockColumns}
        data={largeData}
        paginated
        itemsPerPage={10}
        selectable
        onSelectionChange={mockOnSelectionChange}
      />,
    );

    // Select first row on page 1
    const checkboxes = screen.getAllByRole('checkbox');
    await user.click(checkboxes[1]);

    // Go to page 2
    const nextButton = screen.getByLabelText('Next page');
    await user.click(nextButton);

    // Go back to page 1
    const previousButton = screen.getByLabelText('Previous page');
    await user.click(previousButton);

    // First row should still be selected
    const newCheckboxes = screen.getAllByRole('checkbox');
    expect(newCheckboxes[1]).toBeChecked();
  });
});
