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 type { CustomElementFor } from '../../tests/component-registry'
import './breadcrumbs'

expect.extend(toHaveNoViolations)

interface BreadcrumbsTestConfig extends BaseTestConfig {
  breadcrumbs?: { text: string; href: string }[]
}

const defaultBreadcrumbs = [
  { text: 'Hjem', href: '/' },
  { text: 'Produkter', href: '/produkter' },
  { text: 'Detaljer', href: '/produkter/detaljer' },
]

const createBreadcrumbsTest = async (config: BreadcrumbsTestConfig = {}) => {
  const { container, element } = await createElementTest<
    CustomElementFor<'pkt-breadcrumbs'>,
    BreadcrumbsTestConfig
  >('pkt-breadcrumbs', config)

  if (config.breadcrumbs) {
    element.breadcrumbs = config.breadcrumbs
    await element.updateComplete
  }

  return { container, breadcrumbs: element }
}

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

describe('PktBreadcrumbs', () => {
  describe('Rendering', () => {
    test('renders without errors', async () => {
      const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
      expect(breadcrumbs).toBeInTheDocument()
    })

    test('renders all breadcrumb items', async () => {
      const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
      const items = breadcrumbs.querySelectorAll('.pkt-breadcrumbs__item')
      expect(items).toHaveLength(3)
    })

    test('renders nothing when breadcrumbs array is empty', async () => {
      const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: [] })
      const nav = breadcrumbs.querySelector('nav')
      expect(nav).not.toBeInTheDocument()
    })

    test('renders correctly when breadcrumbs are set as JSON attribute', async () => {
      const container = document.createElement('div')
      document.body.appendChild(container)
      container.innerHTML = `<pkt-breadcrumbs breadcrumbs='${JSON.stringify(defaultBreadcrumbs)}'></pkt-breadcrumbs>`

      const element = container.querySelector('pkt-breadcrumbs') as CustomElementFor<'pkt-breadcrumbs'>
      await customElements.whenDefined('pkt-breadcrumbs')
      await element.updateComplete

      const items = element.querySelectorAll('.pkt-breadcrumbs__item')
      expect(items).toHaveLength(3)

      const links = element.querySelectorAll('.pkt-breadcrumbs__link')
      expect(links).toHaveLength(2)
      expect(links[0]).toHaveAttribute('href', '/')
    })

    test('last item has aria-current and no link', async () => {
      const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
      const items = breadcrumbs.querySelectorAll('.pkt-breadcrumbs__item')
      const lastItem = items[items.length - 1]

      const span = lastItem.querySelector('[aria-current="true"]')
      expect(span).toBeInTheDocument()

      const link = lastItem.querySelector('a')
      expect(link).not.toBeInTheDocument()
    })

    test('non-last items render as links with correct href', async () => {
      const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
      const links = breadcrumbs.querySelectorAll('.pkt-breadcrumbs__link')

      expect(links).toHaveLength(2)
      expect(links[0]).toHaveAttribute('href', '/')
      expect(links[1]).toHaveAttribute('href', '/produkter')
    })

    test('renders breadcrumb text correctly', async () => {
      const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
      const texts = breadcrumbs.querySelectorAll('.pkt-breadcrumbs__text')

      // 3 desktop + 1 mobile back-link
      expect(texts).toHaveLength(4)
      expect(texts[0]).toHaveTextContent('Hjem')
      expect(texts[1]).toHaveTextContent('Produkter')
      expect(texts[2]).toHaveTextContent('Detaljer')
    })

    test('renders desktop list with correct classes', async () => {
      const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
      const list = breadcrumbs.querySelector('.pkt-breadcrumbs--desktop')
      expect(list).toBeInTheDocument()
    })

    test('renders mobile back-link pointing to second-to-last item', async () => {
      const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
      const mobileLink = breadcrumbs.querySelector('.pkt-breadcrumbs--mobile')

      expect(mobileLink).toBeInTheDocument()
      expect(mobileLink).toHaveAttribute('href', '/produkter')
      expect(mobileLink).toHaveTextContent('Produkter')
    })

    test('renders chevron-right icons on desktop links', async () => {
      const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
      const desktopIcons = breadcrumbs.querySelectorAll(
        '.pkt-breadcrumbs--desktop pkt-icon[name="chevron-thin-right"]',
      )
      expect(desktopIcons).toHaveLength(2)
    })

    test('renders chevron-left icon on mobile back-link', async () => {
      const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
      const mobileIcon = breadcrumbs.querySelector(
        '.pkt-breadcrumbs--mobile pkt-icon[name="chevron-thin-left"]',
      )
      expect(mobileIcon).toBeInTheDocument()
    })
  })

  describe('Events', () => {
    test('dispatches navigate event on link click', async () => {
      const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
      const navigateSpy = vi.fn()
      breadcrumbs.addEventListener('navigate', navigateSpy)

      const link = breadcrumbs.querySelector('.pkt-breadcrumbs__link') as HTMLAnchorElement
      fireEvent.click(link)

      expect(navigateSpy).toHaveBeenCalledTimes(1)
      expect(navigateSpy.mock.calls[0][0].detail.item).toEqual({ text: 'Hjem', href: '/' })
      expect(navigateSpy.mock.calls[0][0].detail.index).toBe(0)
    })

    test('dispatches navigate event on mobile back-link click', async () => {
      const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
      const navigateSpy = vi.fn()
      breadcrumbs.addEventListener('navigate', navigateSpy)

      const mobileLink = breadcrumbs.querySelector('.pkt-breadcrumbs--mobile') as HTMLAnchorElement
      fireEvent.click(mobileLink)

      expect(navigateSpy).toHaveBeenCalledTimes(1)
      expect(navigateSpy.mock.calls[0][0].detail.item).toEqual({
        text: 'Produkter',
        href: '/produkter',
      })
      expect(navigateSpy.mock.calls[0][0].detail.index).toBe(1)
    })

    test('preventDefault on navigate event prevents default link behavior', async () => {
      const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
      breadcrumbs.addEventListener('navigate', (e: Event) => {
        e.preventDefault()
      })

      const link = breadcrumbs.querySelector('.pkt-breadcrumbs__link') as HTMLAnchorElement
      const clickEvent = new MouseEvent('click', { bubbles: true, cancelable: true })
      const preventDefaultSpy = vi.spyOn(clickEvent, 'preventDefault')

      link.dispatchEvent(clickEvent)

      expect(preventDefaultSpy).toHaveBeenCalled()
    })
  })

  describe('Accessibility', () => {
    test('has nav with aria-label', async () => {
      const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
      const nav = breadcrumbs.querySelector('nav')
      expect(nav).toHaveAttribute('aria-label', 'brødsmulemeny')
    })

    test('has no WCAG violations', async () => {
      // Suppress jsdom "Not implemented: navigation" error triggered by axe-core
      const originalError = console.error
      console.error = (...args: unknown[]) => {
        if (typeof args[0] === 'string' && args[0].includes('Not implemented: navigation')) return
        originalError(...args)
      }

      const { container } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
      const results = await axe(container)
      expect(results).toHaveNoViolations()

      console.error = originalError
    })
  })
})
