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

expect.extend(toHaveNoViolations)

export interface AlertTestConfig extends BaseTestConfig {
  skin?: string
  compact?: boolean
  title?: string
  date?: string
  closeAlert?: boolean
  ariaLive?: string
}

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

  return {
    container,
    alert: element,
  }
}

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

describe('PktAlert', () => {
  describe('Rendering and basic functionality', () => {
    test('renders without errors', async () => {
      const { alert } = await createAlertTest({
        content: 'Test alert content',
      })

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

      const alertDiv = alert.querySelector('.pkt-alert')
      expect(alertDiv).toBeInTheDocument()
    })

    test('renders with correct structure', async () => {
      const { alert } = await createAlertTest({
        title: 'Test Title',
        content: 'Test alert message',
      })

      await alert.updateComplete

      const alertDiv = alert.querySelector('.pkt-alert')
      const grid = alertDiv?.querySelector('.pkt-alert__grid')
      const icon = alert.querySelector('.pkt-alert__icon')
      const title = alert.querySelector('.pkt-alert__title')
      const text = alert.querySelector('.pkt-alert__text')

      expect(alertDiv).toHaveClass('pkt-alert')
      expect(grid).toHaveClass('pkt-alert__grid')
      expect(icon).toBeInTheDocument()
      expect(title).toBeInTheDocument()
      expect(text).toBeInTheDocument()
      expect(title?.textContent).toContain('Test Title')
    })

    test('renders icon correctly based on skin', async () => {
      const skins = ['error', 'success', 'warning', 'info'] as const

      for (const skin of skins) {
        const { alert } = await createAlertTest({ skin })
        await alert.updateComplete

        const icon = alert.querySelector('pkt-icon')
        const expectedIconName = skin === 'info' ? 'alert-information' : `alert-${skin}`

        expect(icon?.getAttribute('name')).toBe(expectedIconName)
        expect(icon?.getAttribute('aria-hidden')).toBe('true')
      }
    })
  })

  describe('Properties and attributes', () => {
    test('applies default properties correctly', async () => {
      const { alert } = await createAlertTest()

      await alert.updateComplete

      expect(alert.skin).toBe('info')
      expect(alert.compact).toBe(false)
      expect(alert.closeAlert).toBe(false)
      expect(alert.title).toBe('')
      expect(alert.date).toBe(null)
      expect(alert.role).toBe('status')

      const alertDiv = alert.querySelector('.pkt-alert')
      expect(alertDiv).toHaveClass('pkt-alert')
      expect(alertDiv).toHaveClass('pkt-alert--info')
      expect(alertDiv).not.toHaveClass('pkt-alert--compact')
    })

    test('applies different skin properties correctly', async () => {
      const skins = ['error', 'success', 'warning', 'info'] as const

      for (const skin of skins) {
        const { alert } = await createAlertTest({ skin })
        await alert.updateComplete

        expect(alert.skin).toBe(skin)
        expect(alert.getAttribute('skin')).toBe(skin)

        const alertDiv = alert.querySelector('.pkt-alert')
        expect(alertDiv).toHaveClass(`pkt-alert--${skin}`)
      }
    })

    test('applies compact property correctly', async () => {
      const { alert } = await createAlertTest({ compact: true })

      await alert.updateComplete

      expect(alert.compact).toBe(true)

      const alertDiv = alert.querySelector('.pkt-alert')
      expect(alertDiv).toHaveClass('pkt-alert--compact')
    })

    test('handles title property', async () => {
      const { alert } = await createAlertTest({ title: 'Alert Title' })

      await alert.updateComplete

      expect(alert.title).toBe('Alert Title')
      expect(alert.getAttribute('title')).toBe('Alert Title')

      const title = alert.querySelector('.pkt-alert__title')
      expect(title).toBeInTheDocument()
      expect(title?.textContent).toBe('Alert Title')

      // Test title updates
      alert.title = 'Updated Title'
      await alert.updateComplete

      expect(title?.textContent).toBe('Updated Title')
    })

    test('handles date property', async () => {
      const { alert } = await createAlertTest({ date: '2024-01-15' })

      await alert.updateComplete

      expect(alert.date).toBe('2024-01-15')
      expect(alert.getAttribute('date')).toBe('2024-01-15')

      const dateElement = alert.querySelector('.pkt-alert__date')
      expect(dateElement).toBeInTheDocument()
      expect(dateElement?.textContent).toContain('2024-01-15')
    })

    test('handles aria-live property', async () => {
      const { alert } = await createAlertTest({ ariaLive: 'assertive' })

      await alert.updateComplete

      // The component uses the ariaLive property value that was set
      expect(alert.ariaLiveAttr).toBe('assertive')

      const alertDiv = alert.querySelector('.pkt-alert')
      expect(alertDiv?.getAttribute('aria-live')).toBe('assertive')
    })
  })

  describe('Close functionality', () => {
    test('renders close button when closeAlert is true', async () => {
      const { alert } = await createAlertTest({ closeAlert: true })

      await alert.updateComplete

      expect(alert.closeAlert).toBe(true)

      const closeDiv = alert.querySelector('.pkt-alert__close')
      const closeButton = alert.querySelector('pkt-button')

      expect(closeDiv).toBeInTheDocument()
      expect(closeButton).toBeInTheDocument()
      expect(closeButton?.getAttribute('aria-label')).toBe('close')
      expect(closeButton?.getAttribute('skin')).toBe('tertiary')
      expect(closeButton?.getAttribute('variant')).toBe('icon-only')
    })

    test('does not render close button when closeAlert is false', async () => {
      const { alert } = await createAlertTest({ closeAlert: false })

      await alert.updateComplete

      expect(alert.closeAlert).toBe(false)

      const closeDiv = alert.querySelector('.pkt-alert__close')
      const closeButton = alert.querySelector('pkt-button')

      expect(closeDiv).not.toBeInTheDocument()
      expect(closeButton).not.toBeInTheDocument()
    })

    test('closes alert when close button is clicked', async () => {
      const { alert } = await createAlertTest({ closeAlert: true })

      await alert.updateComplete

      // Listen for close events
      const closeSpy = vi.fn()
      const onCloseSpy = vi.fn()
      alert.addEventListener('close', closeSpy)
      alert.addEventListener('on-close', onCloseSpy)

      const closeButton = alert.querySelector('pkt-button')
      expect(closeButton).toBeInTheDocument()

      // Click the close button
      fireEvent.click(closeButton!)
      await alert.updateComplete

      // Alert should be hidden
      const alertDiv = alert.querySelector('.pkt-alert')
      expect(alertDiv).toHaveClass('pkt-hide')
      expect(alert).toHaveClass('pkt-hide')

      // Events should be dispatched
      expect(closeSpy).toHaveBeenCalledWith(
        expect.objectContaining({
          type: 'close',
          detail: expect.objectContaining({
            origin: expect.any(Object),
          }),
        }),
      )
      expect(onCloseSpy).toHaveBeenCalledWith(
        expect.objectContaining({
          type: 'on-close',
          detail: expect.objectContaining({
            origin: expect.any(Object),
          }),
        }),
      )
    })
  })

  describe('Grid layout classes', () => {
    test('applies correct grid classes based on content', async () => {
      // Test with no title and no date
      const { alert: alert1 } = await createAlertTest()
      await alert1.updateComplete

      const grid1 = alert1.querySelector('.pkt-alert__grid')
      expect(grid1).toHaveClass('pkt-alert__noTitle')
      expect(grid1).toHaveClass('pkt-alert__noDate')

      // Test with title but no date
      const { alert: alert2 } = await createAlertTest({ title: 'Test Title' })
      await alert2.updateComplete

      const grid2 = alert2.querySelector('.pkt-alert__grid')
      expect(grid2).not.toHaveClass('pkt-alert__noTitle')
      expect(grid2).toHaveClass('pkt-alert__noDate')

      // Test with both title and date
      const { alert: alert3 } = await createAlertTest({ title: 'Test Title', date: '2024-01-15' })
      await alert3.updateComplete

      const grid3 = alert3.querySelector('.pkt-alert__grid')
      expect(grid3).not.toHaveClass('pkt-alert__noTitle')
      expect(grid3).not.toHaveClass('pkt-alert__noDate')
    })
  })

  describe('Accessibility', () => {
    test('has correct ARIA attributes', async () => {
      const { alert } = await createAlertTest({ ariaLive: 'polite' })

      await alert.updateComplete

      const alertDiv = alert.querySelector('.pkt-alert')
      const icon = alert.querySelector('pkt-icon')

      expect(alertDiv?.getAttribute('aria-live')).toBe('polite')
      expect(alert.role).toBe('status')
      expect(icon?.getAttribute('aria-hidden')).toBe('true')
    })

    test('renders with no WCAG errors with axe - simple alert', async () => {
      const { container } = await createAlertTest({
        title: 'Test Alert',
        content: 'This is a test alert message.',
      })

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

    test('renders with no WCAG errors with axe - complex alert', async () => {
      const { container } = await createAlertTest({
        skin: 'error',
        title: 'Error Alert',
        date: '2024-01-15',
        closeAlert: true,
        ariaLive: 'assertive',
        content: '<p>This is an error message with <a href="#">a link</a>.</p>',
      })

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

    test('renders with no WCAG errors with axe - compact alert', async () => {
      const { container } = await createAlertTest({
        skin: 'success',
        compact: true,
        closeAlert: true,
        content: 'Success message',
      })

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