"use client"

import * as React from "react"
import { format, type Locale } from "date-fns"
import { CalendarIcon, ChevronLeft, ChevronRight, X } from "lucide-react"

import { cn, isRadixOverlayTarget } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { Calendar } from "@/components/ui/calendar"
import { Input } from "@/components/ui/input"
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover"
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select"

export type DateTimePickerMode =
  | "date"
  | "month"
  | "year"
  | "datetime-hour"
  | "datetime-minute"
  | "datetime-second"

export interface DateTimePickerProps {
  mode?: DateTimePickerMode
  value?: Date | null
  defaultValue?: Date | null
  onChange?: (value: Date | null) => void

  placeholder?: string
  disabled?: boolean
  readOnly?: boolean
  clearable?: boolean
  required?: boolean

  minDate?: Date
  maxDate?: Date
  disabledDate?: (date: Date) => boolean

  displayFormat?: string
  className?: string
  buttonClassName?: string
  popoverClassName?: string

  yearRange?: [number, number]
  locale?: Locale

  hourStep?: number
  minuteStep?: number
  secondStep?: number
  use12Hour?: boolean

  showNow?: boolean
  showToday?: boolean
  showConfirm?: boolean
  closeOnSelect?: boolean

  name?: string
  id?: string
  "aria-label"?: string
}

const DEFAULT_PLACEHOLDERS: Record<DateTimePickerMode, string> = {
  year: "选择年份，如 2026",
  month: "选择年月，如 2026-03 或 2026/03",
  date: "yyyy-MM-dd 或 yyyy/MM/dd",
  "datetime-hour": "yyyy-MM-dd HH 或 yyyy/MM/dd HH",
  "datetime-minute": "yyyy-MM-dd HH:mm 或 yyyy/MM/dd HH:mm",
  "datetime-second": "yyyy-MM-dd HH:mm:ss 或 yyyy/MM/dd HH:mm:ss",
}

function parseDateParts(text: string): { year: number; month: number; day: number } | null {
  const match = /^(\d{4})[-/](\d{1,2})[-/](\d{1,2})$/.exec(text.trim())
  if (!match) {
    return null
  }

  const year = Number(match[1])
  const month = Number(match[2]) - 1
  const day = Number(match[3])
  const date = new Date(year, month, day)

  if (
    date.getFullYear() !== year ||
    date.getMonth() !== month ||
    date.getDate() !== day
  ) {
    return null
  }

  return { year, month, day }
}

function parseTimeParts(
  text: string,
  mode: DateTimePickerMode,
): { hour: number; minute: number; second: number } | null {
  const trimmed = text.trim()
  if (!trimmed) {
    return mode === "datetime-hour"
      ? { hour: 0, minute: 0, second: 0 }
      : null
  }

  const segments = trimmed.split(":")
  const hour = Number(segments[0])
  const minute =
    mode === "datetime-minute" || mode === "datetime-second"
      ? Number(segments[1] ?? 0)
      : 0
  const second = mode === "datetime-second" ? Number(segments[2] ?? 0) : 0

  if (
    Number.isNaN(hour) ||
    hour < 0 ||
    hour > 23 ||
    Number.isNaN(minute) ||
    minute < 0 ||
    minute > 59 ||
    Number.isNaN(second) ||
    second < 0 ||
    second > 59
  ) {
    return null
  }

  return { hour, minute, second }
}

export function parseDateInputText(
  text: string,
  mode: DateTimePickerMode,
): Date | null {
  const raw = text.trim()
  if (!raw) {
    return null
  }

  switch (mode) {
    case "year": {
      const match = /^(\d{4})$/.exec(raw)
      if (!match) {
        return null
      }
      return normalizeDateByMode(new Date(Number(match[1]), 0, 1), mode)
    }
    case "month": {
      const match = /^(\d{4})[-/](\d{1,2})$/.exec(raw)
      if (!match) {
        return null
      }
      const year = Number(match[1])
      const month = Number(match[2]) - 1
      if (month < 0 || month > 11) {
        return null
      }
      return normalizeDateByMode(new Date(year, month, 1), mode)
    }
    case "date": {
      const parts = parseDateParts(raw)
      if (!parts) {
        return null
      }
      return normalizeDateByMode(
        new Date(parts.year, parts.month, parts.day),
        mode,
      )
    }
    case "datetime-hour":
    case "datetime-minute":
    case "datetime-second": {
      const [dateText, timeText = ""] = raw.split(/\s+/, 2)
      const parts = parseDateParts(dateText)
      if (!parts) {
        return null
      }
      const timeParts = parseTimeParts(timeText, mode)
      if (!timeParts) {
        return null
      }
      return normalizeDateByMode(
        new Date(
          parts.year,
          parts.month,
          parts.day,
          timeParts.hour,
          timeParts.minute,
          timeParts.second,
        ),
        mode,
      )
    }
  }
}

