import '@testing-library/jest-dom'
import { fireEvent } from '@testing-library/dom'
import { parseISODateString } from 'shared-utils/date-utils'

import './calendar'
import { PktCalendar } from './calendar'

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

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

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

describe('PktCalendar', () => {
  describe('Date selection functionality', () => {
    test('selects single date correctly', async () => {
      const container = await createCalendar()

      const calendar = container.querySelector('pkt-calendar') as PktCalendar
      await calendar.updateComplete

      // Find and click on a date
      const availableDate = calendar.querySelector(
        '.pkt-calendar__date:not(.pkt-calendar__date--disabled)',
      )
      expect(availableDate).toBeInTheDocument()

      fireEvent.click(availableDate!)
      await calendar.updateComplete

      expect(availableDate).toHaveClass('pkt-calendar__date--selected')
    })

    test('handles multiple date selection', async () => {
      const container = await createCalendar('multiple')

      const calendar = container.querySelector('pkt-calendar') as PktCalendar
      await calendar.updateComplete

      // Find and click on multiple dates
      const availableDates = calendar.querySelectorAll(
        '.pkt-calendar__date:not(.pkt-calendar__date--disabled)',
      )
      expect(availableDates.length).toBeGreaterThan(1)

      fireEvent.click(availableDates[0])
      await calendar.updateComplete
      fireEvent.click(availableDates[1])
      await calendar.updateComplete

      expect(availableDates[0]).toHaveClass('pkt-calendar__date--selected')
      expect(availableDates[1]).toHaveClass('pkt-calendar__date--selected')
    })

    test('respects maxMultiple limit', async () => {
      const container = await createCalendar('multiple max-multiple="2"')

      const calendar = container.querySelector('pkt-calendar') as PktCalendar
      await calendar.updateComplete

      // Try to select more than maxMultiple dates
      const availableDates = calendar.querySelectorAll(
        '.pkt-calendar__date:not(.pkt-calendar__date--disabled)',
      )
      expect(availableDates.length).toBeGreaterThan(2)

      // Select 3 dates but only 2 should be selected
      fireEvent.click(availableDates[0])
      await calendar.updateComplete
      fireEvent.click(availableDates[1])
      await calendar.updateComplete
      fireEvent.click(availableDates[2])
      await calendar.updateComplete

      const selectedDates = calendar.querySelectorAll('.pkt-calendar__date--selected')
      expect(selectedDates.length).toBeLessThanOrEqual(2)
    })

    test('handles pre-selected dates', async () => {
      const preSelectedDates = '2024-06-15,2024-06-20'
      const container = await createCalendar(
        `selected="${preSelectedDates}" currentmonth="2024-06-01"`,
      )

      const calendar = container.querySelector('pkt-calendar') as PktCalendar
      await calendar.updateComplete

      expect(calendar.selected).toEqual(['2024-06-15', '2024-06-20'])

      const selectedDates = calendar.querySelectorAll('.pkt-calendar__date--selected')
      expect(selectedDates.length).toBe(2)
    })

    test('toggles date selection when clicking same date twice', async () => {
      const container = await createCalendar()

      const calendar = container.querySelector('pkt-calendar') as PktCalendar
      await calendar.updateComplete

      const availableDate = calendar.querySelector(
        '.pkt-calendar__date:not(.pkt-calendar__date--disabled)',
      )
      expect(availableDate).toBeInTheDocument()

      // First click - select
      fireEvent.click(availableDate!)
      await calendar.updateComplete
      expect(availableDate).toHaveClass('pkt-calendar__date--selected')

      // Second click - deselect
      fireEvent.click(availableDate!)
      await calendar.updateComplete
      expect(availableDate).not.toHaveClass('pkt-calendar__date--selected')
    })
  })

  describe('Range selection functionality', () => {
    test('handles range selection correctly', async () => {
      const container = await createCalendar('range')

      const calendar = container.querySelector('pkt-calendar') as PktCalendar
      await calendar.updateComplete

      const availableDates = calendar.querySelectorAll(
        '.pkt-calendar__date:not(.pkt-calendar__date--disabled)',
      )
      expect(availableDates.length).toBeGreaterThan(3)

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

      // Select end date
      fireEvent.click(availableDates[10])
      await calendar.updateComplete

      // Check for range styling
      const rangeStart = calendar.querySelector('.pkt-calendar__date--range-start')
      const rangeEnd = calendar.querySelector('.pkt-calendar__date--range-end')
      const rangeInBetween = calendar.querySelectorAll('.pkt-calendar__date--in-range')

      expect(rangeStart).toBeInTheDocument()
      expect(rangeEnd).toBeInTheDocument()
      expect(rangeInBetween.length).toBeGreaterThan(0)
    })

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

      const calendar = container.querySelector('pkt-calendar') as PktCalendar
      await calendar.updateComplete

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

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

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

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

    test('clears range when selecting new start date', async () => {
      const container = await createCalendar('range')

      const calendar = container.querySelector('pkt-calendar') as PktCalendar
      await calendar.updateComplete

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

      // Create initial range
      fireEvent.click(availableDates[5])
      await calendar.updateComplete
      fireEvent.click(availableDates[10])
      await calendar.updateComplete

      // Select new start date
      fireEvent.click(availableDates[2])
      await calendar.updateComplete

      // Old range should be cleared
      const selectedDates = calendar.querySelectorAll('.pkt-calendar__date--selected')
      expect(selectedDates.length).toBe(1)
    })
  })

  describe('API methods', () => {
    test('addToSelected method works correctly', async () => {
      const container = await createCalendar('multiple')

      const calendar = container.querySelector('pkt-calendar') as PktCalendar
      await calendar.updateComplete

      const testDate = parseISODateString('2024-06-15')
      calendar.addToSelected(testDate)
      await calendar.updateComplete

      expect(calendar['_selected']).toContainEqual(testDate)
    })

    test('removeFromSelected method works correctly', async () => {
      const container = await createCalendar('multiple')

      const calendar = container.querySelector('pkt-calendar') as PktCalendar
      await calendar.updateComplete

      const testDate = new Date('2024-06-15')
      calendar.addToSelected(testDate)
      await calendar.updateComplete

      calendar.removeFromSelected(testDate)
      await calendar.updateComplete

      expect(calendar['_selected']).not.toContainEqual(testDate)
    })

    test('toggleSelected method works correctly', async () => {
      const container = await createCalendar()

      const calendar = container.querySelector('pkt-calendar') as PktCalendar
      await calendar.updateComplete

      const testDate = parseISODateString('2024-06-15')

      // Toggle on
      calendar.toggleSelected(testDate)
      await calendar.updateComplete
      expect(calendar['_selected']).toContainEqual(testDate)

      // Toggle off
      calendar.toggleSelected(testDate)
      await calendar.updateComplete
      expect(calendar['_selected']).not.toContainEqual(testDate)
    })

    test('focusOnCurrentDate method works correctly', async () => {
      const container = await createCalendar()

      const calendar = container.querySelector('pkt-calendar') as PktCalendar
      await calendar.updateComplete

      calendar.focusOnCurrentDate()
      await calendar.updateComplete

      // Should focus on a date element
      const focusedElement = document.activeElement
      expect(focusedElement).toHaveClass('pkt-calendar__date')
    })
  })
})
