/**
 * DatePicker - Input with calendar dropdown for date selection
 * @countermeasure/web-components/form/date-picker
 */

import { type DatePickerConfig } from './types'
import { Calendar } from '../calendar/index'
import { resolveContainer, uniqueId } from '../../primitives/utils'
import { announce } from '../../utils/a11y'

export type {
  DatePickerConfig,
  DateRangePickerConfig,
  DateRangePreset,
  TimePickerConfig,
} from './types'
export { DateRangePicker } from './range'
export { TimePicker } from './time'

function formatDate(date: Date, format: string, locale?: string): string {
  // For ISO format, use standard formatting
  if (format === 'YYYY-MM-DD') {
    return date.toISOString().split('T')[0] ?? ''
  }

  // For display, use Intl.DateTimeFormat
  const loc = locale ?? (typeof navigator !== 'undefined' ? navigator.language : 'en-US')

  return new Intl.DateTimeFormat(loc, {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
  }).format(date)
}

function parseDate(str: string, _format: string): Date | null {
  const clean = str.trim()
  if (clean === '') return null

  // Extract all digits from the string
  const digits = clean.replace(/\D/g, '')

  // Try ISO format first (YYYY-MM-DD or YYYYMMDD)
  if (digits.length >= 8) {
    const year = parseInt(digits.substring(0, 4), 10)
    const month = parseInt(digits.substring(4, 6), 10)
    const day = parseInt(digits.substring(6, 8), 10)

    if (!isNaN(year) && month >= 1 && month <= 12 && day >= 1 && day <= 31) {
      const result = new Date(year, month - 1, day)
      if (!isNaN(result.getTime())) return result
    }
  }

  // Fallback: try native Date parsing
  const parsed = new Date(clean)
  if (!isNaN(parsed.getTime())) return parsed

  return null
}

export class DatePicker {
  public readonly el: HTMLElement

  private readonly inputEl: HTMLInputElement
  private readonly toggleBtn: HTMLButtonElement
  private readonly dropdownEl: HTMLElement
  private readonly inputId: string
  private readonly dropdownId: string
  private calendar: Calendar | null = null
  private config: DatePickerConfig
  private currentValue: Date | null
  private isOpen = false
  private isDestroyed = false

  private readonly handleDocumentClick: (e: MouseEvent) => void
  private readonly handleInputKeydown: (e: KeyboardEvent) => void

  constructor(config: DatePickerConfig) {
    this.config = config
    this.inputId = uniqueId('cmm-datepicker')
    this.dropdownId = uniqueId('cmm-datepicker-dropdown')
    this.currentValue = config.value ?? null

    const fmt = config.format ?? 'YYYY-MM-DD'

    this.el = document.createElement('div')
    this.el.className = this.buildRootClass()

    const inputWrap = document.createElement('div')
    inputWrap.className = 'cmm-date-picker__input-wrap'

    this.inputEl = document.createElement('input')
    this.inputEl.id = this.inputId
    this.inputEl.type = 'text'
    this.inputEl.className = 'cmm-date-picker__input'
    this.inputEl.placeholder = config.placeholder ?? fmt.toLowerCase()

    // Combobox ARIA pattern
    this.inputEl.setAttribute('role', 'combobox')
    this.inputEl.setAttribute('aria-haspopup', 'dialog')
    this.inputEl.setAttribute('aria-expanded', 'false')
    this.inputEl.setAttribute('aria-controls', this.dropdownId)
    this.inputEl.setAttribute('aria-label', 'Date')
    this.inputEl.setAttribute('autocomplete', 'off')

    if (this.currentValue !== null) {
      this.inputEl.value = formatDate(this.currentValue, fmt, config.locale)
    }

    this.inputEl.addEventListener('change', () => {
      if (this.isDestroyed) return
      const parsed = parseDate(this.inputEl.value, fmt)
      if (parsed !== null) {
        this.currentValue = parsed
        this.inputEl.value = formatDate(parsed, fmt, config.locale)
        this.calendar?.setValue(parsed)
        this.config.onChange?.(parsed)
      }
    })

    this.inputEl.addEventListener('focus', () => {
      if (this.isDestroyed) return
      this.open()
    })

    const clearBtn = document.createElement('button')
    clearBtn.type = 'button'
    clearBtn.className = 'cmm-date-picker__clear'
    clearBtn.setAttribute('aria-label', 'Clear date')
    clearBtn.innerHTML =
      '<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M10.5 3.5L3.5 10.5M3.5 3.5L10.5 10.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>'
    clearBtn.addEventListener('click', e => {
      e.stopPropagation()
      if (this.isDestroyed) return
      this.currentValue = null
      this.inputEl.value = ''
      this.calendar?.setValue(null)
      this.config.onChange?.(null)
    })

    this.toggleBtn = document.createElement('button')
    this.toggleBtn.type = 'button'
    this.toggleBtn.className = 'cmm-date-picker__toggle'
    this.toggleBtn.setAttribute('aria-label', 'Open calendar')
    this.toggleBtn.innerHTML =
      '<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="2" y="3" width="12" height="11" rx="1.5" stroke="currentColor" stroke-width="1.2"/><path d="M2 6.5h12M5.5 2v2M10.5 2v2" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>'
    this.toggleBtn.addEventListener('click', e => {
      e.stopPropagation()
      if (this.isDestroyed) return
      if (this.isOpen) this.close()
      else this.open()
    })

    inputWrap.appendChild(this.inputEl)
    inputWrap.appendChild(clearBtn)
    inputWrap.appendChild(this.toggleBtn)
    this.el.appendChild(inputWrap)

    this.dropdownEl = document.createElement('div')
    this.dropdownEl.id = this.dropdownId
    this.dropdownEl.className = 'cmm-date-picker__dropdown'
    this.dropdownEl.setAttribute('role', 'dialog')
    this.dropdownEl.setAttribute('aria-modal', 'false')
    this.dropdownEl.setAttribute('aria-label', 'Choose date')
    this.dropdownEl.style.display = 'none'
    this.el.appendChild(this.dropdownEl)

    this.handleDocumentClick = (e: MouseEvent) => {
      if (this.isDestroyed) return
      const target = e.target as Node
      if (!this.el.contains(target)) {
        this.close()
      }
    }

    this.handleInputKeydown = (e: KeyboardEvent) => {
      if (this.isDestroyed) return
      if (e.key === 'Escape' && this.isOpen) {
        e.preventDefault()
        this.close()
      } else if (e.key === 'ArrowDown' && !this.isOpen) {
        e.preventDefault()
        this.open()
      }
    }

    this.inputEl.addEventListener('keydown', this.handleInputKeydown)

    const parent = resolveContainer(config.container)
    parent.appendChild(this.el)
  }