export function getDefaultFormat(mode: DateTimePickerMode): string {
  switch (mode) {
    case "year":
      return "yyyy"
    case "month":
      return "yyyy-MM"
    case "date":
      return "yyyy-MM-dd"
    case "datetime-hour":
      return "yyyy-MM-dd HH"
    case "datetime-minute":
      return "yyyy-MM-dd HH:mm"
    case "datetime-second":
      return "yyyy-MM-dd HH:mm:ss"
  }
}

export function isValidDate(date: unknown): date is Date {
  return date instanceof Date && !Number.isNaN(date.getTime())
}

export function normalizeDateByMode(
  date: Date,
  mode: DateTimePickerMode,
): Date {
  if (!isValidDate(date)) {
    return date
  }

  const year = date.getFullYear()
  const month = date.getMonth()
  const day = date.getDate()
  const hour = date.getHours()
  const minute = date.getMinutes()
  const second = date.getSeconds()

  switch (mode) {
    case "year":
      return new Date(year, 0, 1, 0, 0, 0, 0)
    case "month":
      return new Date(year, month, 1, 0, 0, 0, 0)
    case "date":
      return new Date(year, month, day, 0, 0, 0, 0)
    case "datetime-hour":
      return new Date(year, month, day, hour, 0, 0, 0)
    case "datetime-minute":
      return new Date(year, month, day, hour, minute, 0, 0)
    case "datetime-second":
      return new Date(year, month, day, hour, minute, second, 0)
  }
}

function startOfDay(date: Date): Date {
  return new Date(date.getFullYear(), date.getMonth(), date.getDate())
}

export function isDateDisabled(
  date: Date,
  minDate?: Date,
  maxDate?: Date,
  disabledDate?: (date: Date) => boolean,
): boolean {
  if (disabledDate?.(date)) {
    return true
  }

  const day = startOfDay(date)

  if (minDate && isValidDate(minDate) && day < startOfDay(minDate)) {
    return true
  }

  if (maxDate && isValidDate(maxDate) && day > startOfDay(maxDate)) {
    return true
  }

  return false
}

function isDateTimeOutOfRange(
  date: Date,
  minDate?: Date,
  maxDate?: Date,
): boolean {
  if (minDate && isValidDate(minDate) && date < minDate) {
    return true
  }

  if (maxDate && isValidDate(maxDate) && date > maxDate) {
    return true
  }

  return false
}

export function buildYearOptions(
  yearRange: [number, number],
): { value: string; label: string }[] {
  const [start, end] = yearRange
  const from = Math.min(start, end)
  const to = Math.max(start, end)
  const options: { value: string; label: string }[] = []

  for (let year = from; year <= to; year += 1) {
    options.push({ value: String(year), label: String(year) })
  }

  return options
}

export function buildTimeOptions(
  step: number,
  max: number,
): { value: string; label: string }[] {
  const safeStep = Math.max(1, step)
  const options: { value: string; label: string }[] = []

  for (let value = 0; value <= max; value += safeStep) {
    options.push({
      value: String(value),
      label: String(value).padStart(2, "0"),
    })
  }

  return options
}

function buildMonthOptions(locale?: Locale): { value: string; label: string }[] {
  return Array.from({ length: 12 }, (_, index) => ({
    value: String(index),
    label: format(new Date(2000, index, 1), "MM月", { locale }),
  }))
}

interface CalendarMonthYearNavProps {
  activeDate: Date
  yearOptions: { value: string; label: string }[]
  monthOptions: { value: string; label: string }[]
  onYearChange: (year: number) => void
  onMonthChange: (month: number) => void
}

