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

interface TestItem {
  id: string
  name: string
}

const items: TestItem[] = [
  { id: '1', name: 'Alpha' },
  { id: '2', name: 'Beta' },
  { id: '3', name: 'Gamma' },
]

describe('EntityList', () => {
  it('renders all items', () => {
    render(
      <EntityList
        items={items}
        renderItem={(item) => <span>{item.name}</span>}
        getItemId={(item) => item.id}
      />
    )
    expect(screen.getByText('Alpha')).toBeInTheDocument()
    expect(screen.getByText('Beta')).toBeInTheDocument()
    expect(screen.getByText('Gamma')).toBeInTheDocument()
  })

  it('calls onSelect when item clicked', async () => {
    const onSelect = vi.fn()
    const user = userEvent.setup()
    render(
      <EntityList
        items={items}
        renderItem={(item) => <span>{item.name}</span>}
        getItemId={(item) => item.id}
        onSelect={onSelect}
      />
    )
    await user.click(screen.getByText('Beta'))
    expect(onSelect).toHaveBeenCalledWith('2')
  })

  it('highlights selected item (aria-selected="true" and has selected class)', () => {
    render(
      <EntityList
        items={items}
        renderItem={(item) => <span>{item.name}</span>}
        getItemId={(item) => item.id}
        selectedId="2"
      />
    )
    const selectedOption = screen.getByText('Beta').closest('[role="option"]')
    expect(selectedOption).toHaveAttribute('aria-selected', 'true')

    const unselectedOption = screen.getByText('Alpha').closest('[role="option"]')
    expect(unselectedOption).toHaveAttribute('aria-selected', 'false')
  })

  it('renders search input when onSearch provided', () => {
    render(
      <EntityList
        items={items}
        renderItem={(item) => <span>{item.name}</span>}
        getItemId={(item) => item.id}
        onSearch={() => {}}
        searchPlaceholder="Filter items..."
      />
    )
    expect(screen.getByPlaceholderText('Filter items...')).toBeInTheDocument()
  })

  it('calls onSearch when typing in search', async () => {
    const onSearch = vi.fn()
    const user = userEvent.setup()
    render(
      <EntityList
        items={items}
        renderItem={(item) => <span>{item.name}</span>}
        getItemId={(item) => item.id}
        onSearch={onSearch}
      />
    )
    const input = screen.getByPlaceholderText('Search...')
    await user.type(input, 'test')
    expect(onSearch).toHaveBeenLastCalledWith('test')
  })

  it('renders add button when onAdd provided', () => {
    render(
      <EntityList
        items={items}
        renderItem={(item) => <span>{item.name}</span>}
        getItemId={(item) => item.id}
        onAdd={() => {}}
        addLabel="Add Item"
      />
    )
    expect(screen.getByText('Add Item')).toBeInTheDocument()
  })

  it('calls onAdd when add button clicked', async () => {
    const onAdd = vi.fn()
    const user = userEvent.setup()
    render(
      <EntityList
        items={items}
        renderItem={(item) => <span>{item.name}</span>}
        getItemId={(item) => item.id}
        onAdd={onAdd}
        addLabel="Add Item"
      />
    )
    await user.click(screen.getByText('Add Item'))
    expect(onAdd).toHaveBeenCalledOnce()
  })

  it('hides header when no search or add', () => {
    const { container } = render(
      <EntityList
        items={items}
        renderItem={(item) => <span>{item.name}</span>}
        getItemId={(item) => item.id}
      />
    )
    // No input or button should be present in the header area
    expect(container.querySelector('input')).toBeNull()
    expect(container.querySelector('button')).toBeNull()
  })

  it('shows empty message when items is empty', () => {
    render(
      <EntityList
        items={[]}
        renderItem={(item: TestItem) => <span>{item.name}</span>}
        getItemId={(item: TestItem) => item.id}
      />
    )
    expect(screen.getByText('No items found')).toBeInTheDocument()
  })

  it('shows custom empty message', () => {
    render(
      <EntityList
        items={[]}
        renderItem={(item: TestItem) => <span>{item.name}</span>}
        getItemId={(item: TestItem) => item.id}
        emptyMessage="Nothing here"
      />
    )
    expect(screen.getByText('Nothing here')).toBeInTheDocument()
  })

  it('accepts className', () => {
    const { container } = render(
      <EntityList
        items={items}
        renderItem={(item) => <span>{item.name}</span>}
        getItemId={(item) => item.id}
        className="custom-class"
      />
    )
    expect(container.firstChild).toHaveClass('custom-class')
  })
})
