import '@testing-library/jest-dom'

import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import { axe, toHaveNoViolations } from 'jest-axe'
import { vi } from 'vitest'
import { createRef } from 'react'

import { PktAccordion } from './Accordion'
import { PktAccordionItem } from './AccordionItem'

expect.extend(toHaveNoViolations)

describe('PktAccordion', () => {
  test('renders without errors', () => {
    render(
      <PktAccordion>
        <PktAccordionItem id="render-id" title="test">
          content
        </PktAccordionItem>
      </PktAccordion>,
    )
    // Assert that the Accordion component renders without throwing any errors
  })

  test('renders children', () => {
    const mockToggleOpen = vi.fn()
    render(
      <PktAccordion>
        <PktAccordionItem title="Title 1" id="item1" onClick={mockToggleOpen}>
          Content 1
        </PktAccordionItem>
        <PktAccordionItem title="Title 2" id="item2" onClick={mockToggleOpen}>
          Content 2
        </PktAccordionItem>
      </PktAccordion>,
    )

    // Assert that the Accordion component renders its children correctly
    expect(screen.getByText('Title 1')).toBeInTheDocument()
    expect(screen.getByText('Content 1')).toBeInTheDocument()
    expect(screen.getByText('Title 2')).toBeInTheDocument()
    expect(screen.getByText('Content 2')).toBeInTheDocument()
  })

  test('applies compact and skin classes', () => {
    const { container } = render(
      <>
        <h3 id="accordion-heading">Accordion Heading</h3>
        <PktAccordion compact skin="blue" ariaLabelledBy="accordion-heading" data-testid="accordion">
          <PktAccordionItem title="Title" id="item1">
            Content
          </PktAccordionItem>
        </PktAccordion>
      </>,
    )

    const accordion = container.querySelector('[data-testid="pkt-accordion"]')!
    expect(accordion).toBeInTheDocument()
    expect(accordion).toHaveClass('pkt-accordion')
    expect(accordion).toHaveClass('pkt-accordion--compact')
    expect(accordion).toHaveClass('pkt-accordion--blue')
    expect(accordion).toHaveAttribute('aria-labelledby', 'accordion-heading')
  })

  test('forwards ref correctly', () => {
    const ref = createRef<HTMLDivElement>()
    render(
      <PktAccordion ref={ref}>
        <PktAccordionItem title="Title" id="item1">
          Content
        </PktAccordionItem>
      </PktAccordion>,
    )

    expect(ref.current).toBeDefined()
    expect(ref.current?.tagName).toBe('DIV')
    expect(ref.current).toHaveClass('pkt-accordion')
  })

  test('propagates plus-minus skin to items', () => {
    const { container } = render(
      <PktAccordion skin="plus-minus">
        <PktAccordionItem title="Title" id="item1">
          Content
        </PktAccordionItem>
      </PktAccordion>,
    )

    expect(container.querySelector('.pkt-accordion-item__icon--plus')).toBeInTheDocument()
    expect(container.querySelector('.pkt-accordion-item__icon--minus')).toBeInTheDocument()
    expect(container.querySelector('[name="chevron-thin-down"]')).not.toBeInTheDocument()
  })

  test('updates item icons when skin changes', () => {
    const { container, rerender } = render(
      <PktAccordion skin="outlined">
        <PktAccordionItem title="Title" id="item1">
          Content
        </PktAccordionItem>
      </PktAccordion>,
    )

    expect(container.querySelector('[name="chevron-thin-down"]')).toBeInTheDocument()
    expect(container.querySelector('.pkt-accordion-item__icon--plus')).not.toBeInTheDocument()

    rerender(
      <PktAccordion skin="plus-minus">
        <PktAccordionItem title="Title" id="item1">
          Content
        </PktAccordionItem>
      </PktAccordion>,
    )

    expect(container.querySelector('[name="chevron-thin-down"]')).not.toBeInTheDocument()
    expect(container.querySelector('.pkt-accordion-item__icon--plus')).toBeInTheDocument()
  })

  test('item skin prop overrides accordion skin', () => {
    const { container } = render(
      <PktAccordion skin="plus-minus">
        <PktAccordionItem title="Title" id="item1" skin="outlined">
          Content
        </PktAccordionItem>
      </PktAccordion>,
    )

    expect(container.querySelector('[name="chevron-thin-down"]')).toBeInTheDocument()
    expect(container.querySelector('.pkt-accordion-item__icon--plus')).not.toBeInTheDocument()
  })
})