function CalendarMonthYearNav({
  activeDate,
  yearOptions,
  monthOptions,
  onYearChange,
  onMonthChange,
}: CalendarMonthYearNavProps) {
  return (
    <div className="grid h-10 grid-cols-2 gap-2">
      <Select
        value={String(activeDate.getFullYear())}
        onValueChange={(nextValue) => onYearChange(Number(nextValue))}
      >
        <SelectTrigger aria-label="选择年份" className="h-10">
          <SelectValue placeholder="年份" />
        </SelectTrigger>
        <SelectContent position="item-aligned" className="z-[110] max-h-60">
          {yearOptions.map((option) => (
            <SelectItem key={option.value} value={option.value}>
              {option.label}
            </SelectItem>
          ))}
        </SelectContent>
      </Select>

      <Select
        value={String(activeDate.getMonth())}
        onValueChange={(nextValue) => onMonthChange(Number(nextValue))}
      >
        <SelectTrigger aria-label="选择月份" className="h-10">
          <SelectValue placeholder="月份" />
        </SelectTrigger>
        <SelectContent position="item-aligned" className="z-[110]">
          {monthOptions.map((option) => (
            <SelectItem key={option.value} value={option.value}>
              {option.label}
            </SelectItem>
          ))}
        </SelectContent>
      </Select>
    </div>
  )
}

const YEAR_PAGE_SIZE = 10

interface YearPagerPanelProps {
  yearRange: [number, number]
  selectedYear: number
  open: boolean
  onSelect: (year: number) => void
}

function YearPagerPanel({
  yearRange,
  selectedYear,
  open,
  onSelect,
}: YearPagerPanelProps) {
  const minYear = Math.min(yearRange[0], yearRange[1])
  const maxYear = Math.max(yearRange[0], yearRange[1])

  const clampPageStart = React.useCallback(
    (start: number) => {
      const maxStart = Math.max(minYear, maxYear - YEAR_PAGE_SIZE + 1)
      return Math.max(minYear, Math.min(start, maxStart))
    },
    [maxYear, minYear],
  )

  const getPageStartForYear = React.useCallback(
    (year: number) => {
      const pageStart = Math.floor(year / YEAR_PAGE_SIZE) * YEAR_PAGE_SIZE
      return clampPageStart(pageStart)
    },
    [clampPageStart],
  )

  const [viewStart, setViewStart] = React.useState(() =>
    getPageStartForYear(selectedYear),
  )

  React.useEffect(() => {
    if (open) {
      setViewStart(getPageStartForYear(selectedYear))
    }
  }, [getPageStartForYear, open, selectedYear])

  const viewEnd = viewStart + YEAR_PAGE_SIZE - 1
  const canGoPrev = viewStart > minYear
  const canGoNext = viewStart + YEAR_PAGE_SIZE <= maxYear

  return (
    <div className="w-64 p-2">
      <div className="mb-2 flex items-center justify-between gap-1">
        <Button
          type="button"
          variant="ghost"
          size="icon"
          className="size-8 shrink-0"
          disabled={!canGoPrev}
          aria-label="上一页年份"
          onClick={() =>
            setViewStart((current) => clampPageStart(current - YEAR_PAGE_SIZE))
          }
        >
          <ChevronLeft className="size-4" />
        </Button>
        <span className="text-sm text-muted-foreground">
          {viewStart} - {Math.min(viewEnd, maxYear)}
        </span>
        <Button
          type="button"
          variant="ghost"
          size="icon"
          className="size-8 shrink-0"
          disabled={!canGoNext}
          aria-label="下一页年份"
          onClick={() =>
            setViewStart((current) => clampPageStart(current + YEAR_PAGE_SIZE))
          }
        >
          <ChevronRight className="size-4" />
        </Button>
      </div>

      <div className="grid grid-cols-2 gap-1">
        {Array.from({ length: YEAR_PAGE_SIZE }, (_, index) => {
          const year = viewStart + index
          const isOutOfRange = year < minYear || year > maxYear
          const isSelected = year === selectedYear

          return (
            <button
              key={year}
              type="button"
              disabled={isOutOfRange}
              className={cn(
                "rounded-md px-2 py-2 text-sm transition-colors",
                isOutOfRange &&
                  "cursor-not-allowed text-muted-foreground/40",
                !isOutOfRange &&
                  "hover:bg-accent hover:text-accent-foreground",
                isSelected &&
                  "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground",
              )}
              onClick={() => onSelect(year)}
            >
              {year}
            </button>
          )
        })}
      </div>
    </div>
  )
}

