/**
 * date-picker.tsx — locale-aware, accessible date picker primitive.
 * (hebrew-locale-dates spec 117)
 *
 * Wraps react-day-picker v9 DayPicker with:
 *   - Hebrew locale support (month/day names from date-fns/locale/he)
 *   - RTL direction from useDirection (rtl-hebrew-ui spec 81)
 *   - WCAG 2.1 AA ARIA structure (grid, gridcell, aria-label, aria-live)
 *   - Keyboard navigation (react-day-picker v9 built-in)
 *   - Focus trap and Escape-to-close
 *   - prefers-reduced-motion: no open/close animation when reduced motion requested
 *
 * Props:
 *   selected    — currently selected date
 *   onSelect    — selection callback
 *   disabled    — predicate for disabling individual dates
 *   ariaLabel   — contextual grid label (default "Choose date")
 *   open        — controlled open state
 *   onOpenChange — controlled open state setter
 *   triggerRef  — ref to the trigger element (for Escape-to-close focus return)
 *
 * NOTE: react-day-picker and date-fns must be added to packages/ui/package.json
 * (see pkg_json in wave manifest). Types will not resolve until pnpm install runs.
 */
import * as React from 'react'
import { useRef, useEffect, useCallback, type ReactElement } from 'react'
import { DayPicker } from 'react-day-picker'
import { he } from 'date-fns/locale'
import { useLocale } from '../hooks/useLocale'
import { useDirection } from '../i18n/useDirection'
import { useReducedMotion } from '../hooks/use-reduced-motion'
import { cn } from '../lib/cn'

export interface DatePickerProps {
  /** Currently selected date. */
  selected?: Date
  /** Called when user selects a date (undefined = clear). */
  onSelect?: (date: Date | undefined) => void
  /** Predicate for disabling individual dates. */
  disabled?: (date: Date) => boolean
  /**
   * Contextual ARIA label for the calendar grid.
   * Use specific labels like "Choose invoice due date" for assistive tech.
   * @default "Choose date"
   */
  ariaLabel?: string
  /** Whether the calendar popup is open (controlled). */
  open?: boolean
  /** Callback to change open state (controlled). */
  onOpenChange?: (open: boolean) => void
  /** Ref to the trigger input/button — receives focus on Escape. */
  triggerRef?: React.RefObject<HTMLElement>
  /** Additional className for the calendar wrapper. */
  className?: string
}

/**
 * Accessible, locale-aware date picker.
 *
 * Used across invoice create, task due-date, and report period selectors.
 * Render this inside a <LocaleProvider> tree so useLocale() resolves correctly.
 *
 * @example
 * <DatePicker
 *   selected={dueDate}
 *   onSelect={setDueDate}
 *   ariaLabel="Choose invoice due date"
 *   open={isOpen}
 *   onOpenChange={setIsOpen}
 *   triggerRef={inputRef}
 * />
 */
