import * as React from 'react'
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import '@testing-library/jest-dom'
import { CAutocomplete } from '../CAutocomplete'

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 CAutocomplete component', async () => {
  const { container } = render(<CAutocomplete options={mockOptions} />)
  expect(container).toMatchSnapshot()
})

test('CAutocomplete customize', async () => {
  const { container } = render(
    <CAutocomplete
      className="bazinga"
      disabled={true}
      size="lg"
      placeholder="Select option"
      options={mockOptions}
      cleaner={true}
      indicator={true}
    />
  )
  expect(container).toMatchSnapshot()
  expect(container.querySelector('.autocomplete')).toHaveClass('bazinga')
  expect(container.querySelector('.autocomplete')).toHaveClass('autocomplete-lg')
  expect(container.querySelector('.autocomplete')).toHaveClass('disabled')
})

test('CAutocomplete renders input with placeholder', async () => {
  render(<CAutocomplete options={mockOptions} placeholder="Type to search..." />)
  const input = screen.getByRole('combobox')
  expect(input).toHaveAttribute('placeholder', 'Type to search...')
})

test('CAutocomplete opens dropdown on input click', async () => {
  const { container } = render(<CAutocomplete options={mockOptions} />)
  const input = screen.getByRole('combobox')

  fireEvent.click(input)

  await waitFor(() => {
    const dropdown = container.querySelector('.autocomplete-dropdown')
    expect(dropdown).toBeVisible()
  })
})

test('CAutocomplete filters options on input change', async () => {
  render(<CAutocomplete options={mockOptions} />)
  const input = screen.getByRole('combobox')

  fireEvent.change(input, { target: { value: 'app' } })

  await waitFor(() => {
    const option = screen.getByText('Apple')
    expect(option).toBeVisible()
    expect(screen.queryByText('Banana')).not.toBeInTheDocument()
  })
})

test('CAutocomplete selects option on click', async () => {
  const onChange = jest.fn()
  render(<CAutocomplete options={mockOptions} onChange={onChange} />)

  const input = screen.getByRole('combobox')
  fireEvent.click(input)

  await waitFor(() => {
    const option = screen.getByText('Apple')
    fireEvent.click(option)
    expect(onChange).toHaveBeenCalledWith(mockOptions[0])
  })
})

test('CAutocomplete selects option on Enter key', async () => {
  const onChange = jest.fn()
  render(<CAutocomplete options={mockOptions} onChange={onChange} />)

  const input = screen.getByRole('combobox')
  fireEvent.change(input, { target: { value: 'Apple' } })
  fireEvent.keyDown(input, { key: 'Enter' })

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

test('CAutocomplete closes dropdown on Escape key', async () => {
  const { container } = render(<CAutocomplete options={mockOptions} />)
  const input = screen.getByRole('combobox')

  fireEvent.click(input)

  await waitFor(() => {
    expect(container.querySelector('.autocomplete-dropdown')).toBeVisible()
  })

  fireEvent.keyDown(input, { key: 'Escape' })

  await waitFor(() => {
    expect(input).toHaveAttribute('aria-expanded', 'false')
  })
})

test('CAutocomplete navigates options with arrow keys', async () => {
  const { container } = render(<CAutocomplete options={mockOptions} />)
  const input = screen.getByRole('combobox')

  fireEvent.click(input)

  await waitFor(() => {
    expect(container.querySelector('.autocomplete-dropdown')).toBeVisible()
  })

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

  await waitFor(() => {
    const options = container.querySelectorAll('.autocomplete-option')
    expect(options.length).toBeGreaterThan(0)
  })
})

test('CAutocomplete shows cleaner button when option selected', async () => {
  render(<CAutocomplete options={mockOptions} cleaner={true} value={1} />)

  const cleanerButton = screen.getByRole('button', { name: '' })
  expect(cleanerButton).toHaveClass('autocomplete-cleaner')
})

test('CAutocomplete clears selection when cleaner clicked', async () => {
  const onChange = jest.fn()
  render(<CAutocomplete options={mockOptions} cleaner={true} value={1} onChange={onChange} />)

  const cleanerButton = screen.getByRole('button', { name: '' })
  fireEvent.click(cleanerButton)

  expect(onChange).toHaveBeenCalledWith(null)
})

test('CAutocomplete shows no results message', async () => {
  render(<CAutocomplete options={mockOptions} searchNoResultsLabel="No options found" />)

  const input = screen.getByRole('combobox')
  fireEvent.change(input, { target: { value: 'xyz' } })

  await waitFor(() => {
    expect(screen.getByText('No options found')).toBeVisible()
  })
})

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

  const input = screen.getByRole('combobox')
  fireEvent.click(input)

  await waitFor(() => {
    expect(screen.getByText('Fruits')).toBeVisible()
    expect(screen.getByText('Vegetables')).toBeVisible()
    expect(screen.getByText('Apple')).toBeVisible()
    expect(screen.getByText('Carrot')).toBeVisible()
  })
})

test('CAutocomplete shows hint when enabled', async () => {
  const { container } = render(<CAutocomplete options={mockOptions} showHints={true} />)

  const input = screen.getByRole('combobox')
  fireEvent.change(input, { target: { value: 'ap' } })

  await waitFor(() => {
    const hintInput = container.querySelector('.autocomplete-input-hint')
    expect(hintInput).toBeInTheDocument()
  })
})

test('CAutocomplete allows custom values when allowOnlyDefinedOptions is false', async () => {
  const onChange = jest.fn()
  render(
    <CAutocomplete options={mockOptions} onChange={onChange} allowOnlyDefinedOptions={false} />
  )

  const input = screen.getByRole('combobox')
  fireEvent.change(input, { target: { value: 'Custom Value' } })
  fireEvent.keyDown(input, { key: 'Enter' })

  expect(onChange).toHaveBeenCalledWith('Custom Value')
})

test('CAutocomplete prevents custom values when allowOnlyDefinedOptions is true', async () => {
  const onChange = jest.fn()
  render(<CAutocomplete options={mockOptions} onChange={onChange} allowOnlyDefinedOptions={true} />)

  const input = screen.getByRole('combobox')
  fireEvent.change(input, { target: { value: 'Custom Value' } })
  fireEvent.keyDown(input, { key: 'Enter' })

  expect(onChange).not.toHaveBeenCalled()
})

test('CAutocomplete highlights search matches', async () => {
  const { container } = render(<CAutocomplete options={mockOptions} highlightOptionsOnSearch={true} />)

  const input = screen.getByRole('combobox')
  fireEvent.change(input, { target: { value: 'app' } })

  await waitFor(() => {
    const option = container.querySelector('.autocomplete-option')
    expect(option).toBeInTheDocument()
  })
})

test('CAutocomplete is disabled when disabled prop is true', async () => {
  render(<CAutocomplete options={mockOptions} disabled={true} />)

  const input = screen.getByRole('combobox')
  expect(input).toBeDisabled()
})

test('CAutocomplete is readonly when readOnly prop is true', async () => {
  render(<CAutocomplete options={mockOptions} readOnly={true} />)

  const input = screen.getByRole('combobox')
  expect(input).toHaveAttribute('readonly')
})