function resolveDate(value: Date | null | undefined): Date | null {
  if (value == null) {
    return null
  }

  return isValidDate(value) ? new Date(value) : null
}

function getBaseDate(current: Date | null | undefined): Date {
  if (current && isValidDate(current)) {
    return new Date(current)
  }

  return new Date()
}

function mergeDatePart(
  base: Date,
  part: "year" | "month" | "day" | "hour" | "minute" | "second",
  value: number,
): Date {
  const next = new Date(base)

  switch (part) {
    case "year":
      next.setFullYear(value)
      break
    case "month":
      next.setMonth(value)
      break
    case "day":
      next.setDate(value)
      break
    case "hour":
      next.setHours(value)
      break
    case "minute":
      next.setMinutes(value)
      break
    case "second":
      next.setSeconds(value)
      break
  }

  return next
}

function to12HourParts(date: Date): { hour: number; period: "AM" | "PM" } {
  const hour24 = date.getHours()
  const period: "AM" | "PM" = hour24 >= 12 ? "PM" : "AM"
  const hour12 = hour24 % 12 === 0 ? 12 : hour24 % 12
  return { hour: hour12, period }
}

function from12HourParts(hour12: number, period: "AM" | "PM"): number {
  if (period === "AM") {
    return hour12 === 12 ? 0 : hour12
  }

  return hour12 === 12 ? 12 : hour12 + 12
}

function shouldCloseOnChange(
  mode: DateTimePickerMode,
  changedPart: "year" | "month" | "day" | "hour" | "minute" | "second",
  showConfirm: boolean,
  closeOnSelect: boolean,
): boolean {
  if (showConfirm || !closeOnSelect) {
    return false
  }

  const closePartMap: Record<
    DateTimePickerMode,
    "year" | "month" | "day" | "hour" | "minute" | "second"
  > = {
    year: "year",
    month: "month",
    date: "day",
    "datetime-hour": "hour",
    "datetime-minute": "minute",
    "datetime-second": "second",
  }

  return closePartMap[mode] === changedPart
}

function formatDisplayValue(
  value: Date | null,
  mode: DateTimePickerMode,
  displayFormat: string | undefined,
  locale?: Locale,
): string {
  if (!value) {
    return ""
  }

  const formatString = displayFormat ?? getDefaultFormat(mode)
  return format(value, formatString, { locale })
}

function hasTimePicker(mode: DateTimePickerMode): boolean {
  return mode.startsWith("datetime-")
}

