import '@testing-library/jest-dom'

import { cleanup, render } from '@testing-library/react'
import { axe, toHaveNoViolations } from 'jest-axe'
import { createRef } from 'react'

import { PktCard } from './Card'

expect.extend(toHaveNoViolations)

afterEach(cleanup)

describe('PktCard', () => {
  describe('Rendering and basic functionality', () => {
    test('renders without errors', () => {
      const { container } = render(<PktCard>innhold</PktCard>)
      const article = container.querySelector('article.pkt-card')
      expect(article).toBeInTheDocument()
    })

    test('renders content in children', () => {
      const { container } = render(<PktCard><p>Test content here</p></PktCard>)
      const content = container.querySelector('.pkt-card__content')
      expect(content).toBeInTheDocument()
      expect(content?.textContent).toContain('Test content here')
    })

    test('renders basic structure correctly', () => {
      const { container } = render(<PktCard heading="Test Heading">Test content</PktCard>)
      const article = container.querySelector('article')
      const wrapper = article?.querySelector('.pkt-card__wrapper')
      const header = wrapper?.querySelector('.pkt-card__header')
      const content = wrapper?.querySelector('.pkt-card__content')

      expect(wrapper).toBeInTheDocument()
      expect(header).toBeInTheDocument()
      expect(content).toBeInTheDocument()
    })

    test('forwards ref correctly', () => {
      const ref = createRef<HTMLDivElement>()
      const { unmount } = render(<PktCard ref={ref} heading="Test">innhold</PktCard>)
      expect(ref.current).toBeInstanceOf(HTMLElement)
      unmount()
      expect(ref.current).toBeNull()
    })
  })

  describe('Properties and attributes', () => {
    test('applies default properties correctly', () => {
      const { container } = render(<PktCard>innhold</PktCard>)
      const article = container.querySelector('article')
      expect(article).toHaveClass('pkt-card--outlined')
      expect(article).toHaveClass('pkt-card--vertical')
      expect(article).toHaveClass('pkt-card--padding-default')
      expect(article).toHaveClass('pkt-card--border-on-hover')
    })

    test('applies different skin properties correctly', () => {
      const skins = ['outlined', 'outlined-beige', 'gray', 'beige', 'green', 'blue'] as const
      for (const skin of skins) {
        const { container, unmount } = render(<PktCard skin={skin}>innhold</PktCard>)
        const article = container.querySelector('article')
        expect(article).toHaveClass(`pkt-card--${skin}`)
        unmount()
      }
    })

    test('applies different layout properties correctly', () => {
      const layouts = ['vertical', 'horizontal'] as const
      for (const layout of layouts) {
        const { container, unmount } = render(<PktCard layout={layout}>innhold</PktCard>)
        const article = container.querySelector('article')
        expect(article).toHaveClass(`pkt-card--${layout}`)
        unmount()
      }
    })

    test('applies different padding properties correctly', () => {
      const paddingOptions = ['none', 'default'] as const
      for (const padding of paddingOptions) {
        const { container, unmount } = render(<PktCard padding={padding}>innhold</PktCard>)
        const article = container.querySelector('article')
        expect(article).toHaveClass(`pkt-card--padding-${padding}`)
        unmount()
      }
    })

    test('handles borderOnHover property correctly', () => {
      const { container } = render(<PktCard borderOnHover={false}>innhold</PktCard>)
      const article = container.querySelector('article')
      expect(article).not.toHaveClass('pkt-card--border-on-hover')
    })

    test('supports custom className', () => {
      const { container } = render(<PktCard className="custom-class">innhold</PktCard>)
      const wrapper = container.querySelector('.pkt-card-root')
      expect(wrapper).toHaveClass('custom-class')
    })
  })

  describe('Heading functionality', () => {
    test('renders heading when provided', () => {
      const { container } = render(<PktCard heading="Test Card Title">innhold</PktCard>)
      const heading = container.querySelector('.pkt-card__heading')
      expect(heading).toBeInTheDocument()
      expect(heading?.textContent?.trim()).toBe('Test Card Title')
    })

    test('renders subheading when provided', () => {
      const { container } = render(<PktCard subheading="Test Subheading">innhold</PktCard>)
      const subheading = container.querySelector('.pkt-card__subheading')
      expect(subheading).toBeInTheDocument()
      expect(subheading?.textContent).toBe('Test Subheading')
    })

    test('applies correct heading level', () => {
      const { container } = render(
        <PktCard heading="Test" headingLevel={2}>innhold</PktCard>,
      )
      const heading = container.querySelector('h2.pkt-card__heading')
      expect(heading).toBeInTheDocument()
    })

    test('defaults to h3 heading level', () => {
      const { container } = render(<PktCard heading="Test">innhold</PktCard>)
      const heading = container.querySelector('h3.pkt-card__heading')
      expect(heading).toBeInTheDocument()
    })

    test('does not render header when no heading or subheading', () => {
      const { container } = render(<PktCard>innhold</PktCard>)
      const header = container.querySelector('.pkt-card__header')
      expect(header).not.toBeInTheDocument()
    })
  })

  describe('Link functionality', () => {
    test('renders as regular card when no clickCardLink', () => {
      const { container } = render(<PktCard heading="Test Title">innhold</PktCard>)
      const link = container.querySelector('.pkt-card__link')
      expect(link).not.toBeInTheDocument()

      const article = container.querySelector('article')
      expect(article?.getAttribute('aria-label')).toBe('Test Title')
    })

    test('renders as link card when clickCardLink provided', () => {
      const { container } = render(
        <PktCard heading="Test Title" clickCardLink="/test-url">innhold</PktCard>,
      )
      const linkHeading = container.querySelector('.pkt-card__link-heading')
      const link = container.querySelector('.pkt-card__link')
      expect(linkHeading).toBeInTheDocument()
      expect(link).toBeInTheDocument()
      expect(link?.getAttribute('href')).toBe('/test-url')
      expect(link?.textContent).toBe('Test Title')

      const article = container.querySelector('article')
      expect(article?.getAttribute('aria-label')).toBe('Test Title lenkekort')
    })

    test('does not render plain heading when clickCardLink is set', () => {
      const { container } = render(
        <PktCard heading="Test" clickCardLink="/test">innhold</PktCard>,
      )
      const headings = container.querySelectorAll('.pkt-card__heading')
      // Should only have the link heading, not both
      expect(headings).toHaveLength(1)
      expect(headings[0]).toHaveClass('pkt-card__link-heading')
    })

    test('handles openLinkInNewTab correctly', () => {
      const { container } = render(
        <PktCard heading="Test" clickCardLink="/test" openLinkInNewTab>innhold</PktCard>,
      )
      const link = container.querySelector('.pkt-card__link')
      expect(link?.getAttribute('target')).toBe('_blank')
      expect(link?.getAttribute('rel')).toBe('noopener noreferrer')
    })

    test('applies correct aria-label for link cards with custom ariaLabel', () => {
      const { container } = render(
        <PktCard clickCardLink="/test" ariaLabel="Custom aria label">innhold</PktCard>,
      )
      const article = container.querySelector('article')
      expect(article?.getAttribute('aria-label')).toBe('Custom aria label')
    })
  })

  describe('Image functionality', () => {
    test('renders image when provided', () => {
      const { container } = render(
        <PktCard image={{ src: '/test-image.jpg', alt: 'Test image' }}>innhold</PktCard>,
      )
      const imageDiv = container.querySelector('.pkt-card__image')
      const img = imageDiv?.querySelector('img')
      expect(imageDiv).toBeInTheDocument()
      expect(img).toBeInTheDocument()
      expect(img?.getAttribute('src')).toBe('/test-image.jpg')
      expect(img?.getAttribute('alt')).toBe('Test image')
    })

    test('does not render image when not provided', () => {
      const { container } = render(<PktCard>innhold</PktCard>)
      const imageDiv = container.querySelector('.pkt-card__image')
      expect(imageDiv).not.toBeInTheDocument()
    })

    test('applies correct image shape classes', () => {
      const shapes = ['square', 'round'] as const
      for (const shape of shapes) {
        const { container, unmount } = render(
          <PktCard image={{ src: '/test.jpg', alt: 'Test' }} imageShape={shape}>innhold</PktCard>,
        )
        const imageDiv = container.querySelector('.pkt-card__image')
        expect(imageDiv).toHaveClass(`pkt-card__image-${shape}`)
        unmount()
      }
    })
  })

  describe('Tags functionality', () => {
    test('renders tags when provided', () => {
      const tags = [
        { text: 'Tag 1', skin: 'blue' as const },
        { text: 'Tag 2', skin: 'green' as const },
      ]
      const { container } = render(<PktCard tags={tags}>innhold</PktCard>)
      const tagsContainer = container.querySelector('.pkt-card__tags')
      expect(tagsContainer).toBeInTheDocument()
      expect(tagsContainer?.getAttribute('aria-label')).toBe('merkelapper')
    })

    test('renders single tag with correct aria-label', () => {
      const tags = [{ text: 'Single Tag' }]
      const { container } = render(<PktCard tags={tags}>innhold</PktCard>)
      const tagsContainer = container.querySelector('.pkt-card__tags')
      expect(tagsContainer?.getAttribute('aria-label')).toBe('merkelapp')
    })

    test('applies correct tag position classes', () => {
      const positions = ['top', 'bottom'] as const
      for (const position of positions) {
        const tags = [{ text: 'Test Tag' }]
        const { container, unmount } = render(
          <PktCard tags={tags} tagPosition={position}>innhold</PktCard>,
        )
        const tagsContainer = container.querySelector('.pkt-card__tags')
        expect(tagsContainer).toHaveClass(`pkt-card__tags-${position}`)
        unmount()
      }
    })

    test('does not render tags when array is empty', () => {
      const { container } = render(<PktCard>innhold</PktCard>)
      const tagsContainer = container.querySelector('.pkt-card__tags')
      expect(tagsContainer).not.toBeInTheDocument()
    })
  })

  describe('Metadata functionality', () => {
    test('renders metadata when provided', () => {
      const { container } = render(
        <PktCard metaLead="Author Name" metaTrail="2023-12-01">innhold</PktCard>,
      )
      const metadata = container.querySelector('.pkt-card__metadata')
      const metaLead = metadata?.querySelector('.pkt-card__metadata-lead')
      const metaTrail = metadata?.querySelector('.pkt-card__metadata-trail')

      expect(metadata).toBeInTheDocument()
      expect(metaLead?.textContent).toBe('Author Name')
      expect(metaTrail?.textContent).toBe('2023-12-01')
    })

    test('renders only metaLead when metaTrail not provided', () => {
      const { container } = render(<PktCard metaLead="Author Only">innhold</PktCard>)
      const metaLead = container.querySelector('.pkt-card__metadata-lead')
      const metaTrail = container.querySelector('.pkt-card__metadata-trail')
      expect(metaLead).toBeInTheDocument()
      expect(metaTrail).not.toBeInTheDocument()
    })

    test('renders only metaTrail when metaLead not provided', () => {
      const { container } = render(<PktCard metaTrail="Date Only">innhold</PktCard>)
      const metaLead = container.querySelector('.pkt-card__metadata-lead')
      const metaTrail = container.querySelector('.pkt-card__metadata-trail')
      expect(metaLead).not.toBeInTheDocument()
      expect(metaTrail).toBeInTheDocument()
    })

    test('does not render metadata when neither provided', () => {
      const { container } = render(<PktCard>innhold</PktCard>)
      const metadata = container.querySelector('.pkt-card__metadata')
      expect(metadata).not.toBeInTheDocument()
    })
  })

  describe('Content placement and structure', () => {
    test('renders content elements in correct order', () => {
      const tags = [{ text: 'Test Tag' }]
      const { container } = render(
        <PktCard
          heading="Test Title"
          subheading="Test Sub"
          tags={tags}
          image={{ src: '/test.jpg', alt: 'Test' }}
          metaLead="Author"
          metaTrail="Date"
        >
          innhold
        </PktCard>,
      )
      const article = container.querySelector('article')
      const children = Array.from(article?.children || [])

      // Image first, then wrapper
      expect(children[0]).toHaveClass('pkt-card__image')
      expect(children[1]).toHaveClass('pkt-card__wrapper')

      const wrapperChildren = Array.from(children[1]?.children || [])

      // Order within wrapper: tags (top), header, content, metadata
      expect(wrapperChildren[0]).toHaveClass('pkt-card__tags-top')
      expect(wrapperChildren[1]).toHaveClass('pkt-card__header')
      expect(wrapperChildren[2]).toHaveClass('pkt-card__content')
      expect(wrapperChildren[3]).toHaveClass('pkt-card__metadata')
    })

    test('places tags at bottom when tagPosition is bottom', () => {
      const tags = [{ text: 'Test Tag' }]
      const { container } = render(
        <PktCard heading="Test Title" tags={tags} tagPosition="bottom">innhold</PktCard>,
      )
      const wrapperChildren = Array.from(
        container.querySelector('.pkt-card__wrapper')?.children || [],
      )

      // Order: header, content, tags (bottom)
      expect(wrapperChildren[0]).toHaveClass('pkt-card__header')
      expect(wrapperChildren[1]).toHaveClass('pkt-card__content')
      expect(wrapperChildren[2]).toHaveClass('pkt-card__tags-bottom')
    })
  })

  describe('Accessibility', () => {
    test('has no accessibility violations', async () => {
      const { container } = render(<PktCard heading="Accessible Card">innhold</PktCard>)
      const results = await axe(container)
      expect(results).toHaveNoViolations()
    })

    test('applies custom aria-label', () => {
      const { container } = render(
        <PktCard heading="Test" ariaLabel="Custom accessible label">innhold</PktCard>,
      )
      const article = container.querySelector('article')
      expect(article?.getAttribute('aria-label')).toBe('Custom accessible label')
    })

    test('falls back to heading for aria-label', () => {
      const { container } = render(<PktCard heading="Default Aria Label">innhold</PktCard>)
      const article = container.querySelector('article')
      expect(article?.getAttribute('aria-label')).toBe('Default Aria Label')
    })

    test('falls back to "kort" when no heading or aria-label', () => {
      const { container } = render(<PktCard>innhold</PktCard>)
      const article = container.querySelector('article')
      expect(article?.getAttribute('aria-label')).toBe('kort')
    })
  })
})
