import { render, screen, fireEvent } from '@testing-library/react'
import { describe, it, expect, vi } from 'vitest'

import { DataTable, type Column } from '../DataTable'

interface TestRow {
  id: string
  name: string
  status: string
}

const mockData: TestRow[] = [
  { id: '1', name: 'Item 1', status: 'Active' },
  { id: '2', name: 'Item 2', status: 'Inactive' },
  { id: '3', name: 'Item 3', status: 'Active' },
]

const mockColumns: Column<TestRow>[] = [
  { key: 'name', header: 'Name', cell: (row) => row.name },
  { key: 'status', header: 'Status', cell: (row) => row.status },
]

const sortableColumns: Column<TestRow>[] = [
  { key: 'name', header: 'Name', cell: (row) => row.name, sortable: true },
  { key: 'status', header: 'Status', cell: (row) => row.status, sortable: true },
]

describe('DataTable', () => {
  it('renders correctly with data', () => {
    render(
      <DataTable
        data={mockData}
        columns={mockColumns}
        rowKey={(row) => row.id}
      />
    )

    expect(screen.getByText('Name')).toBeInTheDocument()
    expect(screen.getByText('Status')).toBeInTheDocument()
    expect(screen.getByText('Item 1')).toBeInTheDocument()
    expect(screen.getByText('Item 2')).toBeInTheDocument()
    expect(screen.getByText('Item 3')).toBeInTheDocument()
  })

  it('renders empty state when no data', () => {
    render(
      <DataTable
        data={[]}
        columns={mockColumns}
        rowKey={(row) => row.id}
      />
    )

    expect(screen.getByText('No data available')).toBeInTheDocument()
  })

  it('renders custom empty state', () => {
    render(
      <DataTable
        data={[]}
        columns={mockColumns}
        rowKey={(row) => row.id}
        emptyState={<div>Custom empty message</div>}
      />
    )

    expect(screen.getByText('Custom empty message')).toBeInTheDocument()
  })

  it('renders loading state', () => {
    render(
      <DataTable
        data={[]}
        columns={mockColumns}
        rowKey={(row) => row.id}
        isLoading={true}
      />
    )

    // Loading spinner should be present (animated div)
    const spinnerContainer = document.querySelector('.animate-spin')
    expect(spinnerContainer).toBeInTheDocument()
  })

  it('handles sortable column click - ascending', () => {
    render(
      <DataTable
        data={mockData}
        columns={sortableColumns}
        rowKey={(row) => row.id}
      />
    )

    const nameHeader = screen.getByText('Name').closest('th')
    expect(nameHeader).toHaveAttribute('role', 'button')

    fireEvent.click(nameHeader!)
    expect(nameHeader).toHaveAttribute('aria-sort', 'ascending')
  })

  it('handles sortable column click - descending on second click', () => {
    render(
      <DataTable
        data={mockData}
        columns={sortableColumns}
        rowKey={(row) => row.id}
      />
    )

    const nameHeader = screen.getByText('Name').closest('th')

    // First click - ascending
    fireEvent.click(nameHeader!)
    expect(nameHeader).toHaveAttribute('aria-sort', 'ascending')

    // Second click - descending
    fireEvent.click(nameHeader!)
    expect(nameHeader).toHaveAttribute('aria-sort', 'descending')
  })

  it('handles sortable column click - resets on third click', () => {
    render(
      <DataTable
        data={mockData}
        columns={sortableColumns}
        rowKey={(row) => row.id}
      />
    )

    const nameHeader = screen.getByText('Name').closest('th')

    // First click - ascending
    fireEvent.click(nameHeader!)
    // Second click - descending
    fireEvent.click(nameHeader!)
    // Third click - reset
    fireEvent.click(nameHeader!)
    expect(nameHeader).not.toHaveAttribute('aria-sort')
  })

  it('handles sortable column keyboard navigation', () => {
    render(
      <DataTable
        data={mockData}
        columns={sortableColumns}
        rowKey={(row) => row.id}
      />
    )

    const nameHeader = screen.getByText('Name').closest('th')

    fireEvent.keyDown(nameHeader!, { key: 'Enter' })
    expect(nameHeader).toHaveAttribute('aria-sort', 'ascending')

    fireEvent.keyDown(nameHeader!, { key: ' ' })
    expect(nameHeader).toHaveAttribute('aria-sort', 'descending')
  })

  it('handles row selection - single row', () => {
    const onSelectionChange = vi.fn()
    render(
      <DataTable
        data={mockData}
        columns={mockColumns}
        rowKey={(row) => row.id}
        selectable={true}
        selectedKeys={new Set()}
        onSelectionChange={onSelectionChange}
      />
    )

    const checkboxes = screen.getAllByRole('checkbox')
    // First checkbox is "select all", row checkboxes start at index 1
    fireEvent.click(checkboxes[1])

    expect(onSelectionChange).toHaveBeenCalledWith(new Set(['1']))
  })

  it('handles row selection - deselect row', () => {
    const onSelectionChange = vi.fn()
    render(
      <DataTable
        data={mockData}
        columns={mockColumns}
        rowKey={(row) => row.id}
        selectable={true}
        selectedKeys={new Set(['1'])}
        onSelectionChange={onSelectionChange}
      />
    )

    const checkboxes = screen.getAllByRole('checkbox')
    fireEvent.click(checkboxes[1])

    expect(onSelectionChange).toHaveBeenCalledWith(new Set())
  })

  it('handles row selection - select all', () => {
    const onSelectionChange = vi.fn()
    render(
      <DataTable
        data={mockData}
        columns={mockColumns}
        rowKey={(row) => row.id}
        selectable={true}
        selectedKeys={new Set()}
        onSelectionChange={onSelectionChange}
      />
    )

    const checkboxes = screen.getAllByRole('checkbox')
    // First checkbox is "select all"
    fireEvent.click(checkboxes[0])

    expect(onSelectionChange).toHaveBeenCalledWith(new Set(['1', '2', '3']))
  })

  it('handles row selection - deselect all when all selected', () => {
    const onSelectionChange = vi.fn()
    render(
      <DataTable
        data={mockData}
        columns={mockColumns}
        rowKey={(row) => row.id}
        selectable={true}
        selectedKeys={new Set(['1', '2', '3'])}
        onSelectionChange={onSelectionChange}
      />
    )

    const checkboxes = screen.getAllByRole('checkbox')
    // First checkbox is "select all" - clicking it deselects all
    fireEvent.click(checkboxes[0])

    expect(onSelectionChange).toHaveBeenCalledWith(new Set())
  })

  it('handles row click', () => {
    const onRowClick = vi.fn()
    render(
      <DataTable
        data={mockData}
        columns={mockColumns}
        rowKey={(row) => row.id}
        onRowClick={onRowClick}
      />
    )

    const row = screen.getByText('Item 1').closest('tr')
    fireEvent.click(row!)

    expect(onRowClick).toHaveBeenCalledWith(mockData[0])
  })

  it('handles row keyboard navigation', () => {
    const onRowClick = vi.fn()
    render(
      <DataTable
        data={mockData}
        columns={mockColumns}
        rowKey={(row) => row.id}
        onRowClick={onRowClick}
      />
    )

    const row = screen.getByText('Item 1').closest('tr')

    fireEvent.keyDown(row!, { key: 'Enter' })
    expect(onRowClick).toHaveBeenCalledWith(mockData[0])

    fireEvent.keyDown(row!, { key: ' ' })
    expect(onRowClick).toHaveBeenCalledTimes(2)
  })

  it('applies sticky header class when stickyHeader is true', () => {
    render(
      <DataTable
        data={mockData}
        columns={mockColumns}
        rowKey={(row) => row.id}
        stickyHeader={true}
      />
    )

    const thead = document.querySelector('thead')
    expect(thead).toHaveClass('sticky')
  })

  it('applies custom className', () => {
    const { container } = render(
      <DataTable
        data={mockData}
        columns={mockColumns}
        rowKey={(row) => row.id}
        className="custom-class"
      />
    )

    expect(container.firstChild).toHaveClass('custom-class')
  })

  it('highlights selected rows', () => {
    render(
      <DataTable
        data={mockData}
        columns={mockColumns}
        rowKey={(row) => row.id}
        selectable={true}
        selectedKeys={new Set(['1'])}
        onSelectionChange={() => {}}
      />
    )

    const selectedRow = screen.getByText('Item 1').closest('tr')
    expect(selectedRow).toHaveClass('bg-brand-500/5')
  })

  it('does not call onSelectionChange when not provided', () => {
    // This test ensures no error when clicking selection without handler
    render(
      <DataTable
        data={mockData}
        columns={mockColumns}
        rowKey={(row) => row.id}
        selectable={true}
        selectedKeys={new Set()}
      />
    )

    const checkboxes = screen.getAllByRole('checkbox')
    // Should not throw
    fireEvent.click(checkboxes[0])
    fireEvent.click(checkboxes[1])
  })

  it('applies column width when specified', () => {
    const columnsWithWidth: Column<TestRow>[] = [
      { key: 'name', header: 'Name', cell: (row) => row.name, width: '200px' },
      { key: 'status', header: 'Status', cell: (row) => row.status },
    ]

    render(
      <DataTable
        data={mockData}
        columns={columnsWithWidth}
        rowKey={(row) => row.id}
      />
    )

    const nameHeader = screen.getByText('Name').closest('th')
    expect(nameHeader).toHaveStyle({ width: '200px' })
  })
})
