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('Rendering and basic functionality', () => {
    test('renders without errors', async () => {
      const container = await createCalendar()

      const calendar = container.querySelector('pkt-calendar') as PktCalendar
      expect(calendar).toBeInTheDocument()

      await calendar.updateComplete
      expect(calendar).toBeTruthy()
    })

    test('renders with correct structure', async () => {
      const container = await createCalendar()

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

      const calendarElement = calendar.querySelector('.pkt-calendar')
      const calendarTable = calendar.querySelector('.pkt-cal-days')
      const monthNav = calendar.querySelector('.pkt-cal-month-nav')

      expect(calendarElement).toBeInTheDocument()
      expect(calendarTable).toBeInTheDocument()
      expect(monthNav).toBeInTheDocument()
    })

    test('renders month picker when withcontrols is true', async () => {
      const container = await createCalendar('withcontrols')

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

      // Navigation buttons should always be present
      const prevButton = calendar.querySelector('.pkt-calendar__prev-month')
      const nextButton = calendar.querySelector('.pkt-calendar__next-month')
      expect(prevButton).toBeInTheDocument()
      expect(nextButton).toBeInTheDocument()

      // Should show month picker controls, not static title
      const monthPicker = calendar.querySelector('.pkt-cal-month-picker')
      const monthTitle = calendar.querySelector('.pkt-calendar__month-title')

      expect(monthPicker).toBeInTheDocument()
      expect(monthTitle).not.toBeInTheDocument()

      // Should have month and year inputs
      const monthSelect = monthPicker?.querySelector('select')
      const yearInput = monthPicker?.querySelector('input[type="number"]')
      expect(monthSelect).toBeInTheDocument()
      expect(yearInput).toBeInTheDocument()
    })

    test('renders static month title when withcontrols is false', async () => {
      const container = await createCalendar()

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

      // Navigation buttons should always be present
      const prevButton = calendar.querySelector('.pkt-calendar__prev-month')
      const nextButton = calendar.querySelector('.pkt-calendar__next-month')
      expect(prevButton).toBeInTheDocument()
      expect(nextButton).toBeInTheDocument()

      // Should show static month title, not controls
      const monthTitle = calendar.querySelector('.pkt-calendar__month-title')
      const monthPicker = calendar.querySelector('.pkt-cal-month-picker')

      expect(monthTitle).toBeInTheDocument()
      expect(monthPicker).not.toBeInTheDocument()
    })
  })

  describe('Properties and attributes', () => {
    test('applies default properties correctly', async () => {
      const container = await createCalendar()

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

      expect(calendar.multiple).toBe(false)
      expect(calendar.range).toBe(false)
      expect(calendar.weeknumbers).toBe(false)
      expect(calendar.withcontrols).toBe(false)
      expect(calendar.selected).toEqual([])
      expect(calendar.earliest).toBe(null)
      expect(calendar.latest).toBe(null)
    })

    test('handles multiple property correctly', async () => {
      const container = await createCalendar('multiple')

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

      expect(calendar.multiple).toBe(true)
    })

    test('handles range property correctly', async () => {
      const container = await createCalendar('range')

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

      expect(calendar.range).toBe(true)
    })

    test('handles weeknumbers property correctly', async () => {
      const container = await createCalendar('weeknumbers')

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

      expect(calendar.weeknumbers).toBe(true)

      const weekNumbers = calendar.querySelectorAll('.pkt-calendar__week-number')
      expect(weekNumbers.length).toBeGreaterThan(0)
    })

    test('handles maxMultiple property correctly', async () => {
      const container = await createCalendar('multiple max-multiple="3"')

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

      expect(calendar.maxMultiple).toBe(3)
    })

    test('handles currentmonth property correctly', async () => {
      const testDate = '2024-03-15'
      const container = await createCalendar(`currentmonth="${testDate}"`)

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

      expect(calendar.currentmonth).toEqual(parseISODateString(testDate))
    })
  })

  describe('Month navigation', () => {
    it('should navigate to previous month when clicking previous button', async () => {
      const calendar = document.createElement('pkt-calendar') as PktCalendar
      document.body.appendChild(calendar)
      await waitForCustomElements()

      const navElement = calendar.querySelector('.pkt-cal-month-nav')
      const prevButton = navElement?.querySelector('button')

      expect(prevButton).toBeInTheDocument()

      const initialMonth = calendar.currentmonth!.getMonth()
      const initialYear = calendar.currentmonth!.getFullYear()
      fireEvent.click(prevButton!)
      await calendar.updateComplete

      // Should go to previous month (or December of previous year if we're in January)
      if (initialMonth === 0) {
        expect(calendar.currentmonth!.getMonth()).toBe(11)
        expect(calendar.currentmonth!.getFullYear()).toBe(initialYear - 1)
      } else {
        expect(calendar.currentmonth!.getMonth()).toBe(initialMonth - 1)
        expect(calendar.currentmonth!.getFullYear()).toBe(initialYear)
      }

      document.body.removeChild(calendar)
    })

    it('should navigate to next month when clicking next button', async () => {
      const calendar = document.createElement('pkt-calendar') as PktCalendar
      document.body.appendChild(calendar)
      await waitForCustomElements()

      const navElement = calendar.querySelector('.pkt-cal-month-nav')
      const buttons = navElement?.querySelectorAll('button')
      const nextButton = buttons?.[1] // Second button is next

      expect(nextButton).toBeInTheDocument()

      const initialMonth = calendar.currentmonth!.getMonth()
      const initialYear = calendar.currentmonth!.getFullYear()
      fireEvent.click(nextButton!)
      await calendar.updateComplete

      // Should go to next month (or January of next year if we're in December)
      if (initialMonth === 11) {
        expect(calendar.currentmonth!.getMonth()).toBe(0)
        expect(calendar.currentmonth!.getFullYear()).toBe(initialYear + 1)
      } else {
        expect(calendar.currentmonth!.getMonth()).toBe(initialMonth + 1)
        expect(calendar.currentmonth!.getFullYear()).toBe(initialYear)
      }

      document.body.removeChild(calendar)
    })

    test('navigates to next month', async () => {
      const container = await createCalendar('withcontrols currentmonth="2024-06-15"')

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

      const nextButton = calendar.querySelector('.pkt-calendar__next-month')
      expect(nextButton).toBeInTheDocument()

      // With controls=true, check the month select dropdown instead of title
      const monthSelect = calendar.querySelector(
        '.pkt-cal-month-picker select',
      ) as HTMLSelectElement
      const initialMonth = monthSelect?.value

      fireEvent.click(nextButton!)
      await calendar.updateComplete

      const newMonth = monthSelect?.value
      expect(newMonth).not.toBe(initialMonth)
    })

    test('month navigation updates visible dates', async () => {
      const container = await createCalendar('withcontrols currentmonth="2024-06-15"')

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

      const initialDates = Array.from(calendar.querySelectorAll('.pkt-calendar__date')).map(
        (el) => el.textContent,
      )

      const nextButton = calendar.querySelector('.pkt-calendar__next-month')
      fireEvent.click(nextButton!)
      await calendar.updateComplete

      const newDates = Array.from(calendar.querySelectorAll('.pkt-calendar__date')).map(
        (el) => el.textContent,
      )
      expect(newDates).not.toEqual(initialDates)
    })
  })

  describe('Date formatting and localization', () => {
    test('displays day names correctly', async () => {
      const container = await createCalendar()

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

      const dayHeaders = calendar.querySelectorAll('.pkt-calendar__day-name')
      expect(dayHeaders.length).toBe(7)

      // Check that day names are displayed
      dayHeaders.forEach((header) => {
        expect(header.textContent).toBeTruthy()
        expect(header.textContent!.length).toBeGreaterThan(0)
      })
    })

    test('displays month names correctly', async () => {
      const container = await createCalendar('currentmonth="2024-06-15"')

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

      const monthTitle = calendar.querySelector('.pkt-calendar__month-title')
      expect(monthTitle).toBeInTheDocument()
      expect(monthTitle?.textContent).toBeTruthy()
    })

    test('handles custom day strings', async () => {
      const container = await createCalendar()

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

      // Set custom day strings
      calendar.dayStrings = ['S', 'M', 'T', 'W', 'T', 'F', 'S']
      await calendar.updateComplete

      const dayHeaders = calendar.querySelectorAll('.pkt-calendar__day-name')
      expect(dayHeaders[0].textContent?.trim()).toBe('S')
      expect(dayHeaders[1].textContent?.trim()).toBe('M')
    })

    test('handles custom month strings', async () => {
      const container = await createCalendar('withcontrols')

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

      // Set custom month strings
      calendar.monthStrings = [
        'Jan',
        'Feb',
        'Mar',
        'Apr',
        'May',
        'Jun',
        'Jul',
        'Aug',
        'Sep',
        'Oct',
        'Nov',
        'Dec',
      ]
      await calendar.updateComplete

      // When withcontrols=true, check the month select dropdown instead of title
      const monthSelect = calendar.querySelector('.pkt-cal-month-picker select')
      expect(monthSelect).toBeInTheDocument()

      // Check that the custom month string appears in the dropdown options
      const options = monthSelect?.querySelectorAll('option')
      expect(options?.[0]?.textContent).toContain('Jan')
    })
  })

  describe('Today highlighting', () => {
    test('highlights today date', async () => {
      const container = await createCalendar()

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

      const todayDate = calendar.querySelector('.pkt-calendar__date--today')
      expect(todayDate).toBeInTheDocument()
    })

    test('today date is selectable unless excluded', async () => {
      const container = await createCalendar()

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

      const todayDate = calendar.querySelector('.pkt-calendar__date--today')

      if (todayDate && !todayDate.classList.contains('pkt-calendar__date--disabled')) {
        fireEvent.click(todayDate)
        await calendar.updateComplete
        expect(todayDate).toHaveClass('pkt-calendar__date--selected')
      }
    })
  })
})
