import '@testing-library/jest-dom'
import { fireEvent } from '@testing-library/dom'

import './datepicker'
import '../calendar/calendar'
import { PktDatepicker } from './datepicker'
import { PktDatepickerPopup } from './datepicker-popup'

const waitForCustomElements = async () => {
  await customElements.whenDefined('pkt-datepicker')
  await customElements.whenDefined('pkt-calendar')
}

// Helper function to create datepicker markup
const createDatepicker = async (datepickerProps = '') => {
  const container = document.createElement('div')
  container.innerHTML = `
    <pkt-datepicker ${datepickerProps}></pkt-datepicker>
  `
  document.body.appendChild(container)
  await waitForCustomElements()
  return container
}

// Cleanup after each test
afterEach(() => {
  document.body.innerHTML = ''
})

describe('PktDatepicker', () => {
  describe('Multiple date selection', () => {
    test('displays multiple selected dates as tags', async () => {
      const multipleDates = '2024-06-15,2024-06-20,2024-06-25'
      const container = await createDatepicker(`value="${multipleDates}" multiple`)

      const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
      await datepicker.updateComplete

      const tags = datepicker.querySelectorAll('pkt-tag')
      expect(tags.length).toBe(3)
    })

    test('allows adding dates through calendar in multiple mode', async () => {
      const container = await createDatepicker('multiple calendar-open')

      const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
      await datepicker.updateComplete

      const popup = datepicker.querySelector('pkt-datepicker-popup') as PktDatepickerPopup
      await popup?.updateComplete

      const calendar = datepicker.querySelector('pkt-calendar')
      await (calendar as any)?.updateComplete

      // Select a date
      const availableDate = datepicker.querySelector('[data-date]:not([data-disabled="disabled"])')
      if (availableDate) {
        fireEvent.click(availableDate)
        await datepicker.updateComplete

        // Should add tag
        const tags = datepicker.querySelectorAll('pkt-tag')
        expect(tags.length).toBe(1)
      }
    })

    test('removes dates when clicking tag close button', async () => {
      const multipleDates = '2024-06-15,2024-06-20'
      const container = await createDatepicker(`value="${multipleDates}" multiple`)

      const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
      await datepicker.updateComplete

      const closeButtons = datepicker.querySelectorAll('pkt-tag .pkt-tag__close-btn')
      expect(closeButtons.length).toBe(2)

      // Click first close button
      fireEvent.click(closeButtons[0])
      await datepicker.updateComplete

      const remainingTags = datepicker.querySelectorAll('pkt-tag')
      expect(remainingTags.length).toBe(1)
    })

    test('respects maxlength in multiple mode', async () => {
      const container = await createDatepicker('multiple maxlength="2"')

      const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
      await datepicker.updateComplete

      // Open calendar and try to select more than maxlength dates
      const calendarButton = datepicker.querySelector('button[type="button"]')
      fireEvent.click(calendarButton!)
      await datepicker.updateComplete

      const availableDates = datepicker.querySelectorAll(
        '.pkt-calendar__date:not(.pkt-calendar__date--disabled)',
      )

      // Select 3 dates
      fireEvent.click(availableDates[0])
      await datepicker.updateComplete
      fireEvent.click(availableDates[1])
      await datepicker.updateComplete
      fireEvent.click(availableDates[2])
      await datepicker.updateComplete

      // Should only have maxlength tags
      const tags = datepicker.querySelectorAll('pkt-tag')
      expect(tags.length).toBeLessThanOrEqual(2)
    })

    test('sorts multiple dates chronologically', async () => {
      const unsortedDates = '2024-06-25,2024-06-15,2024-06-20'
      const container = await createDatepicker(`value="${unsortedDates}" multiple`)

      const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
      await datepicker.updateComplete

      const tags = datepicker.querySelectorAll('pkt-tag')
      const tagTexts = Array.from(tags).map((tag) => tag.textContent?.trim())

      // Should be sorted chronologically
      expect(tagTexts[0]).toContain('15')
      expect(tagTexts[1]).toContain('20')
      expect(tagTexts[2]).toContain('25')
    })

    test('sorts multiple dates chronologically across months and years', async () => {
      // Test dates that would fail with simple string sorting
      const complexUnsortedDates = '2024-12-01,2023-01-15,2024-01-01,2023-12-31'
      const container = await createDatepicker(`value="${complexUnsortedDates}" multiple`)

      const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
      await datepicker.updateComplete

      const tags = datepicker.querySelectorAll('pkt-tag')
      const tagTexts = Array.from(tags).map((tag) =>
        tag.querySelector('time')?.getAttribute('datetime'),
      )

      // Should be sorted chronologically: 2023-01-15, 2023-12-31, 2024-01-01, 2024-12-01
      expect(tagTexts[0]).toBe('2023-01-15')
      expect(tagTexts[1]).toBe('2023-12-31')
      expect(tagTexts[2]).toBe('2024-01-01')
      expect(tagTexts[3]).toBe('2024-12-01')
    })
  })

  describe('Range selection', () => {
    test('displays range labels when showRangeLabels is true', async () => {
      const rangeValue = '2024-06-15,2024-06-20'
      const container = await createDatepicker(`value="${rangeValue}" range show-range-labels`)

      const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
      await datepicker.updateComplete

      expect(datepicker.showRangeLabels).toBe(true)

      const rangeLabels = datepicker.querySelectorAll('.pkt-input-prefix')
      expect(rangeLabels.length).toBeGreaterThan(0)
    })

    test('populates both input fields when initialized with range value', async () => {
      const rangeValue = '2024-06-15,2024-06-20'
      const container = await createDatepicker(`value="${rangeValue}" range`)

      const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
      await datepicker.updateComplete

      const inputs = datepicker.querySelectorAll('input')
      expect(inputs.length).toBe(2)

      // Check that both input fields are populated
      expect(inputs[0].value).toBe('2024-06-15')
      expect(inputs[1].value).toBe('2024-06-20')

      // Check internal state
      expect(datepicker._value).toEqual(['2024-06-15', '2024-06-20'])
      expect(datepicker.value).toBe('2024-06-15,2024-06-20')
    })

    test('dispatches value-change event with array for range and multiple datepickers', async () => {
      // Test range datepicker
      const rangeContainer = await createDatepicker('range')
      const rangeDatepicker = rangeContainer.querySelector('pkt-datepicker') as PktDatepicker
      await rangeDatepicker.updateComplete

      let valueChangeEvent: CustomEvent | null = null
      rangeDatepicker.addEventListener('value-change', (e: Event) => {
        valueChangeEvent = e as CustomEvent
      })

      // Set a range value programmatically
      rangeDatepicker.value = '2024-06-15,2024-06-20'
      await rangeDatepicker.updateComplete

      expect(valueChangeEvent).toBeTruthy()
      expect(valueChangeEvent!.detail).toEqual(['2024-06-15', '2024-06-20'])

      // Test multiple datepicker
      const multipleContainer = await createDatepicker('multiple')
      const multipleDatepicker = multipleContainer.querySelector('pkt-datepicker') as PktDatepicker
      await multipleDatepicker.updateComplete

      valueChangeEvent = null
      multipleDatepicker.addEventListener('value-change', (e: Event) => {
        valueChangeEvent = e as CustomEvent
      })

      // Set multiple values programmatically
      multipleDatepicker.value = '2024-06-15,2024-06-20,2024-06-25'
      await multipleDatepicker.updateComplete

      expect(valueChangeEvent).toBeTruthy()
      expect(valueChangeEvent!.detail).toEqual(['2024-06-15', '2024-06-20', '2024-06-25'])

      // Test single datepicker
      const singleContainer = await createDatepicker('')
      const singleDatepicker = singleContainer.querySelector('pkt-datepicker') as PktDatepicker
      await singleDatepicker.updateComplete

      valueChangeEvent = null
      singleDatepicker.addEventListener('value-change', (e: Event) => {
        valueChangeEvent = e as CustomEvent
      })

      // Set single value programmatically
      singleDatepicker.value = '2024-06-15'
      await singleDatepicker.updateComplete

      expect(valueChangeEvent).toBeTruthy()
      expect(valueChangeEvent!.detail).toBe('2024-06-15')
    })

    test('handles range selection through calendar', async () => {
      const container = await createDatepicker('range')

      const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
      await datepicker.updateComplete

      // Open calendar
      const calendarButton = datepicker.querySelector('button[type="button"]')
      fireEvent.click(calendarButton!)
      await datepicker.updateComplete

      const availableDates = datepicker.querySelectorAll(
        '[data-date]:not([data-disabled="disabled"])',
      )

      // Select start date
      if (availableDates.length > 5) {
        fireEvent.click(availableDates[5])
        await datepicker.updateComplete

        // Select end date
        if (availableDates.length > 10) {
          fireEvent.click(availableDates[10])
          await datepicker.updateComplete

          // Should have range value
          expect(datepicker.value).toContain(',')
          const values = (datepicker.value as string).split(',')
          expect(values.length).toBe(2)
        }
      }
    })

    test('validates range order', async () => {
      const invalidRange = '2024-06-20,2024-06-15' // End before start
      const container = await createDatepicker(`value="${invalidRange}" range`)

      const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
      await datepicker.updateComplete

      // Should reject invalid range and clear the value
      expect(datepicker.value).toBe('')
      expect(datepicker._value).toEqual([])

      // Component should still render without errors
      expect(datepicker).toBeInTheDocument()

      // Test with valid range should work
      datepicker.value = '2024-06-15,2024-06-20'
      await datepicker.updateComplete

      expect(datepicker.value).toBe('2024-06-15,2024-06-20')
      expect(datepicker._value).toEqual(['2024-06-15', '2024-06-20'])
    })

    test('handles range validation edge cases', async () => {
      const container = await createDatepicker('range')
      const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
      await datepicker.updateComplete

      // Same start and end date should be valid
      datepicker.value = '2024-06-15,2024-06-15'
      await datepicker.updateComplete
      expect(datepicker.value).toBe('2024-06-15,2024-06-15')

      // Single date should be valid (incomplete range)
      datepicker.value = '2024-06-15'
      await datepicker.updateComplete
      expect(datepicker.value).toBe('2024-06-15')

      // Empty value should be valid
      datepicker.value = ''
      await datepicker.updateComplete
      expect(datepicker.value).toBe('')

      // Multiple invalid attempts should be consistently rejected
      datepicker.value = '2024-06-25,2024-06-10'
      await datepicker.updateComplete
      expect(datepicker.value).toBe('')

      datepicker.value = '2024-12-31,2024-01-01'
      await datepicker.updateComplete
      expect(datepicker.value).toBe('')
    })

    test('shows range preview on hover', async () => {
      const container = await createDatepicker('range')

      const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
      await datepicker.updateComplete

      // Open calendar
      const calendarButton = datepicker.querySelector('button[type="button"]')
      fireEvent.click(calendarButton!)
      await datepicker.updateComplete

      const availableDates = datepicker.querySelectorAll(
        '.pkt-calendar__date:not(.pkt-calendar__date--disabled)',
      )

      // Select start date
      fireEvent.click(availableDates[5])
      await datepicker.updateComplete

      // Hover over potential end date
      fireEvent.mouseOver(availableDates[10])
      await datepicker.updateComplete

      // Should show hover preview
      const hoverRanges = datepicker.querySelectorAll('.pkt-calendar__date--in-range-hover')
      expect(hoverRanges.length).toBeGreaterThan(0)
    })
  })
})
