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

expect.extend(toHaveNoViolations)

export interface InputWrapperTestConfig extends BaseTestConfig {
  label?: string
  helptext?: string
  errorMessage?: string
  hasError?: boolean
  counter?: boolean
  counterCurrent?: number
  counterMaxLength?: number
  counterError?: boolean
  inline?: boolean
  useWrapper?: boolean
  disabled?: boolean
  optionalTag?: boolean
  requiredTag?: boolean
  hasFieldset?: boolean
  inFieldset?: boolean
  readonly?: boolean
  forId?: string
  ariaDescribedBy?: string
  helptextDropdown?: string
  helptextDropdownButton?: string
  optionalText?: string
  requiredText?: string
  tagText?: string
  role?: string
}

// Use shared framework
export const createInputWrapperTest = async (config: InputWrapperTestConfig = {}) => {
  const { container, element } = await createElementTest<
    CustomElementFor<'pkt-input-wrapper'>,
    InputWrapperTestConfig
  >('pkt-input-wrapper', config)

  return {
    container,
    wrapper: element,
  }
}

describe('PktInputWrapper', () => {
  describe('Rendering and basic functionality', () => {
    test('renders without errors', async () => {
      const { wrapper } = await createInputWrapperTest()

      expect(wrapper).toBeInTheDocument()
      await wrapper.updateComplete
      expect(wrapper).toBeTruthy()
    })

    test('renders with basic structure', async () => {
      const { wrapper } = await createInputWrapperTest({ label: 'Test Label' })
      await wrapper.updateComplete

      expect(wrapper).toBeInTheDocument()
      expect(wrapper.label).toBe('Test Label')
      const label = wrapper.querySelector('label')
      expect(label).toBeInTheDocument()
      expect(label?.textContent).toContain('Test Label')
    })

    test('renders slotted content', async () => {
      const { wrapper } = await createInputWrapperTest({
        label: 'Test Label',
        content: '<input type="text" placeholder="Custom input" />',
      })
      await wrapper.updateComplete

      const input = wrapper.querySelector('input')
      expect(input).toBeInTheDocument()
      expect(input?.placeholder).toBe('Custom input')
    })
  })

  describe('Properties and attributes', () => {
    test('applies default properties correctly', async () => {
      const { wrapper } = await createInputWrapperTest()
      await wrapper.updateComplete

      expect(wrapper.label).toBe('')
      expect(wrapper.hasError).toBe(false)
      expect(wrapper.disabled).toBe(false)
      expect(wrapper.inline).toBe(false)
      expect(wrapper.counter).toBe(false)
      expect(wrapper.optionalTag).toBe(false)
      expect(wrapper.requiredTag).toBe(false)
    })

    test('sets label property correctly', async () => {
      const { wrapper } = await createInputWrapperTest({ label: 'Test Label' })
      await wrapper.updateComplete

      expect(wrapper.label).toBe('Test Label')
    })

    test('sets helptext property correctly', async () => {
      const { wrapper } = await createInputWrapperTest({ helptext: 'This is help text' })
      await wrapper.updateComplete

      expect(wrapper.helptext).toBe('This is help text')
    })

    test('sets error state correctly', async () => {
      const { wrapper } = await createInputWrapperTest({
        hasError: true,
        errorMessage: 'Error message',
      })
      await wrapper.updateComplete

      expect(wrapper.hasError).toBe(true)
      expect(wrapper.errorMessage).toBe('Error message')
    })

    test('sets disabled state correctly', async () => {
      const { wrapper } = await createInputWrapperTest({ disabled: true })
      await wrapper.updateComplete

      expect(wrapper.disabled).toBe(true)
    })

    test('sets inline layout correctly', async () => {
      const { wrapper } = await createInputWrapperTest({ inline: true })
      await wrapper.updateComplete

      expect(wrapper.inline).toBe(true)
    })

    test('sets forId property correctly', async () => {
      const { wrapper } = await createInputWrapperTest({ forId: 'custom-id' })
      await wrapper.updateComplete

      expect(wrapper.forId).toBe('custom-id')
    })
  })

  describe('Tag functionality', () => {
    test('renders optional tag when enabled', async () => {
      const { wrapper } = await createInputWrapperTest({
        label: 'Test Label',
        optionalTag: true,
      })
      await wrapper.updateComplete

      expect(wrapper.optionalTag).toBe(true)
      const tag = wrapper.querySelector('.pkt-tag')
      expect(tag).toBeInTheDocument()
    })

    test('renders required tag when enabled', async () => {
      const { wrapper } = await createInputWrapperTest({
        label: 'Test Label',
        requiredTag: true,
      })
      await wrapper.updateComplete

      expect(wrapper.requiredTag).toBe(true)
      const tag = wrapper.querySelector('.pkt-tag')
      expect(tag).toBeInTheDocument()
    })

    test('renders custom tag text when provided', async () => {
      const { wrapper } = await createInputWrapperTest({
        label: 'Test Label',
        tagText: 'Custom Tag',
      })
      await wrapper.updateComplete

      expect(wrapper.tagText).toBe('Custom Tag')
      const tag = wrapper.querySelector('.pkt-tag')
      expect(tag).toBeInTheDocument()
      expect(tag?.textContent).toContain('Custom Tag')
    })
  })

  describe('Counter functionality', () => {
    test('renders counter when enabled', async () => {
      const { wrapper } = await createInputWrapperTest({
        label: 'Test Label',
        counter: true,
        counterCurrent: 5,
        counterMaxLength: 100,
      })
      await wrapper.updateComplete

      expect(wrapper.counter).toBe(true)
      expect(wrapper.counterCurrent).toBe(5)
      expect(wrapper.counterMaxLength).toBe(100)

      const counter = wrapper.querySelector('.pkt-input__counter')
      expect(counter).toBeInTheDocument()
    })

    test('updates counter current value', async () => {
      const { wrapper } = await createInputWrapperTest({
        label: 'Test Label',
        counter: true,
        counterCurrent: 10,
        counterMaxLength: 100,
      })
      await wrapper.updateComplete

      wrapper.counterCurrent = 25
      await wrapper.updateComplete

      expect(wrapper.counterCurrent).toBe(25)
    })

    test('handles counter error state', async () => {
      const { wrapper } = await createInputWrapperTest({
        label: 'Test Label',
        counter: true,
        counterCurrent: 105,
        counterMaxLength: 100,
      })
      await wrapper.updateComplete

      // Counter should indicate error when current exceeds max
      expect(wrapper.counterCurrent).toBe(105)
      expect(wrapper.counterMaxLength).toBe(100)
    })
  })

  describe('Helptext functionality', () => {
    test('renders dropdown helptext', async () => {
      const { wrapper } = await createInputWrapperTest({
        label: 'Test Label',
        helptextDropdown: 'Dropdown help content',
      })
      await wrapper.updateComplete

      expect(wrapper.helptextDropdown).toBe('Dropdown help content')
      const helptext = wrapper.querySelector('pkt-helptext')
      expect(helptext).toBeInTheDocument()
    })

    test('sets dropdown button text', async () => {
      const { wrapper } = await createInputWrapperTest({
        label: 'Test Label',
        helptextDropdown: 'Dropdown help',
        helptextDropdownButton: 'Custom Button',
      })
      await wrapper.updateComplete

      expect(wrapper.helptextDropdownButton).toBe('Custom Button')
    })

    test('renders helptext from slot', async () => {
      const { wrapper } = await createInputWrapperTest({
        label: 'Test Label',
        content: '<input type="text" /><div slot="helptext">Slotted help content</div>',
      })
      await wrapper.updateComplete

      const helptextEl = wrapper.querySelector('pkt-helptext')
      expect(helptextEl).toBeInTheDocument()
      expect(helptextEl?.textContent).toContain('Slotted help content')
    })
  })

  describe('Error handling', () => {
    test('displays error message when hasError is true', async () => {
      const { wrapper } = await createInputWrapperTest({
        label: 'Test Label',
        hasError: true,
        errorMessage: 'This field is required',
      })
      await wrapper.updateComplete

      expect(wrapper.hasError).toBe(true)
      expect(wrapper.errorMessage).toBe('This field is required')

      const errorMessage = wrapper.querySelector('.pkt-alert--error')
      expect(errorMessage).toBeInTheDocument()
      expect(errorMessage?.textContent).toContain('This field is required')
    })

    test('does not display error message when hasError is false', async () => {
      const { wrapper } = await createInputWrapperTest({ label: 'Test Label' })
      await wrapper.updateComplete

      expect(wrapper.hasError).toBe(false)
      const errorMessage = wrapper.querySelector('.pkt-alert--error')
      expect(errorMessage).not.toBeInTheDocument()
    })

    test('applies error styling when hasError is true', async () => {
      const { wrapper } = await createInputWrapperTest({
        label: 'Test Label',
        hasError: true,
      })
      await wrapper.updateComplete

      expect(wrapper.hasError).toBe(true)
      // Check for error-related classes based on actual component implementation
      const inputWrapper = wrapper.querySelector('.pkt-inputwrapper')
      expect(inputWrapper?.classList.contains('pkt-inputwrapper--error')).toBe(true)
    })
  })

  describe('Fieldset functionality', () => {
    test('handles fieldset mode', async () => {
      const { wrapper } = await createInputWrapperTest({
        label: 'Test Label',
        hasFieldset: true,
      })
      await wrapper.updateComplete

      expect(wrapper.hasFieldset).toBe(true)
    })

    test('sets role attribute correctly', async () => {
      const { wrapper } = await createInputWrapperTest({
        label: 'Test Label',
        role: 'radiogroup',
      })
      await wrapper.updateComplete

      expect(wrapper.role).toBe('radiogroup')
      expect(wrapper.getAttribute('role')).toBe('radiogroup')
    })
  })

  describe('Wrapper functionality', () => {
    test('handles useWrapper property values', async () => {
      // Test true value
      const { wrapper: wrapperTrue } = await createInputWrapperTest({
        label: 'Test Label',
        useWrapper: true,
      })
      await wrapperTrue.updateComplete

      expect(wrapperTrue.useWrapper).toBe(true)
      expect(wrapperTrue.getAttribute('useWrapper')).toBe('true')

      // Test false value explicitly
      const { wrapper: wrapperFalse } = await createInputWrapperTest()
      wrapperFalse.setAttribute('useWrapper', 'false')
      wrapperFalse.label = 'Test Label'
      await wrapperFalse.updateComplete

      expect(wrapperFalse.useWrapper).toBe(false)
      expect(wrapperFalse.getAttribute('useWrapper')).toBe('false')
    })
  })

  describe('ARIA and accessibility attributes', () => {
    test('sets aria-describedby correctly', async () => {
      const { wrapper } = await createInputWrapperTest({
        label: 'Test Label',
        ariaDescribedBy: 'help-text-id',
      })
      await wrapper.updateComplete

      expect(wrapper.ariaDescribedby).toBe('help-text-id')
    })

    test('associates label with input using forId', async () => {
      const { wrapper } = await createInputWrapperTest({
        label: 'Test Label',
        forId: 'test-input',
        content: '<input id="test-input" type="text" />',
      })
      await wrapper.updateComplete

      const label = wrapper.querySelector('label')
      const input = wrapper.querySelector('input')

      expect(label?.getAttribute('for')).toBe('test-input')
      expect(input?.id).toBe('test-input')
    })
  })

  describe('Dynamic updates', () => {
    test('updates label dynamically', async () => {
      const { wrapper } = await createInputWrapperTest({ label: 'Original Label' })
      await wrapper.updateComplete

      wrapper.label = 'Updated Label'
      await wrapper.updateComplete

      expect(wrapper.label).toBe('Updated Label')
      const label = wrapper.querySelector('label')
      expect(label?.textContent).toContain('Updated Label')
    })

    test('updates error state dynamically', async () => {
      const { wrapper } = await createInputWrapperTest({ label: 'Test Label' })
      await wrapper.updateComplete

      wrapper.hasError = true
      wrapper.errorMessage = 'Dynamic error'
      await wrapper.updateComplete

      expect(wrapper.hasError).toBe(true)
      expect(wrapper.errorMessage).toBe('Dynamic error')
    })

    test('updates helptext dynamically', async () => {
      const { wrapper } = await createInputWrapperTest({ label: 'Test Label' })
      await wrapper.updateComplete

      wrapper.helptext = 'Dynamic help text'
      await wrapper.updateComplete

      expect(wrapper.helptext).toBe('Dynamic help text')
    })
  })

  describe('Accessibility', () => {
    test('basic input wrapper is accessible', async () => {
      const { container } = await createInputWrapperTest({
        label: 'Accessible Label',
        forId: 'test-input',
        content: '<input id="test-input" type="text" />',
      })
      await new Promise((resolve) => setTimeout(resolve, 100))

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

    test('input wrapper with helptext is accessible', async () => {
      const { container } = await createInputWrapperTest({
        label: 'Test Label',
        helptext: 'This is helpful information',
        forId: 'test-input-2',
        content: '<input id="test-input-2" type="text" />',
      })
      await new Promise((resolve) => setTimeout(resolve, 100))

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

    test('comprehensive input wrapper accessibility', async () => {
      const { container } = await createInputWrapperTest({
        label: 'Test Label',
        helptext: 'Help text',
        hasError: true,
        errorMessage: 'Error message',
        optionalTag: true,
        forId: 'test-input-comprehensive',
        content: '<input id="test-input-comprehensive" type="text" />',
      })
      await new Promise((resolve) => setTimeout(resolve, 100))

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

  describe('Integration scenarios', () => {
    test('works with complex form elements', async () => {
      const { wrapper } = await createInputWrapperTest({
        label: 'Complex Field',
        helptext: 'Help text',
        counter: true,
        counterMaxLength: 100,
        optionalTag: true,
        content: '<textarea></textarea>',
      })
      await wrapper.updateComplete

      expect(wrapper.label).toBe('Complex Field')
      expect(wrapper.helptext).toBe('Help text')
      expect(wrapper.counter).toBe(true)
      expect(wrapper.optionalTag).toBe(true)

      const textarea = wrapper.querySelector('textarea')
      expect(textarea).toBeInTheDocument()
    })

    test('handles multiple input wrappers on same page', async () => {
      const { container } = await createInputWrapperTest({
        label: 'Field 1',
        content: '<input type="text" />',
      })

      // Add additional wrappers to the container
      container.innerHTML += `
        <pkt-input-wrapper label="Field 2"><input type="email" /></pkt-input-wrapper>
        <pkt-input-wrapper label="Field 3"><textarea></textarea></pkt-input-wrapper>
      `

      // Wait for all components to be defined
      await customElements.whenDefined('pkt-input-wrapper')

      const wrappers = container.querySelectorAll('pkt-input-wrapper')
      expect(wrappers).toHaveLength(3)

      for (const wrapper of wrappers) {
        await (wrapper as any).updateComplete
        expect(wrapper.querySelector('label')).toBeInTheDocument()
      }
    })
  })
})
