import '@testing-library/jest-dom'
import { axe, toHaveNoViolations } from 'jest-axe'
import { createElementTest, BaseTestConfig } from '../../tests/test-framework'
import { CustomElementFor } from '../../tests/component-registry'
import { PktLinkCard, type TLinkCardSkin } from './linkcard'
import type { IPktLinkCard } from './linkcard'
import './linkcard'

export interface LinkCardTestConfig extends IPktLinkCard, BaseTestConfig {}

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

  const link = element.querySelector('a') as HTMLAnchorElement

  return {
    container,
    linkCard: element,
    link,
  }
}
expect.extend(toHaveNoViolations)

// Test data constants
const VALID_SKINS: TLinkCardSkin[] = [
  'normal',
  'no-padding',
  'blue',
  'beige',
  'green',
  'gray',
  'beige-outline',
  'gray-outline',
]

const SAMPLE_ICONS = ['arrow-right', 'external-link', 'download', 'info']

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

// ----------- THE TESTS -----------

describe('PktLinkCard', () => {
  describe('Basic Rendering', () => {
    test('renders without errors', async () => {
      const { linkCard, link } = await createLinkCardTest()

      expect(linkCard).toBeInTheDocument()
      expect(link).toBeInTheDocument()
      expect(link).toHaveClass('pkt-linkcard')
    })

    test('renders with default properties', async () => {
      const { linkCard, link } = await createLinkCardTest()

      expect(linkCard.title).toBe('')
      expect(linkCard.href).toBe('#')
      expect(linkCard.iconName).toBe('')
      expect(linkCard.openInNewTab).toBe(false)
      expect(linkCard.skin).toBe('normal')
      expect(linkCard.external).toBe(false)

      expect(link.getAttribute('href')).toBe('#')
      expect(link.getAttribute('target')).toBe('_self')
      // When openInNewTab is false, rel should be null (ifDefined behavior)
      expect(link.getAttribute('rel')).toBeNull()
    })

    test('renders content in slot', async () => {
      const content = '<p>Custom link card content</p>'
      const { linkCard } = await createLinkCardTest({ content })

      const textSlot = linkCard.querySelector('.pkt-linkcard__text')
      expect(textSlot).toBeInTheDocument()
      expect(textSlot?.innerHTML).toContain(content)
    })
  })

  describe('Title Functionality', () => {
    test('renders title when provided', async () => {
      const title = 'Test Link Card Title'
      const { linkCard } = await createLinkCardTest({ title })

      expect(linkCard.title).toBe(title)

      const titleElement = linkCard.querySelector('.pkt-linkcard__title')
      expect(titleElement).toBeInTheDocument()
      expect(titleElement?.textContent).toBe(title)
    })

    test('does not render title when not provided', async () => {
      const { linkCard } = await createLinkCardTest()

      const titleElement = linkCard.querySelector('.pkt-linkcard__title')
      expect(titleElement).not.toBeInTheDocument()
    })

    test('applies correct title classes', async () => {
      const title = 'Test Title'
      const { linkCard } = await createLinkCardTest({ title })

      const titleElement = linkCard.querySelector('.pkt-linkcard__title')
      expect(titleElement).toHaveClass('pkt-linkcard__title')
    })
  })

  describe('Link Functionality', () => {
    test('renders with custom href', async () => {
      const href = '/custom-path'
      const { linkCard, link } = await createLinkCardTest({ href })

      expect(linkCard.href).toBe(href)
      expect(link.getAttribute('href')).toBe(href)
    })

    test('handles openInNewTab correctly', async () => {
      const { linkCard, link } = await createLinkCardTest({ openInNewTab: true })

      expect(linkCard.openInNewTab).toBe(true)
      expect(link.getAttribute('target')).toBe('_blank')
    })

    test('handles openInNewTab false correctly', async () => {
      const { linkCard, link } = await createLinkCardTest({ openInNewTab: false })

      expect(linkCard.openInNewTab).toBe(false)
      expect(link.getAttribute('target')).toBe('_self')
    })

    test('applies correct rel attribute for new tab', async () => {
      const { link } = await createLinkCardTest({ openInNewTab: true })
      expect(link.getAttribute('rel')).toBe('noopener noreferrer')
    })
  })

  describe('Icon Functionality', () => {
    test('renders icon when iconName provided', async () => {
      const iconName = 'arrow-right'
      const { linkCard } = await createLinkCardTest({ iconName })

      expect(linkCard.iconName).toBe(iconName)

      const icon = linkCard.querySelector('pkt-icon')
      expect(icon).toBeInTheDocument()
      expect(icon).toHaveClass('pkt-link__icon')
      expect(icon?.getAttribute('name')).toBe(iconName)
    })

    test('does not render icon when iconName not provided', async () => {
      const { linkCard } = await createLinkCardTest()

      const icon = linkCard.querySelector('pkt-icon')
      expect(icon).not.toBeInTheDocument()
    })

    test('renders with different icon names', async () => {
      for (const iconName of SAMPLE_ICONS) {
        const { linkCard } = await createLinkCardTest({ iconName })

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

  describe('Skin Variations', () => {
    test('applies default skin class', async () => {
      const { link } = await createLinkCardTest()
      expect(link).toHaveClass('pkt-linkcard')
      expect(link).toHaveClass('pkt-linkcard--normal')
    })

    test('applies different skin classes correctly', async () => {
      for (const skin of VALID_SKINS) {
        const { link } = await createLinkCardTest({ skin })

        expect(link).toHaveClass('pkt-linkcard')
        expect(link).toHaveClass(`pkt-linkcard--${skin}`)
      }
    })

    test('handles skin property changes', async () => {
      const { linkCard, link } = await createLinkCardTest({ skin: 'blue' })

      expect(link).toHaveClass('pkt-linkcard--blue')

      // Change skin
      linkCard.skin = 'green'
      await linkCard.updateComplete

      expect(link).toHaveClass('pkt-linkcard--green')
      expect(link).not.toHaveClass('pkt-linkcard--blue')
    })
  })

  describe('Property Updates', () => {
    test('updates title dynamically', async () => {
      const { linkCard } = await createLinkCardTest({ title: 'Initial Title' })

      expect(linkCard.querySelector('.pkt-linkcard__title')?.textContent).toBe('Initial Title')

      linkCard.title = 'Updated Title'
      await linkCard.updateComplete

      expect(linkCard.querySelector('.pkt-linkcard__title')?.textContent).toBe('Updated Title')
    })

    test('updates href dynamically', async () => {
      const { linkCard, link } = await createLinkCardTest({ href: '/initial' })

      expect(link.getAttribute('href')).toBe('/initial')

      linkCard.href = '/updated'
      await linkCard.updateComplete

      expect(link.getAttribute('href')).toBe('/updated')
    })

    test('updates openInNewTab dynamically', async () => {
      const { linkCard, link } = await createLinkCardTest({ openInNewTab: false })

      expect(link.getAttribute('target')).toBe('_self')

      linkCard.openInNewTab = true
      await linkCard.updateComplete

      expect(link.getAttribute('target')).toBe('_blank')
    })
  })

  describe('Complex Configurations', () => {
    test('renders with all properties set', async () => {
      const config: LinkCardTestConfig = {
        title: 'Complete Link Card',
        href: '/complete-path',
        iconName: 'arrow-right',
        openInNewTab: true,
        skin: 'blue',
        content: '<p>Complete content</p>',
      }

      const { linkCard, link } = await createLinkCardTest(config)

      // Verify all properties
      expect(linkCard.title).toBe(config.title)
      expect(linkCard.href).toBe(config.href)
      expect(linkCard.iconName).toBe(config.iconName)
      expect(linkCard.openInNewTab).toBe(config.openInNewTab)
      expect(linkCard.skin).toBe(config.skin)

      // Verify DOM elements
      expect(link.getAttribute('href')).toBe(config.href)
      expect(link.getAttribute('target')).toBe('_blank')
      expect(link).toHaveClass(`pkt-linkcard--${config.skin}`)

      const titleElement = linkCard.querySelector('.pkt-linkcard__title')
      expect(titleElement?.textContent).toBe(config.title)

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

      const textSlot = linkCard.querySelector('.pkt-linkcard__text')
      expect(textSlot?.innerHTML).toContain(config.content)
    })

    test('renders minimal configuration', async () => {
      const { linkCard, link } = await createLinkCardTest({ href: '/minimal' })

      expect(linkCard.href).toBe('/minimal')
      expect(link.getAttribute('href')).toBe('/minimal')
      expect(link).toHaveClass('pkt-linkcard--normal')

      // Should not have title or icon
      expect(linkCard.querySelector('.pkt-linkcard__title')).not.toBeInTheDocument()
      expect(linkCard.querySelector('pkt-icon')).not.toBeInTheDocument()
    })
  })

  describe('Edge Cases', () => {
    test('handles empty string values', async () => {
      const { linkCard } = await createLinkCardTest({
        title: '',
        href: '',
        iconName: '',
      })

      expect(linkCard.title).toBe('')
      expect(linkCard.href).toBe('')
      expect(linkCard.iconName).toBe('')

      // Should not render title or icon with empty strings
      expect(linkCard.querySelector('.pkt-linkcard__title')).not.toBeInTheDocument()
      expect(linkCard.querySelector('pkt-icon')).not.toBeInTheDocument()
    })

    test('handles special characters in title', async () => {
      const specialTitle = 'Title with "quotes" & <symbols>'
      const { linkCard } = await createLinkCardTest()

      // Set title via property to avoid HTML attribute encoding issues
      linkCard.title = specialTitle
      await linkCard.updateComplete

      expect(linkCard.title).toBe(specialTitle)

      const titleElement = linkCard.querySelector('.pkt-linkcard__title')
      expect(titleElement).toBeInTheDocument()
      expect(titleElement?.textContent).toBe(specialTitle)
    })
  })

  describe('Accessibility', () => {
    test('has no accessibility violations', async () => {
      const { linkCard } = await createLinkCardTest({
        title: 'Accessible Link Card',
        href: '/accessible',
      })

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

    test('maintains proper link semantics', async () => {
      const { link } = await createLinkCardTest({
        title: 'Semantic Link',
        href: '/semantic',
      })

      expect(link.tagName).toBe('A')
      expect(link.getAttribute('href')).toBe('/semantic')
      expect(link.textContent).toContain('Semantic Link')
    })

    test('handles external link accessibility', async () => {
      const { link } = await createLinkCardTest({
        title: 'External Link',
        href: 'https://example.com',
        openInNewTab: true,
      })

      expect(link.getAttribute('target')).toBe('_blank')
      expect(link.getAttribute('rel')).toBe('noopener noreferrer')
    })
  })

  describe('Type Safety', () => {
    test('validates interface implementation', () => {
      const linkCard = new PktLinkCard()

      // Check that all interface properties exist
      expect(linkCard).toHaveProperty('title')
      expect(linkCard).toHaveProperty('href')
      expect(linkCard).toHaveProperty('iconName')
      expect(linkCard).toHaveProperty('openInNewTab')
      expect(linkCard).toHaveProperty('skin')
    })
  })
})