  getValue(): Date | null {
    return this.currentValue
  }

  setValue(date: Date | null): void {
    this.currentValue = date
    const fmt = this.config.format ?? 'YYYY-MM-DD'
    this.inputEl.value = date !== null ? formatDate(date, fmt, this.config.locale) : ''
    this.calendar?.setValue(date)
  }

  open(): void {
    if (this.isOpen || this.isDestroyed) return
    this.isOpen = true
    this.dropdownEl.style.display = ''
    this.inputEl.setAttribute('aria-expanded', 'true')

    if (this.calendar === null) {
      this.calendar = new Calendar({
        container: this.dropdownEl,
        value: this.currentValue,
        minDate: this.config.minDate,
        maxDate: this.config.maxDate,
        disabledDates: this.config.disabledDates,
        weekStart: this.config.weekStart,
        mode: 'single',
        onChange: (date: Date) => {
          const fmt = this.config.format ?? 'YYYY-MM-DD'
          this.currentValue = date
          this.inputEl.value = formatDate(date, fmt, this.config.locale)
          this.config.onChange?.(date)

          // Announce the selection to screen readers
          const dateStr = date.toLocaleDateString('en-US', {
            weekday: 'long',
            year: 'numeric',
            month: 'long',
            day: 'numeric',
          })
          announce(`Selected ${dateStr}`, 'polite')

          this.close()
        },
      })
    } else if (this.currentValue !== null) {
      this.calendar.setValue(this.currentValue)
      this.calendar.goToMonth(this.currentValue.getFullYear(), this.currentValue.getMonth())
    }

    this.positionDropdown()
    document.addEventListener('click', this.handleDocumentClick)
  }

  close(): void {
    if (!this.isOpen) return
    this.isOpen = false
    this.dropdownEl.style.display = 'none'
    this.inputEl.setAttribute('aria-expanded', 'false')
    document.removeEventListener('click', this.handleDocumentClick)
  }

  destroy(): void {
    this.isDestroyed = true
    this.close()
    this.inputEl.removeEventListener('keydown', this.handleInputKeydown)
    document.removeEventListener('click', this.handleDocumentClick)
    this.calendar?.destroy()
    this.el.remove()
  }

  private buildRootClass(): string {
    const classes = ['cmm-date-picker']
    if (this.config.className) classes.push(this.config.className)
    return classes.join(' ')
  }

  private positionDropdown(): void {
    const rect = this.el.getBoundingClientRect()
    const spaceBelow = window.innerHeight - rect.bottom
    if (spaceBelow < 320 && rect.top > spaceBelow) {
      this.dropdownEl.classList.add('cmm-date-picker__dropdown--above')
      this.dropdownEl.classList.remove('cmm-date-picker__dropdown--below')
    } else {
      this.dropdownEl.classList.add('cmm-date-picker__dropdown--below')
      this.dropdownEl.classList.remove('cmm-date-picker__dropdown--above')
    }
  }
}
