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

expect.extend(toHaveNoViolations)

export interface CheckboxTestConfig extends BaseTestConfig {
  id?: string
  name?: string
  label?: string
  checked?: boolean
  disabled?: boolean
  required?: boolean
  value?: string
  checkHelptext?: string
  hasTile?: boolean
  hasError?: boolean
  errorMessage?: string
  optionalTag?: boolean
  requiredTag?: boolean
  tagText?: string
  labelPosition?: string
  hideLabel?: boolean
  isSwitch?: boolean
}

// Use shared framework
export const createCheckboxTest = async (config: CheckboxTestConfig = {}) => {
  const { container, element } = await createElementTest<
    CustomElementFor<'pkt-checkbox'>,
    CheckboxTestConfig
  >('pkt-checkbox', config)

  return {
    container,
    checkbox: element,
  }
}

describe('PktCheckbox', () => {
  describe('Rendering and basic functionality', () => {
    test('renders without errors', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test-checkbox',
        name: 'test',
        label: 'Test Checkbox',
      })

      expect(checkbox).toBeInTheDocument()
      await checkbox.updateComplete
      expect(checkbox).toBeTruthy()

      const inputElement = checkbox.querySelector('input[type="checkbox"]')
      expect(inputElement).toBeInTheDocument()
    })

    test('renders proper structure', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        label: 'Test Label',
      })
      await checkbox.updateComplete

      expect(checkbox).toBeInTheDocument()

      // Check basic structure exists
      const input = checkbox.querySelector('input[type="checkbox"]')
      const label = checkbox.querySelector('label')

      expect(input).toBeInTheDocument()
      expect(label).toBeInTheDocument()
      expect(label).toHaveTextContent('Test Label')
    })

    test('applies ids and names correctly', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test-id',
        name: 'test-name',
        value: 'test-value',
      })
      await checkbox.updateComplete

      expect(checkbox.id).toBe('test-id')
      expect(checkbox.name).toBe('test-name')
      expect(checkbox.value).toBe('test-value')
    })

    test('renders input with correct attributes', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test-id',
        name: 'test-name',
        value: 'test-value',
      })
      await checkbox.updateComplete

      const input = checkbox.querySelector('input[type="checkbox"]')
      expect(input?.getAttribute('id')).toBe('test-id-internal')
      expect(input?.getAttribute('name')).toBe('test-name-internal')
      expect(input?.getAttribute('type')).toBe('checkbox')
    })
  })

  describe('Properties and attributes', () => {
    test('applies default properties correctly', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
      })
      await checkbox.updateComplete

      expect(checkbox.value).toBe('')
      expect(checkbox.checked).toBe(false)
      expect(checkbox.defaultChecked).toBe(false)
      expect(checkbox.hasTile).toBe(false)
      expect(checkbox.isSwitch).toBe(false)
      expect(checkbox.labelPosition).toBe('right')
      expect(checkbox.hideLabel).toBe(false)
      expect(checkbox.disabled).toBe(false)
      expect(checkbox.optionalTag).toBe(false)
      expect(checkbox.requiredTag).toBe(false)

      const input = checkbox.querySelector('input[type="checkbox"]')
      expect(input?.getAttribute('role')).toBe('checkbox')
    })

    test('handles value property correctly', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        value: 'test-value',
      })

      expect(checkbox.value).toBe('test-value')
      expect(checkbox.getAttribute('value')).toBe('test-value')

      // Test value updates
      checkbox.value = 'updated-value'
      await checkbox.updateComplete

      expect(checkbox.value).toBe('updated-value')
    })

    test('handles checked state correctly', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        checked: true,
      })
      await checkbox.updateComplete

      expect(checkbox.checked).toBe(true)

      const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
      expect(input?.checked).toBe(true)
    })

    test('handles checked property changes', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
      })
      await checkbox.updateComplete

      expect(checkbox.checked).toBe(false)

      checkbox.checked = true
      await checkbox.updateComplete

      expect(checkbox.checked).toBe(true)
      const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
      expect(input?.checked).toBe(true)
    })

    test('handles disabled state correctly', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        disabled: true,
      })
      await checkbox.updateComplete

      expect(checkbox.disabled).toBe(true)

      const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
      expect(input.disabled).toBe(true)
    })

    test('handles labelPosition property correctly', async () => {
      // Test left position
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        label: 'Test',
      })
      checkbox.labelPosition = 'left'
      await checkbox.updateComplete

      expect(checkbox.labelPosition).toBe('left')

      let label = checkbox.querySelector('.pkt-input-check__input-label')
      expect(label).toHaveClass('pkt-input-check__input-label--left')

      // Test right position (default)
      checkbox.labelPosition = 'right'
      await checkbox.updateComplete

      // Re-query the label element after DOM change
      label = checkbox.querySelector('.pkt-input-check__input-label')
      expect(label).toHaveClass('pkt-input-check__input-label--right')
      expect(label).not.toHaveClass('pkt-input-check__input-label--left')
    })

    test('handles hideLabel property correctly', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        label: 'Hidden Label',
      })
      checkbox.hideLabel = true
      await checkbox.updateComplete

      expect(checkbox.hideLabel).toBe(true)
      const label = checkbox.querySelector('.pkt-input-check__input-label')
      expect(label).toHaveClass('pkt-sr-only')
    })

    test('handles isSwitch property correctly', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        isSwitch: true,
      })

      expect(checkbox.isSwitch).toBe(true)

      const input = checkbox.querySelector('input[type="checkbox"]')
      expect(input?.getAttribute('role')).toBe('switch')
    })

    test('handles indeterminate property correctly', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        label: 'Test',
      })
      await checkbox.updateComplete

      const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
      expect(input.indeterminate).toBe(false)

      // Set indeterminate to true
      checkbox.indeterminate = true
      await checkbox.updateComplete

      expect(checkbox.indeterminate).toBe(true)
      expect(input.indeterminate).toBe(true)

      // Set indeterminate back to false
      checkbox.indeterminate = false
      await checkbox.updateComplete

      expect(checkbox.indeterminate).toBe(false)
      expect(input.indeterminate).toBe(false)
    })

    test('handles indeterminate attribute changes', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        label: 'Test',
      })
      await checkbox.updateComplete

      const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
      expect(input.indeterminate).toBe(false)

      // Set via attribute
      checkbox.setAttribute('indeterminate', '')
      await checkbox.updateComplete

      expect(checkbox.indeterminate).toBe(true)
      expect(input.indeterminate).toBe(true)

      // Remove attribute
      checkbox.removeAttribute('indeterminate')
      await checkbox.updateComplete

      expect(checkbox.indeterminate).toBe(false)
      expect(input.indeterminate).toBe(false)
    })

    test('clicking checkbox clears indeterminate state', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        label: 'Test',
      })
      checkbox.indeterminate = true
      await checkbox.updateComplete

      const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
      expect(input.indeterminate).toBe(true)

      // Click the checkbox
      fireEvent.click(input)
      await checkbox.updateComplete

      // Indeterminate should be cleared by the browser when clicked
      expect(input.indeterminate).toBe(false)
      expect(input.checked).toBe(true)
    })

    test('handles hasTile property correctly', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        hasTile: true,
      })

      expect(checkbox.hasTile).toBe(true)

      const inputDiv = checkbox.querySelector('.pkt-input-check__input')
      expect(inputDiv).toHaveClass('pkt-input-check__input--tile')

      // Test disabled with tile
      checkbox.disabled = true
      await checkbox.updateComplete

      expect(inputDiv).toHaveClass('pkt-input-check__input--tile-disabled')
    })
  })

  describe('Label and helptext functionality', () => {
    test('renders label correctly', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        label: 'Checkbox Label',
      })
      await checkbox.updateComplete

      expect(checkbox.label).toBe('Checkbox Label')

      const label = checkbox.querySelector('.pkt-input-check__input-label')
      expect(label?.textContent?.trim()).toBe('Checkbox Label')
      expect(label?.getAttribute('for')).toBe('test-internal')
    })

    test('renders checkHelptext when provided', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        label: 'Test',
        checkHelptext: 'This is help text',
      })
      await checkbox.updateComplete

      expect(checkbox.checkHelptext).toBe('This is help text')

      const helptext = checkbox.querySelector('.pkt-input-check__input-helptext')
      expect(helptext).toBeInTheDocument()
      expect(helptext?.textContent).toBe('This is help text')
    })

    test('does not render helptext when not provided', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        label: 'Test',
      })
      await checkbox.updateComplete

      const helptext = checkbox.querySelector('.pkt-input-check__input-helptext')
      expect(helptext).not.toBeInTheDocument()
    })
  })

  describe('Tag functionality', () => {
    test('renders custom tagText when provided', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        label: 'Test',
        tagText: 'Custom Tag',
      })
      await checkbox.updateComplete

      expect(checkbox.tagText).toBe('Custom Tag')

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

    test('renders optional tag when optionalTag is true', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        label: 'Test',
        optionalTag: true,
      })
      await checkbox.updateComplete

      expect(checkbox.optionalTag).toBe(true)
      expect(checkbox.optionalText).toBe('Valgfritt')

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

    test('renders required tag when requiredTag is true', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        label: 'Test',
        requiredTag: true,
      })
      await checkbox.updateComplete

      expect(checkbox.requiredTag).toBe(true)
      expect(checkbox.requiredText).toBe('Må fylles ut')

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

    test('renders custom optional and required text', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        label: 'Test',
      })
      checkbox.optionalTag = true
      checkbox.optionalText = 'Custom Optional'
      checkbox.requiredTag = true
      checkbox.requiredText = 'Custom Required'
      await checkbox.updateComplete

      expect(checkbox.optionalText).toBe('Custom Optional')
      expect(checkbox.requiredText).toBe('Custom Required')

      const optionalTag = checkbox.querySelector('.pkt-tag--blue-light')
      const requiredTag = checkbox.querySelector('.pkt-tag--beige')
      expect(optionalTag?.textContent).toBe('Custom Optional')
      expect(requiredTag?.textContent).toBe('Custom Required')
    })

    test('renders multiple tags when multiple are enabled', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        label: 'Test',
      })
      checkbox.tagText = 'Custom'
      checkbox.optionalTag = true
      checkbox.requiredTag = true
      await checkbox.updateComplete

      const customTag = checkbox.querySelector('.pkt-tag--gray')
      const optionalTag = checkbox.querySelector('.pkt-tag--blue-light')
      const requiredTag = checkbox.querySelector('.pkt-tag--beige')

      expect(customTag).toBeInTheDocument()
      expect(optionalTag).toBeInTheDocument()
      expect(requiredTag).toBeInTheDocument()
    })
  })

  describe('User interaction', () => {
    test('toggles checked state when clicked', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        label: 'Test',
      })
      await checkbox.updateComplete

      const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
      expect(input.checked).toBe(false)

      // Click to check
      fireEvent.click(input)
      await checkbox.updateComplete

      expect(input.checked).toBe(true)
      expect(checkbox.checked).toBe(true)

      // Click to uncheck
      fireEvent.click(input)
      await checkbox.updateComplete

      expect(input.checked).toBe(false)
      expect(checkbox.checked).toBe(false)
    })

    test('does not toggle when disabled', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        label: 'Test',
        disabled: true,
      })
      await checkbox.updateComplete

      const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
      expect(input.checked).toBe(false)
      expect(input.disabled).toBe(true)

      // Try to click
      fireEvent.click(input)
      await checkbox.updateComplete

      // Should remain unchecked
      expect(input.checked).toBe(false)
      expect(checkbox.checked).toBe(false)
    })

    test('handles focus and blur events', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        label: 'Test',
      })
      await checkbox.updateComplete

      const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement

      // Test focus
      fireEvent.focus(input)
      await checkbox.updateComplete

      // Test blur
      fireEvent.blur(input)
      await checkbox.updateComplete

      // These should not throw errors and the element should remain functional
      expect(checkbox).toBeInTheDocument()
    })

    test('marks as touched when interacted with', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        label: 'Test',
      })
      await checkbox.updateComplete

      const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement

      // Initially not touched
      expect(checkbox.touched).toBe(false)

      // Click to interact
      fireEvent.click(input)
      await checkbox.updateComplete

      // Should be marked as touched
      expect(checkbox.touched).toBe(true)
    })
  })

  describe('Label position and structure', () => {
    test('places label on the right by default', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        label: 'Right Label',
      })
      await checkbox.updateComplete

      const inputDiv = checkbox.querySelector('.pkt-input-check__input')
      const children = Array.from(inputDiv?.children || [])

      // Should have input first, then label
      expect(children[0]).toHaveAttribute('type', 'checkbox')
      expect(children[1]).toHaveClass('pkt-input-check__input-label')
    })

    test('places label on the left when labelPosition is left', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        label: 'Left Label',
      })
      checkbox.labelPosition = 'left'
      await checkbox.updateComplete

      const inputDiv = checkbox.querySelector('.pkt-input-check__input')
      const children = Array.from(inputDiv?.children || [])

      // Should have label first, then input
      expect(children[0]).toHaveClass('pkt-input-check__input-label')
      expect(children[1]).toHaveAttribute('type', 'checkbox')
    })
  })

  describe('Error handling', () => {
    test('applies error styling when hasError is true', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        label: 'Test',
      })
      checkbox.hasError = true
      await checkbox.updateComplete

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

    test('does not apply error styling when hasError is false', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        label: 'Test',
      })
      await checkbox.updateComplete

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

  describe('Accessibility', () => {
    test('has no accessibility violations', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'accessible-checkbox',
        name: 'test',
        label: 'Accessible Checkbox',
      })
      await checkbox.updateComplete

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

    test('associates label with input correctly', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test-association',
        name: 'test',
        label: 'Associated Label',
      })
      await checkbox.updateComplete

      const input = checkbox.querySelector('input[type="checkbox"]')
      const label = checkbox.querySelector('.pkt-input-check__input-label')

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

    test('has correct role for switch when isSwitch is true', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        label: 'Test Switch',
      })
      checkbox.isSwitch = true
      await checkbox.updateComplete

      const input = checkbox.querySelector('input[type="checkbox"]')
      expect(input?.getAttribute('role')).toBe('switch')
    })

    test('has correct role for checkbox by default', async () => {
      const { checkbox } = await createCheckboxTest({
        id: 'test',
        name: 'test',
        label: 'Test Checkbox',
      })
      await checkbox.updateComplete

      const input = checkbox.querySelector('input[type="checkbox"]')
      expect(input?.getAttribute('role')).toBe('checkbox')
    })
  })
})