export function DatePicker({
  selected,
  onSelect,
  disabled,
  ariaLabel = 'Choose date',
  open,
  onOpenChange,
  triggerRef,
  className,
}: DatePickerProps): ReactElement {
  const { locale: storedLocale } = useLocale()  // 'he' | 'en'
  const isHebrew = storedLocale === 'he'
  const dir = useDirection(storedLocale)        // 'rtl' | 'ltr'
  const reducedMotion = useReducedMotion()

  const calendarRef = useRef<HTMLDivElement>(null)

  // Keyboard: Escape closes calendar and returns focus to trigger
  const handleKeyDown = useCallback(
    (e: KeyboardEvent) => {
      if (e.key === 'Escape' && open) {
        e.preventDefault()
        onOpenChange?.(false)
        // Return focus to the trigger input/button
        triggerRef?.current?.focus()
      }
    },
    [open, onOpenChange, triggerRef],
  )

  useEffect(() => {
    if (!open) return
    document.addEventListener('keydown', handleKeyDown)
    return () => document.removeEventListener('keydown', handleKeyDown)
  }, [open, handleKeyDown])

  // Focus management: when calendar opens, focus the selected date or today
  useEffect(() => {
    if (!open || !calendarRef.current) return
    // react-day-picker v9 renders the selected/today cell with tabIndex=0
    // Focus it after a paint so the DOM is ready
    requestAnimationFrame(() => {
      if (!calendarRef.current) return
      const focusTarget =
        calendarRef.current.querySelector<HTMLElement>(
          '[aria-selected="true"]',
        ) ??
        calendarRef.current.querySelector<HTMLElement>(
          '[data-today="true"]',
        ) ??
        calendarRef.current.querySelector<HTMLElement>('[tabindex="0"]')
      focusTarget?.focus()
    })
  }, [open])

  // Focus trap: keep Tab within the calendar while open
  const handleFocusTrap = useCallback(
    (e: React.KeyboardEvent<HTMLDivElement>) => {
      if (e.key !== 'Tab' || !calendarRef.current) return
      const focusable = Array.from(
        calendarRef.current.querySelectorAll<HTMLElement>(
          'button:not([disabled]), [tabindex="0"]',
        ),
      ).filter((el) => !el.hasAttribute('disabled'))
      if (focusable.length === 0) return

      const first = focusable[0]
      const last = focusable[focusable.length - 1]

      if (!first || !last) return

      if (e.shiftKey && document.activeElement === first) {
        e.preventDefault()
        last.focus()
      } else if (!e.shiftKey && document.activeElement === last) {
        e.preventDefault()
        first.focus()
      }
    },
    [],
  )

  return (
    /* eslint-disable-next-line jsx-a11y/no-static-element-interactions */
    <div
      ref={calendarRef}
      dir={dir}
      onKeyDown={handleFocusTrap}
      className={cn(
        // Base calendar container — surface token, no hardcoded colors
        'bg-surface border border-control-border rounded p-4 shadow-md',
        // Suppress open/close animation under prefers-reduced-motion
        !reducedMotion && 'transition-opacity duration-150',
        className,
      )}
      // The month/year header announces changes via aria-live — the CaptionLabel
      // component override below wraps the month/year text in aria-live="polite".
    >
      <DayPicker
        mode="single"
        selected={selected}
        onSelect={onSelect}
        disabled={disabled}
        locale={isHebrew ? he : undefined}
        dir={dir}
        // ARIA structure: the grid gets the contextual label
        aria-label={ariaLabel}
        // react-day-picker v9 classNames — override to wire ARIA live region
        classNames={{
          // Month caption: announce changes to screen readers
          caption_label: 'font-medium text-ink',
          // Day cell: role="gridcell" is applied automatically by DayPicker v9
          day: cn(
            'h-8 w-8 rounded font-sans text-body-2 text-ink',
            'hover:bg-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring',
            'aria-selected:bg-accent aria-selected:text-ink-on-accent',
            'aria-disabled:opacity-40 aria-disabled:pointer-events-none',
          ),
          // Navigation buttons
          button_previous: cn(
            'inline-flex items-center justify-center h-8 w-8 rounded text-ink hover:bg-hover',
            'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring',
          ),
          button_next: cn(
            'inline-flex items-center justify-center h-8 w-8 rounded text-ink hover:bg-hover',
            'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring',
          ),
          // Month grid container
          month_grid: 'w-full border-collapse',
          // Weekday header row
          weekdays: 'text-ink-soft text-body-2',
          // Root navigation container
          nav: 'flex items-center justify-between mb-2',
          // Month container
          month: 'w-full',
          months: 'w-full',
        }}
        // react-day-picker v9: wire aria-label on prev/next buttons
        components={{
          // eslint-disable-next-line @typescript-eslint/no-explicit-any
          PreviousMonthButton: (props: any) => (
            <button
              {...props}
              aria-label="Previous month"
              className={cn(
                'inline-flex items-center justify-center h-8 w-8 rounded text-ink hover:bg-hover',
                'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring',
              )}
            />
          ),
          // eslint-disable-next-line @typescript-eslint/no-explicit-any
          NextMonthButton: (props: any) => (
            <button
              {...props}
              aria-label="Next month"
              className={cn(
                'inline-flex items-center justify-center h-8 w-8 rounded text-ink hover:bg-hover',
                'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring',
              )}
            />
          ),
          // Wrap caption in aria-live region so month changes announce
          // eslint-disable-next-line @typescript-eslint/no-explicit-any
          CaptionLabel: ({ children, ...props }: any) => (
            <span
              {...props}
              aria-live="polite"
              aria-atomic="true"
              className="font-medium text-ink"
            >
              {children}
            </span>
          ),
        }}
        // react-day-picker v9 provides built-in keyboard navigation:
        //   Left/Right Arrow → ±1 day
        //   Up/Down Arrow    → ±1 week
        //   Page Up/Down     → ±1 month
        //   Home/End         → first/last day of current week
        // These are NOT overridden.
      />
    </div>
  )
}
