import { describe, expect, test, vi } from 'vitest'
import { render, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { ListDataUI } from './list-data'

describe('ListDataUI', () => {
  const data = [
    { id: '1', label: 'Population', value: '8.4M' },
    { id: '2', label: 'Median Age', value: '32 years' },
    { id: '3', label: 'GDP', value: '$1.2T' },
    { id: '4', label: 'Life Expectancy', value: '78.5 years' },
    { id: '5', label: 'Population Density', value: '345/km²' },
  ]

  test('renders correctly', () => {
    const { container } = render(<ListDataUI data={data} />)

    const rows = within(container).getAllByRole('listitem')
    expect(rows).toHaveLength(5)
  })

  test('renders skeleton when loading', () => {
    const { getByLabelText } = render(<ListDataUI data={data} isLoading />)
    expect(getByLabelText('List Data skeleton')).toBeTruthy()
  })

  test('with maxItems', async () => {
    const { container } = render(<ListDataUI data={data} maxItems={3} />)

    const rows = within(container).getAllByRole('listitem')
    expect(rows).toHaveLength(3)

    const showMoreButton = within(container).getByText('Show More')
    expect(showMoreButton).toBeTruthy()
    await userEvent.click(showMoreButton)

    const updatedRows = within(container).getAllByRole('listitem')
    expect(updatedRows).toHaveLength(5)

    const showLessButton = within(container).getByText('Show Less')
    expect(showLessButton).toBeTruthy()
    await userEvent.click(showLessButton)

    const finalRows = within(container).getAllByRole('listitem')
    expect(finalRows).toHaveLength(3)
  })

  test('with disabled items', () => {
    const disabledData = data.map((item) => ({ ...item, disabled: true }))
    const { container } = render(<ListDataUI data={disabledData} />)

    const rows = within(container).getAllByRole('listitem')

    for (const row of rows) {
      const computedStyle = window.getComputedStyle(row)
      expect(computedStyle.pointerEvents).toBe('none')
    }
  })

  test('with custom expand button labels', async () => {
    const { container } = render(
      <ListDataUI
        data={data}
        maxItems={3}
        labels={{ showMore: 'More', showLess: 'Less' }}
      />,
    )

    const moreButton = within(container).getByText('More')
    expect(moreButton).toBeTruthy()
    await userEvent.click(moreButton)

    const lessButton = within(container).getByText('Less')
    expect(lessButton).toBeTruthy()
  })

  test('with item click handler', async () => {
    const mockOnItemClick = vi.fn()

    const { container } = render(
      <ListDataUI data={data} onItemClick={mockOnItemClick} />,
    )

    const rows = within(container).getAllByRole('listitem')

    for (const [idx, row] of rows.entries()) {
      await userEvent.click(row)

      const foundItem = data.find((item) => item.id === data[idx]?.id)
      expect(mockOnItemClick).toHaveBeenCalledWith(foundItem)
      mockOnItemClick.mockClear()
    }
  })

  test('renders NoDataAlert when data is empty', () => {
    const { container } = render(<ListDataUI data={[]} />)
    // No <li> elements when there's no data
    expect(within(container).queryAllByRole('listitem')).toHaveLength(0)
  })

  test('uses EMPTY_DATA default when data prop is omitted', () => {
    // `data` is required by the type but has a runtime default of EMPTY_DATA.
    // Pass undefined to exercise the default branch.
    const { container } = render(
      <ListDataUI data={undefined as unknown as never} />,
    )
    expect(within(container).queryAllByRole('listitem')).toHaveLength(0)
  })

  test('onExpand fires with the previous showAll value when toggled', async () => {
    const onExpand = vi.fn()
    const { container } = render(
      <ListDataUI data={data} maxItems={3} onExpand={onExpand} />,
    )
    const showMore = within(container).getByText('Show More')
    await userEvent.click(showMore)
    // Called with prior showAll value (false)
    expect(onExpand).toHaveBeenCalledWith(false)
    onExpand.mockClear()
    const showLess = within(container).getByText('Show Less')
    await userEvent.click(showLess)
    // Now called with prior showAll value (true)
    expect(onExpand).toHaveBeenCalledWith(true)
  })

  test('applies `selected` style to the row matching selectedItemId', () => {
    const { container } = render(<ListDataUI data={data} selectedItemId='2' />)
    const rows = within(container).getAllByRole('listitem')
    expect(rows).toHaveLength(5)
    // Smoke-check that the selected branch executed without throwing
  })

  test('respects showDivider=false (skips the divider style branch)', () => {
    expect(() =>
      render(<ListDataUI data={data} showDivider={false} />),
    ).not.toThrow()
  })

  test('clicking a non-disabled sibling of a disabled item still fires onItemClick', async () => {
    // disabled items get pointer-events: none via the disabled style branch,
    // so userEvent can't synthesize a click on them anyway. We assert the
    // happy-path sibling fires correctly to confirm the disabled-row
    // rendering branch ran.
    const onItemClick = vi.fn()
    const mixedData = [
      { id: '1', label: 'A', value: '1', disabled: true },
      { id: '2', label: 'B', value: '2' },
    ]
    const { container } = render(
      <ListDataUI data={mixedData} onItemClick={onItemClick} />,
    )
    const rows = within(container).getAllByRole('listitem')
    await userEvent.click(rows[1]!)
    expect(onItemClick).toHaveBeenCalledWith(mixedData[1])
  })

  test('threads tooltipProps overrides into SmartTooltip', () => {
    expect(() =>
      render(
        <ListDataUI
          data={data}
          tooltipProps={{ placement: 'right', enterDelay: 500 }}
        />,
      ),
    ).not.toThrow()
  })
})
