import '@testing-library/jest-dom'
import { axe, toHaveNoViolations } from 'jest-axe'
import { fireEvent } from '@testing-library/dom'

expect.extend(toHaveNoViolations)

import './combobox'
import { PktCombobox } from './combobox'
import type { IPktComboboxOption } from './combobox'

const waitForCustomElements = async () => {
  await customElements.whenDefined('pkt-combobox')
}

const createCombobox = async (comboboxProps = '') => {
  const container = document.createElement('div')
  container.innerHTML = `
    <pkt-combobox ${comboboxProps}></pkt-combobox>
  `
  document.body.appendChild(container)
  await waitForCustomElements()
  return container
}

const defaultOptions: IPktComboboxOption[] = [
  { value: 'apple', label: 'Apple' },
  { value: 'banana', label: 'Banana' },
  { value: 'cherry', label: 'Cherry' },
]

afterEach(() => {
  document.body.innerHTML = ''
})

describe('PktCombobox', () => {
  describe('Accessibility (axe)', () => {
    test('basic combobox has no accessibility violations', async () => {
      const container = await createCombobox('id="accessible" name="test" label="Choose a fruit"')
      const combobox = container.querySelector('pkt-combobox') as PktCombobox
      await combobox.updateComplete

      const results = await axe(combobox)
      expect(results).toHaveNoViolations()
    })

    test('combobox with options has no accessibility violations', async () => {
      const container = await createCombobox('id="accessible" name="test" label="Choose a fruit"')
      const combobox = container.querySelector('pkt-combobox') as PktCombobox
      combobox.options = [...defaultOptions]
      await combobox.updateComplete

      const results = await axe(combobox)
      expect(results).toHaveNoViolations()
    })

    test('combobox with text input has no accessibility violations', async () => {
      const container = await createCombobox(
        'id="accessible" name="test" label="Choose a fruit" allow-user-input',
      )
      const combobox = container.querySelector('pkt-combobox') as PktCombobox
      combobox.options = [...defaultOptions]
      await combobox.updateComplete

      const results = await axe(combobox)
      expect(results).toHaveNoViolations()
    })

    test('combobox with typeahead has no accessibility violations', async () => {
      const container = await createCombobox(
        'id="accessible" name="test" label="Choose a fruit" typeahead',
      )
      const combobox = container.querySelector('pkt-combobox') as PktCombobox
      combobox.options = [...defaultOptions]
      await combobox.updateComplete

      const results = await axe(combobox)
      expect(results).toHaveNoViolations()
    })

    test('multiple combobox has no accessibility violations', async () => {
      const container = await createCombobox(
        'id="accessible" name="test" label="Choose fruits" multiple',
      )
      const combobox = container.querySelector('pkt-combobox') as PktCombobox
      combobox.options = [...defaultOptions]
      await combobox.updateComplete

      const results = await axe(combobox)
      expect(results).toHaveNoViolations()
    })

    test('disabled combobox has no accessibility violations', async () => {
      const container = await createCombobox(
        'id="accessible" name="test" label="Choose a fruit" disabled',
      )
      const combobox = container.querySelector('pkt-combobox') as PktCombobox
      await combobox.updateComplete

      const results = await axe(combobox)
      expect(results).toHaveNoViolations()
    })

    test('combobox with error state has no accessibility violations', async () => {
      const container = await createCombobox(
        'id="accessible" name="test" label="Choose a fruit" error-message="Required field"',
      )
      const combobox = container.querySelector('pkt-combobox') as PktCombobox
      combobox.hasError = true
      await combobox.updateComplete

      const results = await axe(combobox)
      expect(results).toHaveNoViolations()
    })
  })

  describe('ARIA attributes', () => {
    test('select-only combobox has correct ARIA attributes', async () => {
      const container = await createCombobox('id="test-aria" name="test" label="Test Label"')
      const combobox = container.querySelector('pkt-combobox') as PktCombobox
      await combobox.updateComplete

      const comboboxInput = combobox.querySelector('.pkt-combobox__input')
      expect(comboboxInput?.getAttribute('role')).toBe('combobox')
      expect(comboboxInput?.getAttribute('aria-controls')).toBe('test-aria-listbox')
      expect(comboboxInput?.getAttribute('aria-haspopup')).toBe('listbox')
      expect(comboboxInput?.getAttribute('aria-expanded')).toBe('false')
      expect(comboboxInput?.getAttribute('aria-labelledby')).toBe('test-aria-combobox-label')
    })

    test('arrow button aria-expanded updates when dropdown opens', async () => {
      const container = await createCombobox('id="test" name="test" label="Test"')
      const combobox = container.querySelector('pkt-combobox') as PktCombobox
      await combobox.updateComplete

      const arrowButton = combobox.querySelector('.pkt-combobox__input')
      expect(arrowButton?.getAttribute('aria-expanded')).toBe('false')

      fireEvent.click(arrowButton!)
      await combobox.updateComplete

      expect(arrowButton?.getAttribute('aria-expanded')).toBe('true')
    })

    test('text input has correct ARIA attributes for allowUserInput', async () => {
      const container = await createCombobox(
        'id="test-input" name="test" label="Test Label" allow-user-input',
      )
      const combobox = container.querySelector('pkt-combobox') as PktCombobox
      await combobox.updateComplete

      const textInput = combobox.querySelector('input[type="text"]')
      expect(textInput?.getAttribute('role')).toBe('combobox')
      expect(textInput?.getAttribute('aria-controls')).toBe('test-input-listbox')
      expect(textInput?.getAttribute('aria-label')).toBe('Test Label')
      expect(textInput?.getAttribute('aria-autocomplete')).toBe('list')
    })

    test('text input has correct ARIA attributes for typeahead', async () => {
      const container = await createCombobox(
        'id="test-input" name="test" label="Test Label" typeahead',
      )
      const combobox = container.querySelector('pkt-combobox') as PktCombobox
      await combobox.updateComplete

      const textInput = combobox.querySelector('input[type="text"]')
      expect(textInput?.getAttribute('aria-autocomplete')).toBe('both')
    })

    test('text input sets aria-activedescendant when value is selected', async () => {
      const container = await createCombobox(
        'id="test" name="test" label="Test" allow-user-input value="apple"',
      )
      const combobox = container.querySelector('pkt-combobox') as PktCombobox
      combobox.options = [...defaultOptions]
      await combobox.updateComplete

      const textInput = combobox.querySelector('input[type="text"]')
      expect(textInput?.getAttribute('aria-activedescendant')).toBeTruthy()
    })

    test('listbox has correct id for aria-controls reference', async () => {
      const container = await createCombobox('id="test-combo" name="test" label="Test"')
      const combobox = container.querySelector('pkt-combobox') as PktCombobox
      await combobox.updateComplete

      const listbox = combobox.querySelector('pkt-listbox')
      expect(listbox?.getAttribute('id')).toBe('test-combo-listbox')
    })
  })

  describe('Keyboard accessibility', () => {
    test('arrow button is focusable when not disabled', async () => {
      const container = await createCombobox('id="test" name="test" label="Test"')
      const combobox = container.querySelector('pkt-combobox') as PktCombobox
      await combobox.updateComplete

      const arrowButton = combobox.querySelector('.pkt-combobox__input') as HTMLElement
      expect(arrowButton.getAttribute('tabindex')).toBe('0')
    })

    test('arrow button is not focusable when disabled', async () => {
      const container = await createCombobox('id="test" name="test" label="Test" disabled')
      const combobox = container.querySelector('pkt-combobox') as PktCombobox
      await combobox.updateComplete

      const arrowButton = combobox.querySelector('.pkt-combobox__input') as HTMLElement
      expect(arrowButton.getAttribute('tabindex')).toBe('-1')
    })

    test('text input is part of tab order', async () => {
      const container = await createCombobox('id="test" name="test" label="Test" allow-user-input')
      const combobox = container.querySelector('pkt-combobox') as PktCombobox
      await combobox.updateComplete

      const textInput = combobox.querySelector('input[type="text"]') as HTMLElement
      expect(textInput).toBeInTheDocument()
      // Text inputs are naturally tabbable (no tabindex needed)
      expect(textInput.hasAttribute('tabindex')).toBe(false)
    })
  })

  describe('Label association', () => {
    test('input wrapper has correct forId for text input', async () => {
      const container = await createCombobox(
        'id="test-id" name="test" label="Test Label" allow-user-input',
      )
      const combobox = container.querySelector('pkt-combobox') as PktCombobox
      await combobox.updateComplete

      const wrapper = combobox.querySelector('pkt-input-wrapper') as any
      expect(wrapper?.forId).toBe('test-id-input')
    })

    test('input wrapper has correct forId for combobox (no text input)', async () => {
      const container = await createCombobox('id="test-id" name="test" label="Test Label"')
      const combobox = container.querySelector('pkt-combobox') as PktCombobox
      await combobox.updateComplete

      const wrapper = combobox.querySelector('pkt-input-wrapper') as any
      expect(wrapper?.forId).toBe('test-id-combobox')
    })
  })
})
