import '@testing-library/jest-dom'
import { axe, toHaveNoViolations } from 'jest-axe'
import { fireEvent } from '@testing-library/dom'
import { vi } from 'vitest'
import { createElementTest, BaseTestConfig } from '../../tests/test-framework'
import { CustomElementFor } from '../../tests/component-registry'
import './radiobutton'

export interface RadioButtonTestConfig extends BaseTestConfig {
  value?: string
  checkHelptext?: string | null
  defaultChecked?: boolean
  hasTile?: boolean
  checked?: boolean | string | null
  type?: string
  tagText?: string | null
  optionalTag?: boolean
  optionalText?: string
  requiredTag?: boolean
  requiredText?: string

  id?: string
  name?: string
  label?: string
  disabled?: boolean
  readonly?: boolean
  required?: boolean
  hasError?: boolean
  inline?: boolean
  ariaDescribedBy?: string | null
  ariaLabelledby?: string | null
}

export const createRadioButtonTest = async (config: RadioButtonTestConfig = {}) => {
  const { container, element } = await createElementTest<
    CustomElementFor<'pkt-radiobutton'>,
    RadioButtonTestConfig
  >('pkt-radiobutton', config)

  if (config.label) {
    element.label = config.label
    await element.updateComplete
  }

  return {
    container,
    radiobutton: element,
  }
}

expect.extend(toHaveNoViolations)

