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 { type IPktTag } from './tag'
import './tag'

export interface TagTestConfig extends Partial<IPktTag>, BaseTestConfig {}

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

  return {
    container,
    tag: element,
  }
}

expect.extend(toHaveNoViolations)

// Test data constants
const VALID_SKINS = [
  'blue',
  'blue-dark',
  'blue-light',
  'green',
  'red',
  'yellow',
  'beige',
  'gray',
  'grey',
] as const
const VALID_SIZES = ['small', 'medium', 'large'] as const
const VALID_TYPES = ['button', 'reset', 'submit'] as const

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

describe('PktTag', () => {
  describe('Basic Rendering', () => {
    test('renders without errors', async () => {
      const { tag } = await createTagTest()
      expect(tag).toBeInTheDocument()
    })

    test('renders with default properties', async () => {
      const { tag } = await createTagTest()
      expect(tag.closeTag).toBe(false)
      expect(tag.size).toBe('medium')
      expect(tag.skin).toBe('blue')
      expect(tag.type).toBe('button')
    })

    test('renders content in slot', async () => {
      const content = 'Test Tag Content'
      const { tag } = await createTagTest({ content })
      expect(tag.textContent?.trim()).toBe(content)
    })
  })

  describe('Skin Variations', () => {
    test('applies different skin classes correctly', async () => {
      for (const skin of VALID_SKINS) {
        const { tag } = await createTagTest({ skin })
        // For non-closeable tags, check the span element
        const tagElement = tag.querySelector('span') || tag.querySelector('button')
        expect(tagElement).toHaveClass(`pkt-tag--${skin}`)
      }
    })
  })

  describe('Size Variations', () => {
    test('applies different size classes correctly', async () => {
      for (const size of VALID_SIZES) {
        const { tag } = await createTagTest({ size })
        // For non-closeable tags, check the span element
        const tagElement = tag.querySelector('span') || tag.querySelector('button')
        expect(tagElement).toHaveClass(`pkt-tag--${size}`)
      }
    })
  })

  describe('Type Variations', () => {
    test('sets correct type attribute', async () => {
      for (const type of VALID_TYPES) {
        const { tag } = await createTagTest({ type })
        expect(tag.type).toBe(type)
      }
    })
  })

  describe('Icon Functionality', () => {
    test('renders icon when iconName provided', async () => {
      const { tag } = await createTagTest({ iconName: 'arrow-right' })
      const icon = tag.querySelector('pkt-icon')
      expect(icon).toBeInTheDocument()
      expect(icon?.getAttribute('name')).toBe('arrow-right')
    })

    test('does not render icon when iconName not provided', async () => {
      const { tag } = await createTagTest()
      const icon = tag.querySelector('pkt-icon')
      expect(icon).not.toBeInTheDocument()
    })
  })

  describe('Close Functionality', () => {
    test('renders close button when closeTag is true', async () => {
      const { tag } = await createTagTest({ closeTag: true })
      const closeIcon = tag.querySelector('.pkt-tag__close-btn')
      expect(closeIcon).toBeInTheDocument()
    })

    test('does not render close button when closeTag is false', async () => {
      const { tag } = await createTagTest({ closeTag: false })
      const closeIcon = tag.querySelector('.pkt-tag__close-btn')
      expect(closeIcon).not.toBeInTheDocument()
    })

    test('dispatches close event when close button is clicked', async () => {
      const { tag } = await createTagTest({ closeTag: true })
      const closeSpy = vi.fn()
      tag.addEventListener('close', closeSpy)

      const button = tag.querySelector('button') as HTMLButtonElement
      fireEvent.click(button)

      expect(closeSpy).toHaveBeenCalled()
    })

    test('hides tag when closed', async () => {
      const { tag } = await createTagTest({ closeTag: true })
      const button = tag.querySelector('button') as HTMLButtonElement

      fireEvent.click(button)
      await tag.updateComplete

      expect(button).toHaveClass('pkt-hide')
    })
  })

  describe('Text Style', () => {
    test('applies text style class when provided', async () => {
      const { tag } = await createTagTest({ textStyle: 'thin-text' })
      const tagElement = tag.querySelector('span') || tag.querySelector('button')
      expect(tagElement).toHaveClass('pkt-tag--thin-text')
    })
  })

  describe('Accessibility', () => {
    test('applies aria-label when provided', async () => {
      const { tag } = await createTagTest({ closeTag: true, ariaLabel: 'Close tag' })
      const button = tag.querySelector('button')
      expect(button?.getAttribute('aria-label')).toBe('Close tag')
    })

    test('tag is accessible', async () => {
      const { tag } = await createTagTest({
        content: 'Accessible Tag',
        closeTag: true,
        ariaLabel: 'Close tag',
      })

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

  describe('Complex Configurations', () => {
    test('renders with all properties set', async () => {
      const config: TagTestConfig = {
        content: 'Complete Tag',
        closeTag: true,
        size: 'large',
        skin: 'green',
        iconName: 'check',
        type: 'submit',
        textStyle: 'thin-text',
        ariaLabel: 'Complete tag',
      }

      const { tag } = await createTagTest(config)

      expect(tag.textContent?.trim()).toBe(config.content)
      expect(tag.closeTag).toBe(config.closeTag)
      expect(tag.size).toBe(config.size)
      expect(tag.skin).toBe(config.skin)
      expect(tag.iconName).toBe(config.iconName)
      expect(tag.type).toBe(config.type)
      expect(tag.textStyle).toBe(config.textStyle)
      expect(tag.ariaLabel).toBe(config.ariaLabel)

      const button = tag.querySelector('button')
      expect(button).toHaveClass(`pkt-tag--${config.size}`)
      expect(button).toHaveClass(`pkt-tag--${config.skin}`)
      expect(button).toHaveClass(`pkt-tag--${config.textStyle}`)

      const icon = tag.querySelector('pkt-icon')
      expect(icon?.getAttribute('name')).toBe(config.iconName)

      const closeIcon = tag.querySelector('.pkt-tag__close-btn')
      expect(closeIcon).toBeInTheDocument()
    })
  })
})
