import '@testing-library/jest-dom'

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

import { PktSearchInput } from './SearchInput'

expect.extend(toHaveNoViolations)

const Wrapper = (props: { onChange: (e: ChangeEvent) => void }) => {
  const [value, setValue] = useState('')
  return (
    <PktSearchInput
      id="test-search-input"
      label="Test Label"
      value={value}
      onChange={props.onChange}
      onSearch={(value: string) => setValue(value)}
    />
  )
}

describe('PktSearchInput', () => {
  it('renders with form element when action prop exists', async () => {
    const { container } = render(<PktSearchInput id="test-search-input" action="/search" />)
    expect(container.querySelector('form')).toBeInTheDocument()
  })

  it('renders with div element when action prop does not exist', async () => {
    const { container } = render(<PktSearchInput id="test-search-input" />)
    expect(container.querySelector('div')).toBeInTheDocument()
    expect(container.querySelector('form')).not.toBeInTheDocument()
  })

  it('renders with label element when label prop exists', async () => {
    const { container } = render(<PktSearchInput id="test-search-input" label="Test Label" />)
    expect(container.querySelector('label')).toBeInTheDocument()
  })

  it('renders with div element when label prop does not exist', async () => {
    const { container } = render(<PktSearchInput id="test-search-input" />)
    expect(container.querySelector('div')).toBeInTheDocument()
    expect(container.querySelector('label')).not.toBeInTheDocument()
  })

  it('calls onSearch prop with input value on button click', async () => {
    const mockOnSearch = vi.fn()
    const { getByRole, getByPlaceholderText } = render(<Wrapper onChange={mockOnSearch} />)

    const inputElement = getByPlaceholderText('Søk…')
    const buttonElement = getByRole('button')

    // Type a value in the input
    fireEvent.input(inputElement, { target: { value: 'test value' } })

    // Click the button
    fireEvent.click(buttonElement)

    // Check if onSearch was called with the correct value
    expect(mockOnSearch).toHaveBeenCalledWith(
      expect.objectContaining({ target: expect.objectContaining({ value: 'test value' }) }),
    )
  })

  it('renders nonInteractive suggestion as a div, not a button', async () => {
    render(
      <PktSearchInput
        id="test-search-input"
        suggestions={[{ text: 'Ingen resultater', nonInteractive: true }]}
      />,
    )

    const row = screen.getByText('Ingen resultater').closest('.pkt-searchinput__suggestion')
    expect(row?.tagName.toLowerCase()).toBe('div')
    expect(screen.queryAllByRole('button', { name: /ingen resultater/i })).toHaveLength(0)
  })

  it('renders search suggestions when suggestions prop exists', async () => {
    const suggestions = [
      { title: 'Suggestion 1', onClick: vi.fn() },
      { title: 'Suggestion 2', onClick: vi.fn() },
    ]

    render(<PktSearchInput id="test-search-input" suggestions={suggestions} />)

    // Check if suggestion elements are rendered
    suggestions.forEach((suggestion) => {
      expect(screen.getByText(suggestion.title)).toBeInTheDocument()
    })
  })

  it('applies inputSize classes on wrapper, input and search button', () => {
    const { container } = render(
      <PktSearchInput id="test-search-input" appearance="local-with-button" inputSize="small" />,
    )

    expect(container.querySelector('.pkt-searchinput--small')).toBeTruthy()
    expect(container.querySelector('.pkt-input--small')).toBeTruthy()
    expect(container.querySelector('.pkt-searchinput__button.pkt-btn--small')).toBeTruthy()
  })

  describe('accessibility', () => {
    it('renders with no wcag errors with axe', async () => {
      const suggestions = [
        { title: 'Suggestion 1', onClick: vi.fn() },
        { title: 'Suggestion 2', onClick: vi.fn() },
      ]
      const { container } = render(<PktSearchInput id="test-search-input" label="My label" suggestions={suggestions} />)
      const results = await axe(container)
      expect(results).toHaveNoViolations()
    })
  })
})
