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

expect.extend(toHaveNoViolations)

export interface ButtonTestConfig extends BaseTestConfig {
  size?: string
  skin?: string
  variant?: string
  color?: string
  type?: string
  disabled?: boolean
  isLoading?: boolean
  iconName?: string
  iconNameSecond?: string
  iconPath?: string
  secondIconPath?: string
  iconPosition?: string
  mode?: string
  form?: string
  content?: string
}

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

  return {
    container,
    button: element,
  }
}

// Cleanup after each test
afterEach(() => {
  document.body.innerHTML = ''
})

describe('PktButton', () => {
  describe('Rendering and basic functionality', () => {
    test('renders without errors', async () => {
      const { button } = await createButtonTest({ content: 'Test Button' })

      expect(button).toBeInTheDocument()
      expect(button).toBeTruthy()

      const nativeButton = button.querySelector('button')
      expect(nativeButton).toBeInTheDocument()
    })

    test('renders with correct structure', async () => {
      const { button } = await createButtonTest({
        variant: 'icon-left',
        iconName: 'user',
        content: 'Click Me',
      })

      const nativeButton = button.querySelector('button')
      const icon = nativeButton?.querySelector('pkt-icon')
      const textSpan = nativeButton?.querySelector('.pkt-btn__text')

      expect(nativeButton).toHaveClass('pkt-btn')
      expect(icon).toHaveClass('pkt-btn__icon')
      expect(textSpan).toHaveClass('pkt-btn__text')
      expect(textSpan?.textContent?.trim()).toContain('Click Me')
    })

    test('renders text correctly', async () => {
      const { button } = await createButtonTest({ content: 'Button Text Content' })

      const textSpan = button.querySelector('.pkt-btn__text')
      expect(textSpan).toBeInTheDocument()
      expect(textSpan?.textContent?.trim()).toContain('Button Text Content')
    })
  })

  describe('Properties and attributes', () => {
    test('applies default properties correctly', async () => {
      const { button } = await createButtonTest({ content: 'Test Button' })
      await button.updateComplete

      expect(button.size).toBe('medium')
      expect(button.skin).toBe('primary')
      expect(button.variant).toBe('label-only')
      expect(button.type).toBe('button')
      expect(button.mode).toBe('light')
      expect(button.disabled).toBe(false)
      expect(button.isLoading).toBe(false)

      const buttonEl = button.querySelector('button')
      expect(buttonEl).toHaveClass('pkt-btn')
      expect(buttonEl).toHaveClass('pkt-btn--medium')
      expect(buttonEl).toHaveClass('pkt-btn--primary')
      expect(buttonEl).toHaveClass('pkt-btn--label-only')
    })

    test('applies different size properties correctly', async () => {
      const sizes = ['small', 'medium', 'large'] as const

      for (const size of sizes) {
        const { button } = await createButtonTest({ size, content: 'Test Button' })
        await button.updateComplete

        expect(button.size).toBe(size)

        const buttonEl = button.querySelector('button')
        expect(buttonEl).toHaveClass(`pkt-btn--${size}`)
      }
    })

    test('applies different skin properties correctly', async () => {
      const skins = ['primary', 'secondary', 'tertiary'] as const

      for (const skin of skins) {
        const { button } = await createButtonTest({ skin, content: 'Test Button' })
        await button.updateComplete

        expect(button.skin).toBe(skin)

        const buttonEl = button.querySelector('button')
        expect(buttonEl).toHaveClass(`pkt-btn--${skin}`)
      }
    })

    test('applies different variant properties correctly', async () => {
      const variants = [
        'label-only',
        'icon-left',
        'icon-right',
        'icon-only',
        'icons-right-and-left',
      ] as const

      for (const variant of variants) {
        const { button } = await createButtonTest({
          variant,
          iconName: 'user',
          iconNameSecond: 'star',
          content: 'Test Button',
        })
        await button.updateComplete

        expect(button.variant).toBe(variant)

        const buttonEl = button.querySelector('button')
        expect(buttonEl).toHaveClass(`pkt-btn--${variant}`)

        // Check icon rendering based on variant
        const icons = buttonEl?.querySelectorAll('pkt-icon:not(.pkt-btn__spinner)')
        if (variant === 'label-only') {
          expect(icons).toHaveLength(0)
        } else if (variant === 'icons-right-and-left') {
          expect(icons).toHaveLength(2)
        } else {
          expect(icons).toHaveLength(1)
        }
      }
    })

    test('applies different color properties correctly', async () => {
      const colors = ['blue', 'green', 'red', 'yellow'] as const

      for (const color of colors) {
        const { button } = await createButtonTest({ color, content: 'Test Button' })
        await button.updateComplete

        expect(button.color).toBe(color)

        const buttonEl = button.querySelector('button')
        expect(buttonEl).toHaveClass(`pkt-btn--${color}`)
      }
    })

    test('handles type property correctly', async () => {
      const types = ['button', 'submit', 'reset'] as const

      for (const type of types) {
        const { button } = await createButtonTest({ type, content: 'Test Button' })
        await button.updateComplete

        expect(button.type).toBe(type)

        const buttonEl = button.querySelector('button')
        expect(buttonEl?.getAttribute('type')).toBe(type)
      }
    })

    test('handles icon properties correctly', async () => {
      const { button } = await createButtonTest({
        variant: 'icon-left',
        iconName: 'user',
        content: 'Test Button',
      })
      await button.updateComplete

      expect(button.iconName).toBe('user')

      const icon = button.querySelector('pkt-icon:not(.pkt-btn__spinner)')
      expect(icon?.getAttribute('name')).toBe('user')
      expect(icon).toHaveClass('pkt-btn__icon')
    })

    test('handles second icon for icons-right-and-left variant', async () => {
      const { button } = await createButtonTest({
        variant: 'icons-right-and-left',
        content: 'Test Button',
      })

      // Set both icon names as properties
      button.iconName = 'home'
      button.secondIconName = 'star'
      await button.updateComplete

      expect(button.iconName).toBe('home')
      expect(button.secondIconName).toBe('star')

      const icons = button.querySelectorAll('pkt-icon:not(.pkt-btn__spinner)')
      expect(icons).toHaveLength(2)
      expect(icons[0]?.getAttribute('name')).toBe('home')
      expect(icons[1]?.getAttribute('name')).toBe('star')
    })

    test('handles iconPath property correctly', async () => {
      const customPath = 'https://custom-cdn.example.com/icons/'
      const { button } = await createButtonTest({
        variant: 'icon-left',
        iconName: 'user',
        iconPath: customPath,
        content: 'Test Button',
      })
      await button.updateComplete

      expect(button.iconPath).toBe(customPath)

      const icon = button.querySelector('pkt-icon:not(.pkt-btn__spinner)')
      expect(icon?.getAttribute('path')).toBe(customPath)
    })

    test('does not set path attribute when iconPath is not provided', async () => {
      const { button } = await createButtonTest({
        variant: 'icon-left',
        iconName: 'user',
        content: 'Test Button',
      })
      await button.updateComplete

      expect(button.iconPath).toBeUndefined()

      const icon = button.querySelector('pkt-icon:not(.pkt-btn__spinner)')
      expect(icon?.hasAttribute('path')).toBe(false)
    })

    test('handles secondIconPath property correctly', async () => {
      const customPath = 'https://custom-cdn.example.com/icons/'
      const { button } = await createButtonTest({
        variant: 'icons-right-and-left',
        iconName: 'home',
        secondIconPath: customPath,
        content: 'Test Button',
      })

      button.secondIconName = 'star'
      await button.updateComplete

      expect(button.secondIconPath).toBe(customPath)

      const icons = button.querySelectorAll('pkt-icon:not(.pkt-btn__spinner)')
      expect(icons).toHaveLength(2)
      expect(icons[1]?.getAttribute('path')).toBe(customPath)
    })

    test('handles both iconPath and secondIconPath independently', async () => {
      const iconPath = 'https://custom-cdn.example.com/icons/'
      const secondIconPath = 'https://another-cdn.example.com/icons/'
      const { button } = await createButtonTest({
        variant: 'icons-right-and-left',
        iconName: 'home',
        iconPath: iconPath,
        secondIconPath: secondIconPath,
        content: 'Test Button',
      })

      button.secondIconName = 'star'
      await button.updateComplete

      expect(button.iconPath).toBe(iconPath)
      expect(button.secondIconPath).toBe(secondIconPath)

      const icons = button.querySelectorAll('pkt-icon:not(.pkt-btn__spinner)')
      expect(icons).toHaveLength(2)
      expect(icons[0]?.getAttribute('path')).toBe(iconPath)
      expect(icons[1]?.getAttribute('path')).toBe(secondIconPath)
    })
  })

  describe('Disabled state', () => {
    test('handles disabled property correctly', async () => {
      const { button } = await createButtonTest({ disabled: true, content: 'Test Button' })
      await button.updateComplete

      expect(button.disabled).toBe(true)

      const buttonEl = button.querySelector('button')
      expect(buttonEl).toHaveClass('pkt-btn--disabled')
      expect(buttonEl?.hasAttribute('disabled')).toBe(true)
      expect(buttonEl?.getAttribute('aria-disabled')).toBe('true')
    })

    test('prevents click events when disabled', async () => {
      const { button } = await createButtonTest({ disabled: true, content: 'Test Button' })
      const clickSpy = vi.fn()

      await button.updateComplete

      button.addEventListener('click', clickSpy)

      const buttonEl = button.querySelector('button')
      fireEvent.click(buttonEl!)

      expect(clickSpy).not.toHaveBeenCalled()
    })

    test('prevents keyboard events when disabled', async () => {
      const { button } = await createButtonTest({ disabled: true, content: 'Test Button' })
      const clickSpy = vi.fn()

      await button.updateComplete

      button.addEventListener('click', clickSpy)

      const buttonEl = button.querySelector('button')
      fireEvent.keyDown(buttonEl!, { key: 'Enter' })
      fireEvent.keyDown(buttonEl!, { key: ' ' })

      expect(clickSpy).not.toHaveBeenCalled()
    })

    test('converts string "false" to boolean false for disabled', async () => {
      const { button } = await createButtonTest({ disabled: false, content: 'Test Button' })
      await button.updateComplete

      expect(button.disabled).toBe(false)

      const buttonEl = button.querySelector('button')
      expect(buttonEl).not.toHaveClass('pkt-btn--disabled')
      expect(buttonEl?.hasAttribute('disabled')).toBe(false)
    })
  })

  describe('Loading state', () => {
    test('handles isLoading property correctly', async () => {
      const { button } = await createButtonTest({ content: 'Test Button' })

      // Set isLoading as a property
      button.isLoading = true
      await button.updateComplete

      expect(button.isLoading).toBe(true)

      const buttonEl = button.querySelector('button')
      expect(buttonEl).toHaveClass('pkt-btn--isLoading')
      expect(buttonEl?.getAttribute('aria-busy')).toBe('true')
      expect(buttonEl?.getAttribute('aria-disabled')).toBe('true')
    })

    test('renders loading spinner when isLoading is true', async () => {
      const { button } = await createButtonTest({ content: 'Test Button' })

      // Set isLoading as a property
      button.isLoading = true
      await button.updateComplete

      const spinner = button.querySelector('.pkt-btn__spinner')
      expect(spinner).toBeInTheDocument()
      expect(spinner?.getAttribute('name')).toBe('spinner-blue')
    })

    test('prevents click events when loading', async () => {
      const { button } = await createButtonTest({ content: 'Test Button' })
      const clickSpy = vi.fn()

      // Set isLoading as a property
      button.isLoading = true
      await button.updateComplete

      button.addEventListener('click', clickSpy)

      const buttonEl = button.querySelector('button')
      fireEvent.click(buttonEl!)

      expect(clickSpy).not.toHaveBeenCalled()
    })

    test('uses custom loading animation path', async () => {
      const customPath = 'https://custom.example.com/animations/'
      const { button } = await createButtonTest({ isLoading: true, content: 'Test Button' })

      button.loadingAnimationPath = customPath
      await button.updateComplete

      expect(button.loadingAnimationPath).toBe(customPath)

      const spinner = button.querySelector('.pkt-btn__spinner')
      expect(spinner?.getAttribute('path')).toBe(customPath)
    })

    test('converts string "false" to boolean false for isLoading', async () => {
      const { button } = await createButtonTest({ isLoading: false, content: 'Test Button' })
      await button.updateComplete

      expect(button.isLoading).toBe(false)

      const buttonEl = button.querySelector('button')
      expect(buttonEl).not.toHaveClass('pkt-btn--isLoading')
    })
  })

  describe('Form integration', () => {
    test('handles form attribute correctly', async () => {
      const { button } = await createButtonTest({
        form: 'test-form',
        type: 'submit',
        content: 'Test Button',
      })
      await button.updateComplete

      expect(button.form).toBe('test-form')

      const buttonEl = button.querySelector('button')
      expect(buttonEl?.getAttribute('form')).toBe('test-form')
    })

    test('works as submit button', async () => {
      const { button } = await createButtonTest({ type: 'submit', content: 'Test Button' })
      await button.updateComplete

      const buttonEl = button.querySelector('button')
      expect(buttonEl?.getAttribute('type')).toBe('submit')
    })
  })

  describe('Click functionality', () => {
    test('allows click events when not disabled or loading', async () => {
      const { button } = await createButtonTest({ content: 'Test Button' })
      const clickSpy = vi.fn()

      await button.updateComplete

      button.addEventListener('click', clickSpy)

      const buttonEl = button.querySelector('button')
      fireEvent.click(buttonEl!)

      expect(clickSpy).toHaveBeenCalledTimes(1)
    })

    test('allows keyboard activation when not disabled or loading', async () => {
      const { button } = await createButtonTest({ content: 'Test Button' })
      const clickSpy = vi.fn()

      await button.updateComplete

      button.addEventListener('click', clickSpy)

      const buttonEl = button.querySelector('button')
      fireEvent.keyDown(buttonEl!, { key: 'Enter' })

      // Note: Native button handles Enter key, so we just test that events aren't prevented
      // The actual click event would be triggered by the browser
    })
  })

  describe('Accessibility', () => {
    test('has correct ARIA attributes', async () => {
      const { button } = await createButtonTest({ disabled: true, content: 'Test Button' })

      // Set isLoading as a property
      button.isLoading = true
      await button.updateComplete

      const buttonEl = button.querySelector('button')

      expect(buttonEl?.getAttribute('aria-disabled')).toBe('true')
      expect(buttonEl?.getAttribute('aria-busy')).toBe('true')
      expect(buttonEl?.hasAttribute('disabled')).toBe(true)
    })

    test('provides semantic button structure', async () => {
      const { button } = await createButtonTest({ content: 'Test Button' })
      await button.updateComplete

      const buttonEl = button.querySelector('button')

      expect(buttonEl).toBeInTheDocument()
      expect(buttonEl?.tagName.toLowerCase()).toBe('button')
      expect(buttonEl?.getAttribute('type')).toBe('button')
    })

    test('renders with no WCAG errors with axe - default button', async () => {
      const { container } = await createButtonTest({ content: 'Click me' })

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

    test('renders with no WCAG errors with axe - icon button', async () => {
      const { container } = await createButtonTest({
        variant: 'icon-left',
        iconName: 'user',
        skin: 'secondary',
        content: 'User Profile',
      })

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

    test('renders with no WCAG errors with axe - disabled button', async () => {
      const { container } = await createButtonTest({ disabled: true, content: 'Disabled Button' })

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

    test('renders with no WCAG errors with axe - loading button', async () => {
      const { container } = await createButtonTest({ isLoading: true, content: 'Loading...' })

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

    test('renders with no WCAG errors with axe - submit button', async () => {
      const { container } = await createButtonTest({
        type: 'submit',
        color: 'green',
        content: 'Submit Form',
      })

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