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 './textarea'

export interface TextareaTestConfig extends BaseTestConfig {
  // From PktTextarea specific properties
  value?: string
  autocomplete?: string
  rows?: number | null

  // From PktInputElement base class (commonly used ones)
  id?: string
  label?: string
  name?: string
  disabled?: boolean
  readonly?: boolean
  required?: boolean
  placeholder?: string | null
  maxlength?: number | null
  minlength?: number | null
  hasError?: boolean
  errorMessage?: string
  helptext?: string
  fullwidth?: boolean
  counter?: boolean
  inline?: boolean
  ariaLabelledby?: string | null
  ariaDescribedBy?: string | null
}

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

  return {
    container,
    textarea: element,
  }
}

expect.extend(toHaveNoViolations)

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

describe('PktTextarea', () => {
  describe('Basic Rendering', () => {
    test('renders without errors', async () => {
      const { textarea } = await createTextareaTest()
      expect(textarea).toBeInTheDocument()
    })

    test('renders with default properties', async () => {
      const { textarea } = await createTextareaTest()
      expect(textarea.value).toBe('')
      expect(textarea.autocomplete).toBe('off')
      expect(textarea.rows).toBe(null)
    })

    test('renders textarea element', async () => {
      const { textarea } = await createTextareaTest()
      const textareaElement = textarea.querySelector('textarea')
      expect(textareaElement).toBeInTheDocument()
    })
  })

  describe('Properties and Attributes', () => {
    test('sets value correctly', async () => {
      const value = 'Test textarea content'
      const { textarea } = await createTextareaTest({ value })

      expect(textarea.value).toBe(value)
      const textareaElement = textarea.querySelector('textarea') as HTMLTextAreaElement
      expect(textareaElement.value).toBe(value)
    })

    test('sets rows correctly', async () => {
      const { textarea } = await createTextareaTest({ rows: 5 })

      expect(textarea.rows).toBe(5)
      const textareaElement = textarea.querySelector('textarea')
      expect(textareaElement?.getAttribute('rows')).toBe('5')
    })

    test('sets autocomplete correctly', async () => {
      const { textarea } = await createTextareaTest({ autocomplete: 'on' })

      expect(textarea.autocomplete).toBe('on')
      const textareaElement = textarea.querySelector('textarea')
      expect(textareaElement?.getAttribute('autocomplete')).toBe('on')
    })

    test('handles disabled state', async () => {
      const { textarea } = await createTextareaTest({ disabled: true })

      const textareaElement = textarea.querySelector('textarea')
      expect(textareaElement?.hasAttribute('disabled')).toBe(true)
    })

    test('handles readonly state', async () => {
      const { textarea } = await createTextareaTest({ readonly: true })

      const textareaElement = textarea.querySelector('textarea')
      expect(textareaElement?.hasAttribute('readonly')).toBe(true)
    })

    test('handles required state', async () => {
      const { textarea } = await createTextareaTest({ required: true })

      const textareaElement = textarea.querySelector('textarea')
      expect(textareaElement?.hasAttribute('required')).toBe(false) // Not set as attribute on textarea

      const inputWrapper = textarea.querySelector('pkt-input-wrapper')
      expect(inputWrapper?.hasAttribute('required')).toBe(true) // But passed to wrapper
    })
  })

  describe('Input Wrapper Integration', () => {
    test('displays label correctly', async () => {
      const { textarea } = await createTextareaTest({ label: 'Comment' })

      const inputWrapper = textarea.querySelector('pkt-input-wrapper')
      expect(inputWrapper?.getAttribute('label')).toBe('Comment')
    })

    test('displays helptext correctly', async () => {
      const { textarea } = await createTextareaTest({ helptext: 'Enter your message' })

      // helptext is passed as a property, not attribute to input-wrapper
      expect(textarea.helptext).toBe('Enter your message')
    })

    test('handles error state', async () => {
      const { textarea } = await createTextareaTest({
        hasError: true,
        errorMessage: 'This field is required',
      })

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

      const inputWrapper = textarea.querySelector('pkt-input-wrapper')
      expect(inputWrapper?.hasAttribute('hasError')).toBe(true)

      const textareaElement = textarea.querySelector('textarea')
      expect(textareaElement?.getAttribute('aria-invalid')).toBe('true')
    })

    test('handles fullwidth styling', async () => {
      const { textarea } = await createTextareaTest({ fullwidth: true })

      const textareaElement = textarea.querySelector('textarea')
      expect(textareaElement?.className).toContain('pkt-input--fullwidth')
    })
  })

  describe('Character Counter', () => {
    test('shows counter when enabled', async () => {
      const { textarea } = await createTextareaTest({
        counter: true,
        maxlength: 100,
      })

      const inputWrapper = textarea.querySelector('pkt-input-wrapper')
      expect(inputWrapper?.hasAttribute('counter')).toBe(true)
    })

    test('updates counter on value change', async () => {
      const { textarea } = await createTextareaTest({
        counter: true,
        maxlength: 100,
        value: 'Hello',
      })

      expect(textarea.counterCurrent).toBe(5)
    })
  })

  describe('User Interaction', () => {
    test('updates value on user input', async () => {
      const { textarea } = await createTextareaTest()
      const textareaElement = textarea.querySelector('textarea') as HTMLTextAreaElement

      fireEvent.input(textareaElement, { target: { value: 'New content' } })
      await textarea.updateComplete

      expect(textarea.value).toBe('New content')
      expect(textarea.touched).toBe(true)
    })

    test('handles focus and blur events', async () => {
      const { textarea } = await createTextareaTest()
      const textareaElement = textarea.querySelector('textarea') as HTMLTextAreaElement

      // Focus and input to trigger touched state
      fireEvent.focus(textareaElement)
      fireEvent.input(textareaElement, { target: { value: 'test input' } })
      await textarea.updateComplete
      fireEvent.blur(textareaElement)
      await textarea.updateComplete

      // Test that input with value change sets touched state
      expect(textarea.touched).toBe(true)
    })
  })

  describe('Validation', () => {
    test('respects maxlength constraint', async () => {
      const { textarea } = await createTextareaTest({ maxlength: 10 })

      const textareaElement = textarea.querySelector('textarea')
      expect(textareaElement?.getAttribute('maxlength')).toBe('10')
    })

    test('respects minlength constraint', async () => {
      const { textarea } = await createTextareaTest({ minlength: 5 })

      const textareaElement = textarea.querySelector('textarea')
      expect(textareaElement?.getAttribute('minlength')).toBe('5')
    })
  })

  describe('Accessibility', () => {
    test('passes through accessibility attributes', async () => {
      const { textarea } = await createTextareaTest({
        ariaLabelledby: 'external-label',
        ariaDescribedBy: 'external-description',
      })

      const textareaElement = textarea.querySelector('textarea')
      expect(textareaElement?.getAttribute('aria-labelledby')).toBe('external-label')

      // ariaDescribedBy is passed as property to input-wrapper
      expect(textarea.ariaDescribedBy).toBe('external-description')
    })

    test('textarea is accessible', async () => {
      const { textarea } = await createTextareaTest({
        label: 'Message',
        helptext: 'Enter your message here',
        required: true,
      })

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

  describe('Complex Configuration', () => {
    test('renders with all properties set', async () => {
      const config: TextareaTestConfig = {
        label: 'Feedback',
        value: 'Initial feedback text',
        placeholder: 'Enter your feedback...',
        rows: 6,
        maxlength: 500,
        counter: true,
        required: true,
        helptext: 'Please provide detailed feedback',
      }

      const { textarea } = await createTextareaTest(config)

      expect(textarea.value).toBe(config.value)
      expect(textarea.rows).toBe(config.rows)
      expect(textarea.maxlength).toBe(config.maxlength)
      expect(textarea.required).toBe(config.required)

      const inputWrapper = textarea.querySelector('pkt-input-wrapper')
      expect(inputWrapper?.getAttribute('label')).toBe(config.label)
      expect(textarea.helptext).toBe(config.helptext) // Property, not attribute
      expect(inputWrapper?.hasAttribute('counter')).toBe(true)

      const textareaElement = textarea.querySelector('textarea')
      expect(textareaElement?.getAttribute('placeholder')).toBe(config.placeholder)
      expect(textareaElement?.getAttribute('rows')).toBe(String(config.rows))
      expect(textareaElement?.getAttribute('maxlength')).toBe(String(config.maxlength))
      // required is handled by input-wrapper, not set directly on textarea
      expect(textarea.required).toBe(true)
    })
  })
})
