import * as React from 'react'
import { render, screen, fireEvent } from '@testing-library/react'
import '@testing-library/jest-dom'
import { CAutocompleteOptions } from '../CAutocompleteOptions'
import type { Option, OptionsGroup } from '../types'

const mockOptions = [
  { label: 'Apple', value: 1 },
  { label: 'Banana', value: 2 },
  { label: 'Cherry', value: 3 },
  { label: 'Date', value: 4 },
]

const mockOptionsWithGroups = [
  {
    label: 'Fruits',
    options: [
      { label: 'Apple', value: 1 },
      { label: 'Banana', value: 2 },
    ],
  },
  {
    label: 'Vegetables',
    options: [
      { label: 'Carrot', value: 3 },
      { label: 'Spinach', value: 4 },
    ],
  },
]

test('loads and displays CAutocompleteOptions component', async () => {
  const { container } = render(<CAutocompleteOptions options={mockOptions} selected={null} />)
  expect(container).toMatchSnapshot()
})

test('CAutocompleteOptions renders all options', async () => {
  render(<CAutocompleteOptions options={mockOptions} selected={null} />)

  expect(screen.getByText('Apple')).toBeVisible()
  expect(screen.getByText('Banana')).toBeVisible()
  expect(screen.getByText('Cherry')).toBeVisible()
  expect(screen.getByText('Date')).toBeVisible()
})

test('CAutocompleteOptions renders option groups', async () => {
  render(<CAutocompleteOptions options={mockOptionsWithGroups} selected={null} />)

  expect(screen.getByText('Fruits')).toBeVisible()
  expect(screen.getByText('Vegetables')).toBeVisible()
  expect(screen.getByText('Apple')).toBeVisible()
  expect(screen.getByText('Carrot')).toBeVisible()
})

test('CAutocompleteOptions marks selected option', async () => {
  render(<CAutocompleteOptions options={mockOptions} selected={mockOptions[0]} />)

  const selectedOption = screen.getByText('Apple').closest('.autocomplete-option')
  expect(selectedOption).toHaveClass('selected')
})

test('CAutocompleteOptions calls onOptionClick when option clicked', async () => {
  const onOptionClick = jest.fn()
  render(<CAutocompleteOptions options={mockOptions} selected={null} onOptionClick={onOptionClick} />)

  const option = screen.getByText('Apple')
  fireEvent.click(option)

  expect(onOptionClick).toHaveBeenCalledWith(mockOptions[0])
})

test('CAutocompleteOptions handles Enter key press', async () => {
  const onOptionClick = jest.fn()
  render(<CAutocompleteOptions options={mockOptions} selected={null} onOptionClick={onOptionClick} />)

  const option = screen.getByText('Apple')
  fireEvent.keyDown(option, { key: 'Enter' })

  expect(onOptionClick).toHaveBeenCalledWith(mockOptions[0])
})

test('CAutocompleteOptions handles Space key press', async () => {
  const onOptionClick = jest.fn()
  render(<CAutocompleteOptions options={mockOptions} selected={null} onOptionClick={onOptionClick} />)

  const option = screen.getByText('Apple')
  fireEvent.keyDown(option, { code: 'Space' })

  expect(onOptionClick).toHaveBeenCalledWith(mockOptions[0])
})

test('CAutocompleteOptions navigates with arrow keys', async () => {
  render(<CAutocompleteOptions options={mockOptions} selected={null} />)

  const firstOption = screen.getByText('Apple')
  const secondOption = screen.getByText('Banana')

  firstOption.focus()
  fireEvent.keyDown(firstOption, { key: 'ArrowDown' })

  expect(document.activeElement).toBe(secondOption)
})

test('CAutocompleteOptions shows no results message', async () => {
  render(
    <CAutocompleteOptions options={[]} selected={null} searchNoResultsLabel="No options found" />
  )

  expect(screen.getByText('No options found')).toBeVisible()
})

test('CAutocompleteOptions highlights search matches', async () => {
  const { container } = render(
    <CAutocompleteOptions
      options={mockOptions}
      selected={null}
      highlightOptionsOnSearch={true}
      searchValue="app"
    />
  )

  const option = container.querySelector('.autocomplete-option')
  expect(option).toBeInTheDocument()
  expect(option?.querySelector('strong')).toHaveTextContent('App')
})

test('CAutocompleteOptions uses custom option template', async () => {
  const customTemplate = (option: Option) => {
    const { label, value } = option as Exclude<Option, string>
    return (
      <div className="custom-option">
        {label} - {value}
      </div>
    )
  }

  render(
    <CAutocompleteOptions options={mockOptions} selected={null} optionsTemplate={customTemplate} />
  )

  expect(screen.getByText('Apple - 1')).toBeVisible()
  expect(screen.getByText('Banana - 2')).toBeVisible()
})

test('CAutocompleteOptions uses custom group template', async () => {
  const customGroupTemplate = (group: OptionsGroup) => (
    <div className="custom-group">{group.label.toUpperCase()}</div>
  )

  render(
    <CAutocompleteOptions
      options={mockOptionsWithGroups}
      selected={null}
      optionsGroupsTemplate={customGroupTemplate}
    />
  )

  expect(screen.getByText('FRUITS')).toBeVisible()
  expect(screen.getByText('VEGETABLES')).toBeVisible()
})

test('CAutocompleteOptions shows loading state', async () => {
  const { container } = render(
    <CAutocompleteOptions options={mockOptions} selected={null} loading={true} />
  )

  // CElementCover renders a div with position styles when loading
  const options = container.querySelector('.autocomplete-options')
  expect(options).toBeInTheDocument()
  // When loading, the component should render additional cover elements
  expect(options?.children.length).toBeGreaterThan(0)
})

test('CAutocompleteOptions renders with virtual scroller', async () => {
  const { container } = render(
    <CAutocompleteOptions
      options={mockOptions}
      selected={null}
      virtualScroller={true}
      visibleItems={2}
    />
  )

  expect(container.querySelector('.virtual-scroller')).toBeInTheDocument()
})

test('CAutocompleteOptions sets max height', async () => {
  const { container } = render(
    <CAutocompleteOptions options={mockOptions} selected={null} optionsMaxHeight={200} />
  )

  const optionsContainer = container.querySelector('.autocomplete-options')
  expect(optionsContainer).toHaveStyle('max-height: 200px')
})

test('CAutocompleteOptions marks disabled options', async () => {
  const optionsWithDisabled = [
    { label: 'Apple', value: 1 },
    { label: 'Banana', value: 2, disabled: true },
  ]

  render(<CAutocompleteOptions options={optionsWithDisabled} selected={null} />)

  const disabledOption = screen.getByText('Banana').closest('.autocomplete-option')
  expect(disabledOption).toHaveClass('disabled')
})

test('CAutocompleteOptions has correct accessibility attributes', async () => {
  const { container } = render(<CAutocompleteOptions options={mockOptions} selected={null} />)

  const listbox = screen.getByRole('listbox')
  expect(listbox).toBeInTheDocument()

  const options = container.querySelectorAll('.autocomplete-option')
  expect(options).toHaveLength(4)

  options.forEach((option) => {
    expect(option).toHaveAttribute('tabIndex', '0')
  })
})