function DateTimePicker({
  mode = "date",
  value,
  defaultValue = null,
  onChange,
  placeholder,
  disabled = false,
  readOnly = false,
  clearable = false,
  required = false,
  minDate,
  maxDate,
  disabledDate,
  displayFormat,
  className,
  buttonClassName,
  popoverClassName,
  yearRange,
  locale,
  hourStep = 1,
  minuteStep = 1,
  secondStep = 1,
  use12Hour = false,
  showNow = false,
  showToday = false,
  showConfirm = false,
  closeOnSelect = true,
  name,
  id,
  "aria-label": ariaLabel,
}: DateTimePickerProps) {
  const isControlled = value !== undefined
  const [internalValue, setInternalValue] = React.useState<Date | null>(() =>
    resolveDate(defaultValue),
  )
  const [open, setOpen] = React.useState(false)
  const [draft, setDraft] = React.useState<Date>(() => getBaseDate(null))

  const valueTimestamp =
    value == null ? null : isValidDate(value) ? value.getTime() : null
  const resolvedValue = React.useMemo(
    () => (isControlled ? resolveDate(value) : internalValue),
    [isControlled, internalValue, valueTimestamp],
  )
  const resolvedTimestamp = resolvedValue?.getTime() ?? null
  const currentYear = new Date().getFullYear()
  const resolvedYearRange = React.useMemo<[number, number]>(
    () => yearRange ?? [currentYear - 50, currentYear + 50],
    [currentYear, yearRange],
  )
  const yearOptions = React.useMemo(
    () => buildYearOptions(resolvedYearRange),
    [resolvedYearRange],
  )
  const monthOptions = React.useMemo(
    () => buildMonthOptions(locale),
    [locale],
  )
  const hourOptions = React.useMemo(() => {
    if (use12Hour) {
      const options: { value: string; label: string }[] = []
      for (let hour = 1; hour <= 12; hour += Math.max(1, hourStep)) {
        options.push({
          value: String(hour),
          label: String(hour).padStart(2, "0"),
        })
      }
      return options
    }

    return buildTimeOptions(hourStep, 23)
  }, [hourStep, use12Hour])
  const minuteOptions = React.useMemo(
    () => buildTimeOptions(minuteStep, 59),
    [minuteStep],
  )
  const secondOptions = React.useMemo(
    () => buildTimeOptions(secondStep, 59),
    [secondStep],
  )

  React.useEffect(() => {
    if (open) {
      setDraft(getBaseDate(resolvedValue))
    }
  }, [open, resolvedTimestamp, resolvedValue])

  const commitValue = React.useCallback(
    (
      next: Date | null,
      changedPart?: "year" | "month" | "day" | "hour" | "minute" | "second",
    ) => {
      if (next === null) {
        if (!isControlled) {
          setInternalValue(null)
        }
        onChange?.(null)
        return
      }

      const normalized = normalizeDateByMode(next, mode)

      if (!isValidDate(normalized)) {
        return
      }

      if (isDateTimeOutOfRange(normalized, minDate, maxDate)) {
        return
      }

      if (isDateDisabled(normalized, minDate, maxDate, disabledDate)) {
        return
      }

      if (
        resolvedTimestamp !== null &&
        normalized.getTime() === resolvedTimestamp
      ) {
        if (
          changedPart &&
          shouldCloseOnChange(mode, changedPart, showConfirm, closeOnSelect)
        ) {
          setOpen(false)
        }
        return
      }

      if (!isControlled) {
        setInternalValue(normalized)
      }

      onChange?.(normalized)

      if (
        changedPart &&
        shouldCloseOnChange(mode, changedPart, showConfirm, closeOnSelect)
      ) {
        setOpen(false)
      }
    },
    [
      closeOnSelect,
      disabledDate,
      isControlled,
      maxDate,
      minDate,
      mode,
      onChange,
      resolvedTimestamp,
      showConfirm,
    ],
  )

  const applyDraftChange = React.useCallback(
    (
      part: "year" | "month" | "day" | "hour" | "minute" | "second",
      partValue: number,
      options?: { period?: "AM" | "PM" },
    ) => {
      let nextValue: Date | null = null

      setDraft((current) => {
        const base = getBaseDate(current)
        nextValue = normalizeDateByMode(
          part === "hour" && use12Hour && options?.period
            ? mergeDatePart(
                base,
                "hour",
                from12HourParts(partValue, options.period),
              )
            : mergeDatePart(base, part, partValue),
          mode,
        )
        return nextValue
      })

      if (!showConfirm && nextValue) {
        // date 模式：年月仅改 draft，等选中日再写入表单，避免第一次点年/月就提交半成品日期
        if (mode === "date" && part !== "day") {
          return
        }
        commitValue(nextValue, part)
      }
    },
    [commitValue, mode, showConfirm, use12Hour],
  )

  const handleDaySelect = React.useCallback(
    (day: Date | undefined) => {
      let nextValue: Date | null = null

      if (!day) {
        if (!showConfirm) {
          setDraft((current) => {
            if (current && isValidDate(current)) {
              nextValue = normalizeDateByMode(current, mode)
            }
            return current
          })
          if (nextValue) {
            commitValue(nextValue, "day")
          }
        }
        return
      }

      setDraft((current) => {
        const base = getBaseDate(current)
        nextValue = normalizeDateByMode(
          mergeDatePart(
            mergeDatePart(
              mergeDatePart(base, "year", day.getFullYear()),
              "month",
              day.getMonth(),
            ),
            "day",
            day.getDate(),
          ),
          mode,
        )
        return nextValue
      })

      if (!showConfirm && nextValue) {
        commitValue(nextValue, "day")
      }
    },
    [commitValue, mode, showConfirm],
  )

  const handleClear = React.useCallback(
    (event: React.MouseEvent) => {
      event.preventDefault()
      event.stopPropagation()

      if (showConfirm) {
        setDraft(getBaseDate(null))
      }

      commitValue(null)
      setOpen(false)
    },
    [commitValue, showConfirm],
  )

  const handleToday = React.useCallback(() => {
    const next = normalizeDateByMode(new Date(), mode)
    setDraft(next)

    if (!showConfirm) {
      commitValue(next, "day")
    }
  }, [commitValue, mode, showConfirm])

  const handleNow = React.useCallback(() => {
    const next = normalizeDateByMode(new Date(), mode)
    setDraft(next)

    if (!showConfirm) {
      commitValue(next, "second")
    }
  }, [commitValue, mode, showConfirm])

  const handleConfirm = React.useCallback(() => {
    commitValue(draft)
    setOpen(false)
  }, [commitValue, draft])

  const formattedValue = formatDisplayValue(
    resolvedValue,
    mode,
    displayFormat,
    locale,
  )
  const [inputText, setInputText] = React.useState(formattedValue)
  const [isInputFocused, setIsInputFocused] = React.useState(false)

  React.useEffect(() => {
    if (!isInputFocused) {
      setInputText(formattedValue)
    }
  }, [formattedValue, isInputFocused, resolvedTimestamp])

  const revertInputText = React.useCallback(() => {
    setInputText(formattedValue)
  }, [formattedValue])

  const commitInputText = React.useCallback(() => {
    const trimmed = inputText.trim()

    if (!trimmed) {
      if (clearable || !required) {
        commitValue(null)
        setInputText("")
      } else {
        revertInputText()
      }
      return
    }

    const parsed = parseDateInputText(trimmed, mode)
    if (
      parsed &&
      !isDateTimeOutOfRange(parsed, minDate, maxDate) &&
      !isDateDisabled(parsed, minDate, maxDate, disabledDate)
    ) {
      commitValue(parsed)
      setInputText(formatDisplayValue(parsed, mode, displayFormat, locale))
      setDraft(parsed)
      return
    }

    revertInputText()
  }, [
    clearable,
    commitValue,
    disabledDate,
    displayFormat,
    inputText,
    locale,
    maxDate,
    minDate,
    mode,
    required,
    revertInputText,
  ])

  const handleInputFocus = React.useCallback(() => {
    setIsInputFocused(true)
    if (!disabled && !readOnly) {
      setOpen(true)
    }
  }, [disabled, readOnly])

  const handleInputBlur = React.useCallback(() => {
    setIsInputFocused(false)
    window.setTimeout(() => {
      if (isRadixOverlayTarget(document.activeElement)) {
        return
      }
      commitInputText()
    }, 0)
  }, [commitInputText])

  const handleInputChange = React.useCallback(
    (event: React.ChangeEvent<HTMLInputElement>) => {
      setInputText(event.target.value)
    },
    [],
  )

  const handleInputKeyDown = React.useCallback(
    (event: React.KeyboardEvent<HTMLInputElement>) => {
      if (event.key === "Enter") {
        event.preventDefault()
        commitInputText()
        event.currentTarget.blur()
      }

      if (event.key === "Escape") {
        event.preventDefault()
        revertInputText()
        event.currentTarget.blur()
      }
    },
    [commitInputText, revertInputText],
  )

  const calendarDisabled = React.useCallback(
    (date: Date) => isDateDisabled(date, minDate, maxDate, disabledDate),
    [disabledDate, maxDate, minDate],
  )

  const activeDate = open ? draft : getBaseDate(resolvedValue)
  const calendarSelected =
    mode === "date" && resolvedValue == null ? undefined : activeDate
  const hourParts = to12HourParts(activeDate)
  const showCalendar = mode === "date" || hasTimePicker(mode)
  const showDateNav = showCalendar
  const showYearPicker = mode === "year" || mode === "month"
  const showMonthPicker = mode === "month"
  const showHourPicker =
    mode === "datetime-hour" ||
    mode === "datetime-minute" ||
    mode === "datetime-second"
  const showMinutePicker =
    mode === "datetime-minute" || mode === "datetime-second"
  const showSecondPicker = mode === "datetime-second"

  const handleCalendarMonthChange = React.useCallback(
    (month: Date) => {
      setDraft((current) => {
        const base = getBaseDate(current)
        return normalizeDateByMode(
          mergeDatePart(
            mergeDatePart(base, "year", month.getFullYear()),
            "month",
            month.getMonth(),
          ),
          mode,
        )
      })
    },
    [mode],
  )

  return (
    <div className={cn("relative inline-flex w-full", className)}>
      {name ? (
        <input
          type="hidden"
          name={name}
          value={resolvedValue ? resolvedValue.toISOString() : ""}
          required={required}
        />
      ) : null}

      <Popover
        modal={false}
        open={open}
        onOpenChange={(nextOpen) => {
          if (disabled || readOnly) {
            return
          }
          setOpen(nextOpen)
        }}
      >
        <div className="relative flex w-full items-center">
          <Input
            id={id}
            value={inputText}
            onChange={handleInputChange}
            onFocus={handleInputFocus}
            onBlur={handleInputBlur}
            onKeyDown={handleInputKeyDown}
            placeholder={placeholder || DEFAULT_PLACEHOLDERS[mode]}
            disabled={disabled}
            readOnly={readOnly}
            aria-label={ariaLabel}
            className={cn("pr-16", buttonClassName)}
          />

          <div className="absolute right-1 flex items-center gap-0.5">
            {clearable && resolvedValue && !disabled && !readOnly ? (
              <Button
                type="button"
                variant="ghost"
                size="icon"
                aria-label="清空"
                className="size-7 shrink-0 text-muted-foreground hover:text-foreground"
                onMouseDown={(event) => event.preventDefault()}
                onClick={handleClear}
              >
                <X className="size-4" />
              </Button>
            ) : null}

            <PopoverTrigger asChild>
              <Button
                type="button"
                variant="ghost"
                size="icon"
                disabled={disabled || readOnly}
                aria-label="打开日历"
                className="size-7 shrink-0 text-muted-foreground hover:text-foreground"
                onMouseDown={(event) => event.preventDefault()}
              >
                <CalendarIcon className="size-4" />
              </Button>
            </PopoverTrigger>
          </div>
        </div>

        <PopoverContent
          className={cn(
            "z-[100] w-auto p-0",
            showCalendar && "w-[280px]",
            mode === "year" ? "p-0" : "",
            popoverClassName,
          )}
          align="start"
        >
          <div
            className={cn(
              mode === "year" ? "" : "flex flex-col p-3",
              showCalendar && "gap-3",
            )}
          >
            {showYearPicker ? (
              <div
                className={cn(
                  "grid shrink-0 gap-2",
                  showMonthPicker ? "grid-cols-2" : "grid-cols-1",
                )}
              >
                {mode === "year" ? (
                  <YearPagerPanel
                    yearRange={resolvedYearRange}
                    selectedYear={activeDate.getFullYear()}
                    open={open}
                    onSelect={(year) => applyDraftChange("year", year)}
                  />
                ) : (
                  <Select
                    value={String(activeDate.getFullYear())}
                    onValueChange={(nextValue) =>
                      applyDraftChange("year", Number(nextValue))
                    }
                  >
                    <SelectTrigger aria-label="选择年份" className="h-10">
                      <SelectValue placeholder="年份" />
                    </SelectTrigger>
                    <SelectContent position="item-aligned" className="z-[110] max-h-60">
                      {yearOptions.map((option) => (
                        <SelectItem key={option.value} value={option.value}>
                          {option.label}
                        </SelectItem>
                      ))}
                    </SelectContent>
                  </Select>
                )}

                {showMonthPicker ? (
                  <Select
                    value={String(activeDate.getMonth())}
                    onValueChange={(nextValue) =>
                      applyDraftChange("month", Number(nextValue))
                    }
                  >
                    <SelectTrigger aria-label="选择月份" className="h-10">
                      <SelectValue placeholder="月份" />
                    </SelectTrigger>
                    <SelectContent position="item-aligned" className="z-[110]">
                      {monthOptions.map((option) => (
                        <SelectItem key={option.value} value={option.value}>
                          {option.label}
                        </SelectItem>
                      ))}
                    </SelectContent>
                  </Select>
                ) : null}
              </div>
            ) : null}

            {showDateNav ? (
              <CalendarMonthYearNav
                activeDate={activeDate}
                yearOptions={yearOptions}
                monthOptions={monthOptions}
                onYearChange={(year) => applyDraftChange("year", year)}
                onMonthChange={(month) => applyDraftChange("month", month)}
              />
            ) : null}

            {showCalendar ? (
              <div className="min-h-[280px] shrink-0">
                <Calendar
                  mode="single"
                  required={mode === "date"}
                  month={activeDate}
                  onMonthChange={handleCalendarMonthChange}
                  selected={calendarSelected}
                  onSelect={handleDaySelect}
                  locale={locale}
                  disabled={calendarDisabled}
                  fixedWeeks
                  className="p-0"
                  classNames={{
                    month_caption: "hidden",
                    nav: "hidden",
                  }}
                />
              </div>
            ) : null}

            {showHourPicker || showMinutePicker || showSecondPicker ? (
              <div className="grid grid-cols-2 gap-2 sm:grid-cols-4">
                {showHourPicker ? (
                  <Select
                    value={String(use12Hour ? hourParts.hour : activeDate.getHours())}
                    onValueChange={(nextValue) =>
                      applyDraftChange("hour", Number(nextValue), {
                        period: hourParts.period,
                      })
                    }
                  >
                    <SelectTrigger aria-label="选择小时">
                      <SelectValue placeholder="时" />
                    </SelectTrigger>
                    <SelectContent position="item-aligned" className="max-h-60">
                      {hourOptions.map((option) => (
                        <SelectItem key={option.value} value={option.value}>
                          {option.label}
                        </SelectItem>
                      ))}
                    </SelectContent>
                  </Select>
                ) : null}

                {use12Hour && showHourPicker ? (
                  <Select
                    value={hourParts.period}
                    onValueChange={(nextValue) =>
                      applyDraftChange("hour", hourParts.hour, {
                        period: nextValue as "AM" | "PM",
                      })
                    }
                  >
                    <SelectTrigger aria-label="选择上午或下午">
                      <SelectValue placeholder="AM/PM" />
                    </SelectTrigger>
                    <SelectContent position="item-aligned">
                      <SelectItem value="AM">AM</SelectItem>
                      <SelectItem value="PM">PM</SelectItem>
                    </SelectContent>
                  </Select>
                ) : null}

                {showMinutePicker ? (
                  <Select
                    value={String(activeDate.getMinutes())}
                    onValueChange={(nextValue) =>
                      applyDraftChange("minute", Number(nextValue))
                    }
                  >
                    <SelectTrigger aria-label="选择分钟">
                      <SelectValue placeholder="分" />
                    </SelectTrigger>
                    <SelectContent position="item-aligned" className="max-h-60">
                      {minuteOptions.map((option) => (
                        <SelectItem key={option.value} value={option.value}>
                          {option.label}
                        </SelectItem>
                      ))}
                    </SelectContent>
                  </Select>
                ) : null}

                {showSecondPicker ? (
                  <Select
                    value={String(activeDate.getSeconds())}
                    onValueChange={(nextValue) =>
                      applyDraftChange("second", Number(nextValue))
                    }
                  >
                    <SelectTrigger aria-label="选择秒">
                      <SelectValue placeholder="秒" />
                    </SelectTrigger>
                    <SelectContent position="item-aligned" className="max-h-60">
                      {secondOptions.map((option) => (
                        <SelectItem key={option.value} value={option.value}>
                          {option.label}
                        </SelectItem>
                      ))}
                    </SelectContent>
                  </Select>
                ) : null}
              </div>
            ) : null}

            {showToday || showNow || showConfirm ? (
              <div className="flex flex-wrap items-center gap-2 border-t pt-3">
                {showToday ? (
                  <Button type="button" variant="ghost" size="sm" onClick={handleToday}>
                    今天
                  </Button>
                ) : null}
                {showNow ? (
                  <Button type="button" variant="ghost" size="sm" onClick={handleNow}>
                    此刻
                  </Button>
                ) : null}
                {showConfirm ? (
                  <Button
                    type="button"
                    size="sm"
                    className="ml-auto"
                    onClick={handleConfirm}
                  >
                    确定
                  </Button>
                ) : null}
              </div>
            ) : null}
          </div>
        </PopoverContent>
      </Popover>
    </div>
  )
}

export { DateTimePicker }

/*
示例用法：

1. 年选择器
<DateTimePicker mode="year" value={value} onChange={setValue} />

2. 年月选择器
<DateTimePicker mode="month" value={value} onChange={setValue} />

3. 年月日选择器
<DateTimePicker mode="date" value={value} onChange={setValue} />

4. 年月日小时选择器
<DateTimePicker mode="datetime-hour" value={value} onChange={setValue} />

5. 年月日小时分钟选择器
<DateTimePicker mode="datetime-minute" value={value} onChange={setValue} />

6. 年月日时分秒选择器
<DateTimePicker mode="datetime-second" value={value} onChange={setValue} />
*/