describe('PktAccordionItem', () => {
  test('applies compact and skin classes', () => {
    const { container } = render(
      <PktAccordionItem title="Title" id="item1" skin="blue" compact>
        Content
      </PktAccordionItem>,
    )

    const details = container.querySelector('details')!
    expect(details).toBeInTheDocument()
    expect(details).toHaveClass('pkt-accordion-item')
    expect(details).toHaveClass('pkt-accordion-item--blue')
    expect(details).toHaveClass('pkt-accordion-item--compact')
  })

  test('renders with correct structure', () => {
    const { container } = render(
      <PktAccordionItem title="Test Title" id="test-item">
        Test Content
      </PktAccordionItem>,
    )

    const details = container.querySelector('details')!
    const summary = details.querySelector('summary')!
    const content = details.querySelector('.pkt-accordion-item__content')!
    const contentInner = content.querySelector('.pkt-accordion-item__content-inner')!

    expect(details).toHaveAttribute('id', 'test-item')
    expect(summary).toHaveClass('pkt-accordion-item__title')
    expect(summary).toHaveAttribute('id', 'pkt-accordion-item-summary-test-item')
    expect(summary).toHaveTextContent('Test Title')
    expect(content).toHaveAttribute('id', 'pkt-accordion-item__content-test-item')
    expect(content).toHaveAttribute('role', 'region')
    expect(contentInner).toHaveTextContent('Test Content')
  })

  test('handles defaultOpen prop', () => {
    const { container } = render(
      <PktAccordionItem title="Title" id="item1" defaultOpen>
        Content
      </PktAccordionItem>,
    )

    const details = container.querySelector('details')!
    expect(details).toHaveAttribute('open')
  })

  test('handles controlled isOpen prop', () => {
    const { container, rerender } = render(
      <PktAccordionItem title="Title" id="item1" isOpen={false}>
        Content
      </PktAccordionItem>,
    )

    const details = container.querySelector('details')!
    expect(details).not.toHaveAttribute('open')

    rerender(
      <PktAccordionItem title="Title" id="item1" isOpen={true}>
        Content
      </PktAccordionItem>,
    )

    expect(details).toHaveAttribute('open')
  })

  test('calls onClick handler', () => {
    const mockOnClick = vi.fn()
    const { container } = render(
      <PktAccordionItem title="Title" id="item1" onClick={mockOnClick}>
        Content
      </PktAccordionItem>,
    )

    const details = container.querySelector('details')!
    fireEvent.click(details)

    // onClick is called after a setTimeout, so we need to wait
    setTimeout(() => {
      expect(mockOnClick).toHaveBeenCalledTimes(1)
    }, 10)
  })

  test('forwards ref correctly', () => {
    const ref = createRef<HTMLDetailsElement>()
    render(
      <PktAccordionItem ref={ref} title="Title" id="item1">
        Content
      </PktAccordionItem>,
    )

    expect(ref.current).toBeDefined()
    expect(ref.current?.tagName).toBe('DETAILS')
    expect(ref.current).toHaveClass('pkt-accordion-item')
  })

  test('renders PktIcon', async () => {
    const { container } = render(
      <PktAccordionItem title="Title" id="item1">
        Content
      </PktAccordionItem>,
    )

    const icon = container.querySelector('.pkt-icon')
    expect(icon).toBeInTheDocument()
    expect(icon).toHaveAttribute('name', 'chevron-thin-down')
    expect(icon).toHaveClass('pkt-accordion-item__icon')
    await waitFor(() =>
      expect(icon?.querySelector('svg')).toHaveAttribute('aria-hidden', 'true'),
    )
  })
})

describe('accessibility', () => {
  it('renders with no wcag errors with axe', async () => {
    const { container } = render(
      <PktAccordion>
        <PktAccordionItem title="Title 1" id="item1">
          Content 1
        </PktAccordionItem>
        <PktAccordionItem title="Title 2" id="item2">
          Content 2
        </PktAccordionItem>
      </PktAccordion>,
    )
    const results = await axe(container)
    expect(results).toHaveNoViolations()
  })
})
