import '@testing-library/jest-dom'

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

import { IPktDatepicker, PktDatepicker } from './Datepicker'

expect.extend(toHaveNoViolations)

const datePickerId = 'datepickerId'
const label = 'Date Picker Label'

const createDatepickerTest = (props: Partial<IPktDatepicker> = {}) => {
  const defaultProps: IPktDatepicker = {
    label,
    id: datePickerId,
    ...props,
  }

  return render(<PktDatepicker {...defaultProps} />)
}

describe('PktDatepicker', () => {
  describe('Event handling', () => {
    test('dispatches onChange event when value changes', () => {
      const handleChange = vi.fn()
      const { container } = createDatepickerTest({
        onChange: handleChange,
      })

      const input = container.querySelector('input[type="date"]') as HTMLInputElement
      fireEvent.change(input, { target: { value: '2024-06-15' } })

      expect(handleChange).toHaveBeenCalled()
    })

    test('dispatches onValueChange event when value changes', () => {
      const handleValueChange = vi.fn()
      const { container } = createDatepickerTest({
        onValueChange: handleValueChange,
      })

      const input = container.querySelector('input[type="date"]') as HTMLInputElement
      fireEvent.change(input, { target: { value: '2024-06-15' } })

      expect(handleValueChange).toHaveBeenCalledWith(['2024-06-15'])
    })

    test('input can receive focus', () => {
      const { container } = createDatepickerTest()

      const input = container.querySelector('input[type="date"]') as HTMLInputElement
      input.focus()

      expect(document.activeElement).toBe(input)
    })

    test('input can be blurred', () => {
      const { container } = createDatepickerTest()

      const input = container.querySelector('input[type="date"]') as HTMLInputElement
      input.focus()
      expect(document.activeElement).toBe(input)

      input.blur()
      expect(document.activeElement).not.toBe(input)
    })

    test('dispatches both onChange and onValueChange', () => {
      const handleChange = vi.fn()
      const handleValueChange = vi.fn()
      const { container } = createDatepickerTest({
        onChange: handleChange,
        onValueChange: handleValueChange,
      })

      const input = container.querySelector('input[type="date"]') as HTMLInputElement
      fireEvent.change(input, { target: { value: '2024-06-15' } })

      expect(handleChange).toHaveBeenCalled()
      expect(handleValueChange).toHaveBeenCalledWith(['2024-06-15'])
    })
  })

  describe('Accessibility', () => {
    test('has no accessibility violations with single date set', async () => {
      const { container } = createDatepickerTest({
        value: '2024-06-15',
        helptext: 'Choose a date from the calendar',
      })

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

    test('has no accessibility violations with range dates set', async () => {
      const { container } = createDatepickerTest({
        range: true,
        value: ['2024-06-15', '2024-06-25'],
        helptext: 'Choose start and end dates',
      })

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

    test('has no accessibility violations with multiple dates', async () => {
      const { container } = createDatepickerTest({
        multiple: true,
        value: ['2024-06-15', '2024-06-20', '2024-06-25'],
        helptext: 'Choose multiple dates',
      })

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

    test('has no accessibility violations when disabled', async () => {
      const { container } = createDatepickerTest({
        disabled: true,
        helptext: 'This field is disabled',
      })

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

    test('has no accessibility violations with error state', async () => {
      const { container } = createDatepickerTest({
        hasError: true,
        errorMessage: 'Please enter a valid date',
        helptext: 'Required field',
      })

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

    test('associates label with input correctly', () => {
      const { container } = createDatepickerTest()

      const labelEl = container.querySelector('label')
      expect(labelEl).toBeInTheDocument()
      expect(labelEl).toHaveAttribute('for', `${datePickerId}-input`)

      const input = container.querySelector(`#${datePickerId}-input`)
      expect(input).toBeInTheDocument()
    })

    test('has proper ARIA attributes for calendar button', () => {
      const { container } = createDatepickerTest()

      const button = container.querySelector('button[type="button"]')
      expect(button).toBeInTheDocument()
      expect(button).toHaveAttribute('aria-label', 'Åpne kalender')
    })

    test('input has aria-describedby when helptext is present', () => {
      const { container } = createDatepickerTest({
        helptext: 'Some help text',
      })

      const input = container.querySelector('input[type="date"]') as HTMLInputElement
      expect(input).toHaveAttribute('aria-describedby', `${datePickerId}-helptext`)
    })

    test('input does not have aria-describedby when no helptext', () => {
      const { container } = createDatepickerTest()

      const input = container.querySelector('input[type="date"]') as HTMLInputElement
      expect(input).not.toHaveAttribute('aria-describedby')
    })

    test('input has aria-invalid when hasError is true', () => {
      const { container } = createDatepickerTest({
        hasError: true,
        errorMessage: 'Invalid date',
      })

      const input = container.querySelector('input[type="date"]') as HTMLInputElement
      expect(input).toHaveAttribute('aria-invalid', 'true')
    })

    test('input has aria-errormessage when hasError is true', () => {
      const { container } = createDatepickerTest({
        hasError: true,
        errorMessage: 'Invalid date',
      })

      const input = container.querySelector('input[type="date"]') as HTMLInputElement
      expect(input).toHaveAttribute('aria-errormessage', `${datePickerId}-error`)
    })

    test('input does not have aria-invalid when no error', () => {
      const { container } = createDatepickerTest()

      const input = container.querySelector('input[type="date"]') as HTMLInputElement
      expect(input).not.toHaveAttribute('aria-invalid', 'true')
    })

    test('calendar popup has aria-hidden when closed', () => {
      const { container } = createDatepickerTest()

      const popup = container.querySelector('.pkt-calendar-popup')
      expect(popup).toHaveAttribute('aria-hidden', 'true')
    })

    test('calendar popup has aria-hidden false when open', () => {
      const { container } = createDatepickerTest()

      const button = container.querySelector('button[type="button"]') as HTMLButtonElement
      fireEvent.click(button)

      const popup = container.querySelector('.pkt-calendar-popup')
      expect(popup).toHaveAttribute('aria-hidden', 'false')
    })

    test('date tags container has aria-live polite', () => {
      const { container } = createDatepickerTest({
        multiple: true,
        value: ['2024-06-15'],
      })

      const tagsContainer = container.querySelector('.pkt-date-tags')
      expect(tagsContainer).toHaveAttribute('aria-live', 'polite')
    })

    test('handles focus management - button can be focused', () => {
      const { container } = createDatepickerTest()

      const button = container.querySelector('button[type="button"]') as HTMLButtonElement
      button.focus()

      expect(document.activeElement).toBe(button)
    })

    test('supports keyboard-only interaction to open and close calendar', () => {
      const { container } = createDatepickerTest()

      const input = container.querySelector('input[type="date"]') as HTMLInputElement

      // Open with Space
      fireEvent.keyDown(input, { key: ' ' })
      let popup = container.querySelector('.pkt-calendar-popup')
      expect(popup).not.toHaveAttribute('hidden')

      // Close with Escape
      fireEvent.keyDown(document, { key: 'Escape' })
      popup = container.querySelector('.pkt-calendar-popup')
      expect(popup).toHaveAttribute('hidden')
    })
  })

  describe('Localization', () => {
    test('uses custom strings for calendar button', () => {
      const { container } = createDatepickerTest({
        strings: {
          calendar: { buttonAltText: 'Open calendar' },
        },
      })

      const button = container.querySelector('button[type="button"]')
      expect(button).toHaveAttribute('aria-label', 'Open calendar')
    })

    test('uses custom strings for range labels', () => {
      const { container } = createDatepickerTest({
        range: true,
        showRangeLabels: true,
        strings: {
          generic: { from: 'From', to: 'To' },
          calendar: { buttonAltText: 'Open calendar' },
        },
      })

      const prefix = container.querySelector('.pkt-input-prefix')
      expect(prefix).toHaveTextContent('From')
    })

    test('uses default Norwegian strings', () => {
      const { container } = createDatepickerTest({
        range: true,
        showRangeLabels: true,
      })

      const button = container.querySelector('button[type="button"]')
      expect(button).toHaveAttribute('aria-label', 'Åpne kalender')

      const prefix = container.querySelector('.pkt-input-prefix')
      expect(prefix).toHaveTextContent('Fra')
    })
  })

  describe('InputWrapper integration', () => {
    test('renders helptext', () => {
      createDatepickerTest({
        helptext: 'Please select a date',
      })

      expect(screen.getByText('Please select a date')).toBeInTheDocument()
    })

    test('renders error message when hasError is true', () => {
      createDatepickerTest({
        hasError: true,
        errorMessage: 'Date is required',
      })

      expect(screen.getByText('Date is required')).toBeInTheDocument()
    })

    test('renders optional tag', () => {
      const { container } = createDatepickerTest({
        optionalTag: true,
        optionalText: 'Valgfritt',
      })

      expect(container.textContent).toContain('Valgfritt')
    })

    test('renders required tag', () => {
      const { container } = createDatepickerTest({
        requiredTag: true,
        requiredText: 'Må fylles ut',
      })

      expect(container.textContent).toContain('Må fylles ut')
    })

    test('applies fullwidth class to input', () => {
      const { container } = createDatepickerTest({
        fullwidth: true,
      })

      const input = container.querySelector('.pkt-datepicker__input') as HTMLInputElement
      expect(input).toHaveClass('pkt-input--fullwidth')
    })
  })

  describe('CSS classes', () => {
    test('applies pkt-datepicker class to outer wrapper', () => {
      const { container } = createDatepickerTest()

      const wrapper = container.querySelector('.pkt-datepicker')
      expect(wrapper).toBeInTheDocument()
    })

    test('applies pkt-datepicker__inputs class', () => {
      const { container } = createDatepickerTest()

      const inputs = container.querySelector('.pkt-datepicker__inputs')
      expect(inputs).toBeInTheDocument()
    })

    test('applies datepicker input classes', () => {
      const { container } = createDatepickerTest()

      const input = container.querySelector('.pkt-datepicker__input')
      expect(input).toBeInTheDocument()
      expect(input).toHaveClass('pkt-input')
    })

    test('applies range class for range mode', () => {
      const { container } = createDatepickerTest({
        range: true,
      })

      const inputs = container.querySelectorAll('.pkt-datepicker--range')
      expect(inputs.length).toBe(2)
    })

    test('applies multiple class for multiple mode', () => {
      const { container } = createDatepickerTest({
        multiple: true,
      })

      const input = container.querySelector('.pkt-datepicker--multiple')
      expect(input).toBeInTheDocument()
    })

    test('applies calendar button classes', () => {
      const { container } = createDatepickerTest()

      const button = container.querySelector('button[type="button"]')
      expect(button).toHaveClass('pkt-input-icon')
      expect(button).toHaveClass('pkt-btn')
      expect(button).toHaveClass('pkt-btn--icon-only')
      expect(button).toHaveClass('pkt-btn--tertiary')
      expect(button).toHaveClass('pkt-datepicker__calendar-button')
    })

    test('popup has show/hide classes based on open state', () => {
      const { container } = createDatepickerTest()

      const popup = container.querySelector('.pkt-calendar-popup')
      expect(popup).toHaveClass('hide')

      const button = container.querySelector('button[type="button"]') as HTMLButtonElement
      fireEvent.click(button)

      expect(popup).toHaveClass('show')
    })
  })
})