describe('pkt-radiobutton', () => {
  afterEach(() => {
    document.body.innerHTML = ''
  })

  test('renders with basic properties', async () => {
    const { radiobutton } = await createRadioButtonTest({
      id: 'test',
      name: 'test',
      value: 'test',
    })

    expect(radiobutton).toBeInTheDocument()
    expect(radiobutton.tagName).toBe('PKT-RADIOBUTTON')
  })

  test('renders input element with correct attributes', async () => {
    const { radiobutton } = await createRadioButtonTest({
      id: 'test',
      name: 'test',
      value: 'test',
    })

    const input = radiobutton.querySelector('input[type="radio"]') as HTMLInputElement
    expect(input).toBeInTheDocument()
    expect(input.type).toBe('radio')
    expect(input.id).toBe('test-internal')
    expect(input.name).toBe('test-internal')
  })

  test('displays label correctly', async () => {
    const { radiobutton } = await createRadioButtonTest({
      id: 'test',
      label: 'Test Radio',
    })

    const label = radiobutton.querySelector('label')
    expect(label).toBeInTheDocument()
    expect(label?.textContent?.trim()).toContain('Test Radio')
    expect(label?.getAttribute('for')).toBe('test-internal')
  })

  test('handles checked state correctly', async () => {
    const { radiobutton } = await createRadioButtonTest()

    expect(radiobutton.checked).toBe(null)

    radiobutton.checked = true
    await radiobutton.updateComplete

    const input = radiobutton.querySelector('input') as HTMLInputElement
    expect(input.checked).toBe(true)
    expect(radiobutton.checked).toBe(true)
  })

  test('updates checked state on user interaction', async () => {
    const { radiobutton } = await createRadioButtonTest({
      label: 'Test Option',
    })

    const input = radiobutton.querySelector('input') as HTMLInputElement
    expect(input.checked).toBe(false)
    expect(radiobutton.touched).toBe(false)

    fireEvent.click(input)
    await radiobutton.updateComplete

    expect(input.checked).toBe(true)
    expect(radiobutton.touched).toBe(true)
  })

  test('handles disabled state correctly', async () => {
    const { radiobutton } = await createRadioButtonTest({
      disabled: true,
    })

    const input = radiobutton.querySelector('input') as HTMLInputElement
    expect(input.disabled).toBe(true)

    const label = radiobutton.querySelector('.pkt-input-check__input-label')
    expect(label).toHaveClass('pkt-input-check__input-label--disabled')
  })

  test('renders tile variant correctly', async () => {
    const { radiobutton } = await createRadioButtonTest({
      hasTile: true,
    })

    const container = radiobutton.querySelector('.pkt-input-check__input')
    expect(container).toHaveClass('pkt-input-check__input--tile')
  })

  test('renders disabled tile variant correctly', async () => {
    const { radiobutton } = await createRadioButtonTest({
      hasTile: true,
      disabled: true,
    })

    const container = radiobutton.querySelector('.pkt-input-check__input')
    expect(container).toHaveClass('pkt-input-check__input--tile-disabled')
  })

  test('renders helptext when provided', async () => {
    const { radiobutton } = await createRadioButtonTest({
      checkHelptext: 'This is helpful information',
    })

    const helptext = radiobutton.querySelector('.pkt-input-check__input-helptext')
    expect(helptext).toBeInTheDocument()
    expect(helptext?.textContent).toBe('This is helpful information')
  })

  test('handles error state correctly', async () => {
    const { radiobutton } = await createRadioButtonTest({
      hasError: true,
    })

    const input = radiobutton.querySelector('input')
    expect(input).toHaveClass('pkt-input-check__input-checkbox--error')
  })

  test('renders tag text when provided', async () => {
    const { radiobutton } = await createRadioButtonTest({
      tagText: 'Tag',
    })

    const tag = radiobutton.querySelector('.pkt-tag--gray')
    expect(tag).toBeInTheDocument()
    expect(tag?.textContent).toBe('Tag')
  })

  test('renders optional tag when enabled', async () => {
    const { radiobutton } = await createRadioButtonTest({
      optionalTag: true,
    })

    const tag = radiobutton.querySelector('.pkt-tag--blue-light')
    expect(tag).toBeInTheDocument()
    expect(tag?.textContent).toBe('Valgfritt')
  })

  test('renders required tag when enabled', async () => {
    const { radiobutton } = await createRadioButtonTest({
      requiredTag: true,
    })

    const tag = radiobutton.querySelector('.pkt-tag--beige')
    expect(tag).toBeInTheDocument()
    expect(tag?.textContent).toBe('Må fylles ut')
  })

  test('manages focus events correctly', async () => {
    const { radiobutton } = await createRadioButtonTest()

    const focusSpy = vi.fn()
    const blurSpy = vi.fn()
    radiobutton.addEventListener('focus', focusSpy)
    radiobutton.addEventListener('blur', blurSpy)

    const input = radiobutton.querySelector('input') as HTMLInputElement

    fireEvent.focus(input)
    expect(focusSpy).toHaveBeenCalled()

    fireEvent.blur(input)
    expect(blurSpy).toHaveBeenCalled()
  })

  test('works with radio button groups', async () => {
    const container = document.createElement('form')
    document.body.appendChild(container)

    const radio1Container = document.createElement('div')
    radio1Container.innerHTML = '<pkt-radiobutton name="group" value="1"></pkt-radiobutton>'
    container.appendChild(radio1Container)
    await customElements.whenDefined('pkt-radiobutton')
    const radio1 = radio1Container.querySelector(
      'pkt-radiobutton',
    ) as CustomElementFor<'pkt-radiobutton'>
    radio1.label = 'Option 1'
    await radio1.updateComplete

    const radio2Container = document.createElement('div')
    radio2Container.innerHTML = '<pkt-radiobutton name="group" value="2"></pkt-radiobutton>'
    container.appendChild(radio2Container)
    const radio2 = radio2Container.querySelector(
      'pkt-radiobutton',
    ) as CustomElementFor<'pkt-radiobutton'>
    radio2.label = 'Option 2'
    await radio2.updateComplete

    const input1 = radio1.querySelector('input') as HTMLInputElement
    const input2 = radio2.querySelector('input') as HTMLInputElement

    expect(input1.checked).toBe(false)
    expect(input2.checked).toBe(false)

    fireEvent.click(input1)
    await radio1.updateComplete

    expect(input1.checked).toBe(true)
    expect(radio1.touched).toBe(true)

    fireEvent.click(input2)
    await radio2.updateComplete

    expect(input2.checked).toBe(true)
    expect(radio2.touched).toBe(true)

    document.body.removeChild(container)
  })

  test('meets accessibility standards', async () => {
    const { radiobutton } = await createRadioButtonTest({
      label: 'Accessible Radio Button',
    })

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