"use client"

import * as React from "react"
import {
  CalendarDaysIcon,
  ChevronDownIcon,
  ChevronLeftIcon,
  ChevronRightIcon,
  ChevronUpIcon,
} from "lucide-react"
import { cva } from "class-variance-authority"

import { cn } from "@/lib/utils"

type DatePickerDirection = "ltr" | "rtl" | "auto"
type CalendarView = "days" | "month-year"
type CalendarMode = "single" | "range"
type RangeCalendarType = "single" | "double" | "double-with-presets"

type CalendarTimeValue = {
  hours?: string
  minutes?: string
  period?: string
  zone?: string
}

type CalendarMonthYearItem = {
  id?: string
  month: React.ReactNode
  year: React.ReactNode
  selected?: boolean
  disabled?: boolean
}

type CalendarRange = {
  from?: Date
  to?: Date
}

type CalendarPreset = {
  id?: string
  label: React.ReactNode
  range?: CalendarRange
  disabled?: boolean
}

type CalendarProps = Omit<
  React.ComponentProps<"div">,
  "defaultValue" | "dir" | "onSelect"
> & {
  defaultValue?: Date
  dir?: DatePickerDirection
  displayMonth?: Date
  events?: Date[]
  monthYearItems?: CalendarMonthYearItem[]
  nextLabel?: string
  onDisplayMonthChange?: (month: Date) => void
  onSelect?: (date: Date) => void
  onViewChange?: (view: CalendarView) => void
  previousLabel?: string
  selectedDate?: Date
  showTime?: boolean
  surfaceSize?: "calendar" | "picker"
  timeLabel?: React.ReactNode
  timeValue?: CalendarTimeValue
  title?: React.ReactNode
  value?: Date
  view?: CalendarView
}

type RangeCalendarProps = Omit<
  React.ComponentProps<"div">,
  "defaultValue" | "dir" | "onSelect"
> & {
  defaultValue?: CalendarRange
  dir?: DatePickerDirection
  displayMonth?: Date
  nextLabel?: string
  onDisplayMonthChange?: (month: Date) => void
  onPresetSelect?: (preset: CalendarPreset) => void
  onSelect?: (range: CalendarRange) => void
  presets?: CalendarPreset[]
  previousLabel?: string
  showPresets?: boolean
  showTime?: boolean
  timeLabel?: React.ReactNode
  timeValue?: CalendarTimeValue
  type?: RangeCalendarType
  value?: CalendarRange
}

type DatePickerProps = Omit<
  React.ComponentProps<"div">,
  "defaultValue" | "dir" | "onSelect"
> & {
  calendarProps?: Omit<
    CalendarProps,
    "defaultValue" | "selectedDate" | "value"
  >
  defaultOpen?: boolean
  defaultValue?: Date
  dir?: DatePickerDirection
  disabled?: boolean
  icon?: React.ReactNode
  onOpenChange?: (open: boolean) => void
  onSelect?: (date: Date) => void
  open?: boolean
  placeholder?: React.ReactNode
  triggerClassName?: string
  value?: Date
}

type DateRangePickerProps = Omit<
  React.ComponentProps<"div">,
  "defaultValue" | "dir" | "onSelect"
> & {
  calendarProps?: Omit<
    RangeCalendarProps,
    "defaultValue" | "type" | "value"
  >
  defaultOpen?: boolean
  defaultValue?: CalendarRange
  dir?: DatePickerDirection
  disabled?: boolean
  icon?: React.ReactNode
  onOpenChange?: (open: boolean) => void
  onSelect?: (range: CalendarRange) => void
  open?: boolean
  placeholder?: React.ReactNode
  triggerClassName?: string
  type?: RangeCalendarType
  value?: CalendarRange
}

const defaultDisplayMonth = new Date(2050, 6, 1)
const defaultSelectedDate = new Date(2050, 6, 18)
const defaultRange: CalendarRange = {
  from: new Date(2050, 6, 15),
  to: new Date(2050, 7, 23),
}
const defaultTimeValue: Required<CalendarTimeValue> = {
  hours: "05",
  minutes: "21",
  period: "AM",
  zone: "GTM-3",
}
const defaultDayLabels = ["S", "M", "T", "W", "T", "F", "S"]
const defaultMonthYearItems: CalendarMonthYearItem[] = [
  { month: "April", year: "2023" },
  { month: "May", year: "2022" },
  { month: "June", year: "2023" },
  { month: "July", year: "2024", selected: true },
  { month: "August", year: "2025" },
  { month: "September", year: "2026" },
  { month: "October", year: "2027" },
]
const defaultPresets: CalendarPreset[] = [
  { label: "Today" },
  { label: "Last 7 days" },
  { label: "Last 14 days" },
  { label: "Last 30 days" },
  { label: "Last 3 months" },
  { label: "Last 12 months" },
  { label: "Quarter to date" },
  { label: "Year to date" },
  { label: "All time" },
]

const calendarSurface = cva(
  [
    "relative bg-[var(--bh-bg-raised)]",
    "[--bh-date-surface-border:var(--bh-border-subtle)]",
    "shadow-[var(--shadow-date-surface)]",
  ],
  {
    variants: {
      layout: {
        single:
          "flex flex-col items-start rounded-[var(--bh-radius-2xl-12)] p-[var(--bh-space-md-8)]",
        range:
          "inline-flex max-w-[var(--bh-date-range-surface-max-width)] items-start gap-[var(--bh-space-3xl-16)] overflow-auto rounded-[var(--bh-radius-2xl-12)] p-[var(--bh-space-3xl-16)]",
      },
      surfaceSize: {
        calendar: "w-[var(--bh-date-surface-width-calendar)]",
        picker: "w-[var(--bh-date-surface-width-picker)]",
      },
    },
    defaultVariants: {
      layout: "single",
      surfaceSize: "calendar",
    },
  }
)

const pickerTrigger = cva(
  [
    "group/date-trigger relative flex h-[var(--bh-date-trigger-height)] w-[var(--bh-date-trigger-width)] max-w-full items-center gap-[var(--bh-space-md-8)] rounded-[var(--bh-radius-lg-8)]",
    "bg-[var(--bh-interactive-input-default)] px-[var(--bh-space-lg-10)] text-start outline-none",
    "[--bh-date-trigger-border:var(--bh-border-input)] [--bh-date-trigger-shadow:inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-date-trigger-border),var(--shadow-component-default)]",
    "text-[length:var(--bh-text-body-md-regular-font-size)] font-[var(--bh-text-body-md-regular-font-weight)] leading-[var(--bh-text-body-md-regular-line-height)] tracking-[var(--bh-text-body-md-regular-letter-spacing)]",
    "text-[var(--bh-content-default)] shadow-[var(--bh-date-trigger-shadow)] transition-[background-color,box-shadow]",
    "hover:bg-[var(--bh-bg-default-hover)] focus-visible:shadow-[var(--shadow-input-focus-ring)]",
    "disabled:pointer-events-none disabled:bg-[var(--bh-interactive-input-disabled)] disabled:text-[var(--bh-content-disabled)] disabled:shadow-[inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-border-disabled)]",
  ]
)

const monthButton = cva(
  [
    "flex size-[var(--bh-date-nav-button-size)] shrink-0 items-center justify-center rounded-[var(--bh-control-default)]",
    "bg-[var(--bh-interactive-soft-default)] text-[var(--bh-content-default)] outline-none transition-[background-color,box-shadow,color]",
    "hover:bg-[var(--bh-interactive-soft-hover)] focus-visible:shadow-[var(--shadow-button-focus)] disabled:opacity-0",
    "[&_svg]:size-[var(--bh-date-nav-icon-size)]",
  ]
)

const calendarTitleButton = cva(
  [
    "flex h-[var(--bh-date-title-height)] min-w-0 flex-1 items-center justify-center gap-[var(--bh-space-xs-4)] rounded-[var(--bh-radius-lg-8)]",
    "px-[var(--bh-space-md-8)] text-center outline-none transition-[background-color,box-shadow]",
    "text-[length:var(--bh-text-body-md-medium-font-size)] font-[var(--bh-text-body-md-medium-font-weight)] leading-[var(--bh-text-body-md-medium-line-height)] tracking-[var(--bh-text-body-md-medium-letter-spacing)] text-[var(--bh-content-default)]",
    "hover:bg-[var(--bh-interactive-ghost-hover)] focus-visible:shadow-[var(--shadow-button-focus)]",
  ],
  {
    variants: {
      framed: {
        true:
          "shadow-[inset_0px_0px_0px_var(--bh-border-width-default)_var(--bh-border-subtle)]",
        false: "",
      },
      active: {
        true: "bg-[var(--bh-interactive-ghost-hover)]",
        false: "",
      },
    },
    defaultVariants: {
      active: false,
      framed: false,
    },
  }
)

const dayCellButton = cva(
  [
    "relative flex h-[var(--bh-date-day-cell-height)] min-w-0 flex-1 items-center justify-center text-center outline-none",
    "text-[length:var(--bh-text-body-md-regular-font-size)] font-[var(--bh-text-body-md-regular-font-weight)] leading-[var(--bh-text-body-md-regular-line-height)] tracking-[var(--bh-text-body-md-regular-letter-spacing)]",
    "transition-[background-color,color,box-shadow] focus-visible:z-[var(--bh-z-raised)] focus-visible:shadow-[var(--shadow-button-focus)]",
  ],
  {
    variants: {
      state: {
        default:
          "rounded-[var(--bh-control-default)] text-[var(--bh-content-default)] hover:bg-[var(--bh-interactive-ghost-hover)]",
        outside:
          "rounded-[var(--bh-control-default)] text-[var(--bh-content-disabled)]",
        event:
          "rounded-[var(--bh-control-default)] text-[var(--bh-content-brand-default)] after:absolute after:bottom-[var(--bh-date-event-dot-offset-bottom)] after:left-1/2 after:size-[var(--bh-date-event-dot-size)] after:-translate-x-1/2 after:rounded-[var(--bh-radius-full)] after:bg-[var(--bh-content-brand-default)] rtl:after:translate-x-1/2",
        selected:
          "rounded-[var(--bh-control-default)] bg-[var(--bh-bg-brand-default)] text-[var(--bh-content-on-color)] hover:bg-[var(--bh-bg-brand-default)]",
        range:
          "rounded-none bg-[var(--bh-bg-brand-subtle)] text-[var(--bh-content-brand-strong)] hover:bg-[var(--bh-bg-brand-subtle)]",
      },
    },
    defaultVariants: {
      state: "default",
    },
  }
)

const dateListItem = cva(
  [
    "flex h-[var(--bh-date-list-item-height)] w-full items-center rounded-[var(--bh-radius-lg-8)] px-[var(--bh-space-md-8)] py-[var(--bh-space-sm-6)] text-start outline-none",
    "text-[length:var(--bh-text-body-md-medium-font-size)] font-[var(--bh-text-body-md-medium-font-weight)] leading-[var(--bh-text-body-md-medium-line-height)] tracking-[var(--bh-text-body-md-medium-letter-spacing)]",
    "text-[var(--bh-content-default)] transition-colors hover:bg-[var(--bh-interactive-ghost-hover)] focus-visible:bg-[var(--bh-interactive-ghost-hover)] disabled:pointer-events-none disabled:text-[var(--bh-content-disabled)]",
  ]
)

function Calendar({
  className,
  defaultValue = defaultSelectedDate,
  dir,
  displayMonth,
  events,
  monthYearItems = defaultMonthYearItems,
  nextLabel = "Next month",
  onDisplayMonthChange,
  onSelect,
  onViewChange,
  previousLabel = "Previous month",
  selectedDate,
  showTime = true,
  surfaceSize = "calendar",
  timeLabel = "Time",
  timeValue = defaultTimeValue,
  title,
  value,
  view,
  ...props
}: CalendarProps) {
  const controlledValue = value ?? selectedDate
  const [internalValue, setInternalValue] = React.useState(defaultValue)
  const [internalDisplayMonth, setInternalDisplayMonth] = React.useState(
    startOfMonth(displayMonth ?? controlledValue ?? defaultValue)
  )
  const [internalView, setInternalView] = React.useState<CalendarView>("days")
  const selected = controlledValue ?? internalValue
  const activeDisplayMonth = startOfMonth(displayMonth ?? internalDisplayMonth)
  const activeView = view ?? internalView
  const eventDates = events ?? [new Date(2050, 6, 15)]

  function updateDisplayMonth(nextMonth: Date) {
    const normalized = startOfMonth(nextMonth)
    if (displayMonth === undefined) {
      setInternalDisplayMonth(normalized)
    }
    onDisplayMonthChange?.(normalized)
  }

  function updateView(nextView: CalendarView) {
    if (view === undefined) {
      setInternalView(nextView)
    }
    onViewChange?.(nextView)
  }

  function selectDate(nextDate: Date) {
    if (value === undefined && selectedDate === undefined) {
      setInternalValue(nextDate)
    }
    onSelect?.(nextDate)
  }

  return (
    <div
      data-slot="calendar"
      data-view={activeView}
      dir={dir}
      className={cn(calendarSurface({ layout: "single", surfaceSize }), className)}
      {...props}
    >
      <CalendarMonth
        displayMonth={activeDisplayMonth}
        eventDates={eventDates}
        mode="single"
        className="w-full"
        nextLabel={nextLabel}
        onDisplayMonthChange={updateDisplayMonth}
        onSelectDate={selectDate}
        onToggleView={() =>
          updateView(activeView === "days" ? "month-year" : "days")
        }
        previousLabel={previousLabel}
        selectedDate={selected}
        showTime={showTime && activeView === "days"}
        timeLabel={timeLabel}
        timeValue={timeValue}
        title={title}
        view={activeView}
      />

      {activeView === "month-year" ? (
        <>
          <div aria-hidden="true" className="h-[var(--bh-date-month-year-spacer-height)] w-full" />
          <MonthYearList items={monthYearItems} />
        </>
      ) : null}
    </div>
  )
}

function RangeCalendar({
  className,
  defaultValue = defaultRange,
  dir,
  displayMonth,
  nextLabel = "Next month",
  onDisplayMonthChange,
  onPresetSelect,
  onSelect,
  presets = defaultPresets,
  previousLabel = "Previous month",
  showPresets,
  showTime = true,
  timeLabel = "Time",
  timeValue = defaultTimeValue,
  type = "double",
  value,
  ...props
}: RangeCalendarProps) {
  const [internalRange, setInternalRange] = React.useState(defaultValue)
  const [internalDisplayMonth, setInternalDisplayMonth] = React.useState(
    startOfMonth(displayMonth ?? defaultValue.from ?? defaultDisplayMonth)
  )
  const selectedRange = normalizeRange(value ?? internalRange)
  const activeDisplayMonth = startOfMonth(displayMonth ?? internalDisplayMonth)
  const monthsToRender = type === "single" ? 1 : 2
  const shouldShowPresets = showPresets ?? type === "double-with-presets"
  const monthNodes = Array.from({ length: monthsToRender }, (_, index) =>
    addMonths(activeDisplayMonth, index)
  )

  function updateDisplayMonth(nextMonth: Date) {
    const normalized = startOfMonth(nextMonth)
    if (displayMonth === undefined) {
      setInternalDisplayMonth(normalized)
    }
    onDisplayMonthChange?.(normalized)
  }

  function selectRangeDate(nextDate: Date) {
    const nextRange = getNextRange(selectedRange, nextDate)
    if (value === undefined) {
      setInternalRange(nextRange)
    }
    onSelect?.(nextRange)
  }

  function selectPreset(preset: CalendarPreset) {
    if (preset.disabled) return
    if (preset.range) {
      const nextRange = normalizeRange(preset.range)
      if (value === undefined) {
        setInternalRange(nextRange)
      }
      onSelect?.(nextRange)
    }
    onPresetSelect?.(preset)
  }

  return (
    <div
      data-slot="range-calendar"
      data-type={type}
      dir={dir}
      className={cn(calendarSurface({ layout: "range" }), className)}
      {...props}
    >
      {shouldShowPresets ? (
        <div
          data-slot="range-calendar-presets"
          className="grid w-[var(--bh-date-presets-width)] shrink-0 border-e border-[var(--bh-border-subtle)] pe-[var(--bh-space-md-8)]"
        >
          {presets.map((preset, index) => (
            <button
              aria-disabled={preset.disabled || undefined}
              className={dateListItem()}
              data-slot="range-calendar-preset"
              disabled={preset.disabled}
              key={preset.id || index}
              type="button"
              onClick={() => selectPreset(preset)}
            >
              <span dir="auto" className="min-w-0 truncate">
                {preset.label}
              </span>
            </button>
          ))}
        </div>
      ) : null}

      <div
        data-slot="range-calendar-months"
        className="flex min-w-max gap-[var(--bh-space-3xl-16)]"
      >
        {monthNodes.map((month, index) => (
          <CalendarMonth
            displayMonth={month}
            key={month.toISOString()}
            mode="range"
            className="w-[var(--bh-date-range-month-width)]"
            nextLabel={nextLabel}
            onDisplayMonthChange={updateDisplayMonth}
            onSelectDate={selectRangeDate}
            previousLabel={previousLabel}
            range={selectedRange}
            showNextButton={index === monthNodes.length - 1}
            showPreviousButton={index === 0}
            showTime={showTime}
            timeLabel={timeLabel}
            timeValue={timeValue}
          />
        ))}
      </div>
    </div>
  )
}

function DatePicker({
  calendarProps,
  className,
  defaultOpen = false,
  defaultValue,
  dir,
  disabled,
  icon,
  onOpenChange,
  onSelect,
  open,
  placeholder = "Select date",
  triggerClassName,
  value,
  ...props
}: DatePickerProps) {
  const [internalOpen, setInternalOpen] = React.useState(defaultOpen)
  const [internalValue, setInternalValue] = React.useState(defaultValue)
  const selected = value ?? internalValue
  const isOpen = open ?? internalOpen

  function updateOpen(nextOpen: boolean) {
    if (open === undefined) {
      setInternalOpen(nextOpen)
    }
    onOpenChange?.(nextOpen)
  }

  function selectDate(nextDate: Date) {
    if (value === undefined) {
      setInternalValue(nextDate)
    }
    onSelect?.(nextDate)
  }

  return (
    <div
      data-open={isOpen ? "true" : "false"}
      data-slot="date-picker"
      dir={dir}
      className={cn("relative w-[var(--bh-date-picker-width)] max-w-full", className)}
      {...props}
    >
      <DatePickerTrigger
        disabled={disabled}
        icon={icon}
        open={isOpen}
        placeholder={placeholder}
        value={selected ? formatCalendarDate(selected, { month: "long" }) : undefined}
        className={triggerClassName}
        onClick={() => updateOpen(!isOpen)}
      />

      {isOpen ? (
        <Calendar
          defaultValue={defaultValue ?? defaultSelectedDate}
          displayMonth={selected ?? defaultValue ?? defaultDisplayMonth}
          selectedDate={selected}
          surfaceSize="picker"
          {...calendarProps}
          className={cn(
            "absolute left-1/2 top-[calc(100%+var(--bh-space-md-8))] z-[var(--bh-z-popover)] -translate-x-1/2",
            calendarProps?.className
          )}
          dir={calendarProps?.dir ?? dir}
          onSelect={(nextDate) => {
            selectDate(nextDate)
            calendarProps?.onSelect?.(nextDate)
          }}
        />
      ) : null}
    </div>
  )
}

function DateRangePicker({
  calendarProps,
  className,
  defaultOpen = false,
  defaultValue,
  dir,
  disabled,
  icon,
  onOpenChange,
  onSelect,
  open,
  placeholder = "Select date range",
  triggerClassName,
  type = "double",
  value,
  ...props
}: DateRangePickerProps) {
  const [internalOpen, setInternalOpen] = React.useState(defaultOpen)
  const [internalValue, setInternalValue] = React.useState(defaultValue)
  const selectedRange = normalizeRange(value ?? internalValue ?? {})
  const isOpen = open ?? internalOpen
  const displayMonth = selectedRange.from ?? defaultDisplayMonth

  function updateOpen(nextOpen: boolean) {
    if (open === undefined) {
      setInternalOpen(nextOpen)
    }
    onOpenChange?.(nextOpen)
  }

  function selectRange(nextRange: CalendarRange) {
    if (value === undefined) {
      setInternalValue(nextRange)
    }
    onSelect?.(nextRange)
  }

  return (
    <div
      data-open={isOpen ? "true" : "false"}
      data-slot="date-range-picker"
      data-type={type}
      dir={dir}
      className={cn("relative w-[var(--bh-date-picker-width)] max-w-full", className)}
      {...props}
    >
      <DatePickerTrigger
        disabled={disabled}
        icon={icon}
        open={isOpen}
        placeholder={placeholder}
        value={formatCalendarRange(selectedRange)}
        className={triggerClassName}
        onClick={() => updateOpen(!isOpen)}
      />

      {isOpen ? (
        <RangeCalendar
          defaultValue={defaultValue ?? defaultRange}
          displayMonth={displayMonth}
          type={type}
          value={selectedRange}
          {...calendarProps}
          className={cn(
            "absolute left-1/2 top-[calc(100%+var(--bh-space-md-8))] z-[var(--bh-z-popover)] -translate-x-1/2",
            calendarProps?.className
          )}
          dir={calendarProps?.dir ?? dir}
          onSelect={(nextRange) => {
            selectRange(nextRange)
            calendarProps?.onSelect?.(nextRange)
          }}
        />
      ) : null}
    </div>
  )
}

function DatePickerTrigger({
  className,
  disabled,
  icon,
  onClick,
  open,
  placeholder,
  value,
}: {
  className?: string
  disabled?: boolean
  icon?: React.ReactNode
  onClick?: React.MouseEventHandler<HTMLButtonElement>
  open?: boolean
  placeholder: React.ReactNode
  value?: React.ReactNode
}) {
  const hasValue = hasRenderableContent(value)

  return (
    <button
      aria-expanded={open || undefined}
      data-slot="date-picker-trigger"
      disabled={disabled}
      type="button"
      className={cn(pickerTrigger(), className)}
      onClick={onClick}
    >
      <span
        aria-hidden="true"
        data-slot="date-picker-trigger-icon"
        className="flex size-[var(--bh-date-trigger-icon-size)] shrink-0 items-center justify-center text-[var(--bh-content-muted)] group-disabled/date-trigger:text-[var(--bh-content-disabled)] [&_svg:not([class*='size-'])]:size-[var(--bh-date-trigger-icon-size)]"
      >
        {icon ?? <CalendarDaysIcon strokeWidth={1.8} />}
      </span>
      <span
        data-slot="date-picker-trigger-value"
        dir="auto"
        className={cn(
          "min-w-0 flex-1 truncate text-start",
          hasValue ? "text-[var(--bh-content-default)]" : "text-[var(--bh-content-muted)]",
          disabled && "text-[var(--bh-content-disabled)]"
        )}
      >
        {hasValue ? value : placeholder}
      </span>
    </button>
  )
}

function CalendarMonth({
  className,
  displayMonth,
  eventDates,
  mode,
  nextLabel,
  onDisplayMonthChange,
  onSelectDate,
  onToggleView,
  previousLabel,
  range,
  selectedDate,
  showNextButton = true,
  showPreviousButton = true,
  showTime,
  timeLabel,
  timeValue,
  title,
  view = "days",
}: {
  className?: string
  displayMonth: Date
  eventDates?: Date[]
  mode: CalendarMode
  nextLabel: string
  onDisplayMonthChange: (month: Date) => void
  onSelectDate: (date: Date) => void
  onToggleView?: () => void
  previousLabel: string
  range?: CalendarRange
  selectedDate?: Date
  showNextButton?: boolean
  showPreviousButton?: boolean
  showTime?: boolean
  timeLabel: React.ReactNode
  timeValue: CalendarTimeValue
  title?: React.ReactNode
  view?: CalendarView
}) {
  const cells = React.useMemo(() => getCalendarCells(displayMonth), [displayMonth])
  const monthTitle = title ?? formatMonthTitle(displayMonth)
  const hasMonthToggle = mode === "single"

  return (
    <div
      data-slot="calendar-month"
      className={cn(
        "flex shrink-0 flex-col items-start gap-[var(--bh-space-md-8)]",
        className
      )}
    >
      <div
        data-slot="calendar-header"
        className="flex w-full items-center gap-[var(--bh-space-md-8)]"
      >
        <button
          aria-label={previousLabel}
          disabled={!showPreviousButton || view === "month-year"}
          type="button"
          className={monthButton()}
          onClick={() => onDisplayMonthChange(addMonths(displayMonth, -1))}
        >
          <ChevronLeftIcon aria-hidden="true" data-rtl-flip="true" />
        </button>

        {hasMonthToggle ? (
          <button
            aria-expanded={view === "month-year"}
            data-slot="calendar-title"
            type="button"
            className={calendarTitleButton({
              active: view === "month-year",
              framed: true,
            })}
            onClick={onToggleView}
          >
            <span className="min-w-0 truncate">{monthTitle}</span>
            {view === "month-year" ? (
              <ChevronUpIcon aria-hidden="true" className="size-[var(--bh-date-title-chevron-size)]" />
            ) : (
              <ChevronDownIcon aria-hidden="true" className="size-[var(--bh-date-title-chevron-size)]" />
            )}
          </button>
        ) : (
          <div
            data-slot="calendar-title"
            className={calendarTitleButton({ framed: false })}
          >
            <span className="min-w-0 truncate">{monthTitle}</span>
          </div>
        )}

        <button
          aria-label={nextLabel}
          disabled={!showNextButton || view === "month-year"}
          type="button"
          className={monthButton()}
          onClick={() => onDisplayMonthChange(addMonths(displayMonth, 1))}
        >
          <ChevronRightIcon aria-hidden="true" data-rtl-flip="true" />
        </button>
      </div>

      {view === "days" ? (
        <>
          <div
            aria-hidden="true"
            data-slot="calendar-weekdays"
            className="grid w-full grid-cols-7"
          >
            {defaultDayLabels.map((day, index) => (
              <div
                className="flex h-[var(--bh-date-day-cell-height)] min-w-0 items-center justify-center rounded-[var(--bh-radius-lg-8)] text-center text-[length:var(--bh-text-body-md-regular-font-size)] font-[var(--bh-text-body-md-regular-font-weight)] leading-[var(--bh-text-body-md-regular-line-height)] tracking-[var(--bh-text-body-md-regular-letter-spacing)] text-[var(--bh-content-subtle)]"
                key={`${day}-${index}`}
              >
                {day}
              </div>
            ))}
          </div>

          <div
            data-slot="calendar-grid"
            role="grid"
            className="grid w-full grid-cols-7"
          >
            {cells.map((cell) => (
              <CalendarDayCell
                cell={cell}
                displayMonth={displayMonth}
                eventDates={eventDates}
                key={cell.date.toISOString()}
                mode={mode}
                onSelectDate={onSelectDate}
                range={range}
                selectedDate={selectedDate}
              />
            ))}
          </div>

          {showTime ? (
            <CalendarTime
              label={timeLabel}
              value={timeValue}
            />
          ) : null}
        </>
      ) : null}
    </div>
  )
}

function CalendarDayCell({
  cell,
  displayMonth,
  eventDates,
  mode,
  onSelectDate,
  range,
  selectedDate,
}: {
  cell: CalendarCell
  displayMonth: Date
  eventDates?: Date[]
  mode: CalendarMode
  onSelectDate: (date: Date) => void
  range?: CalendarRange
  selectedDate?: Date
}) {
  const outside = cell.date.getMonth() !== displayMonth.getMonth()
  const hasEvent = eventDates?.some((date) => isSameDay(date, cell.date)) ?? false
  const selected =
    mode === "single"
      ? Boolean(selectedDate && isSameDay(selectedDate, cell.date))
      : Boolean(
          range?.from &&
            (isSameDay(range.from, cell.date) ||
              (range.to && isSameDay(range.to, cell.date)))
        )
  const inRange =
    mode === "range" &&
    Boolean(
      range?.from &&
        range?.to &&
        isAfterOrSameDay(cell.date, range.from) &&
        isBeforeOrSameDay(cell.date, range.to)
    )
  const rangeEndpoint =
    mode === "range" &&
    Boolean(
      range?.from &&
        (isSameDay(range.from, cell.date) ||
          (range.to && isSameDay(range.to, cell.date)))
    )
  const rangeState = selected
    ? "selected"
    : inRange
      ? "range"
      : outside
        ? "outside"
        : hasEvent
          ? "event"
          : "default"

  return (
    <button
      aria-disabled={outside || undefined}
      aria-label={formatCalendarDate(cell.date, { month: "long" })}
      aria-selected={selected || undefined}
      data-outside={outside ? "true" : undefined}
      data-range-endpoint={rangeEndpoint ? "true" : undefined}
      data-slot="calendar-cell"
      disabled={outside}
      role="gridcell"
      type="button"
      className={cn(
        dayCellButton({ state: rangeState }),
        inRange && cell.weekday === 0 && "rounded-s-[var(--bh-control-default)]",
        inRange && cell.weekday === 6 && "rounded-e-[var(--bh-control-default)]"
      )}
      onClick={() => onSelectDate(cell.date)}
    >
      {cell.date.getDate()}
    </button>
  )
}

function CalendarTime({
  label,
  value,
}: {
  label: React.ReactNode
  value: CalendarTimeValue
}) {
  const resolved = {
    ...defaultTimeValue,
    ...value,
  }

  return (
    <div
      data-slot="calendar-time"
      className="flex w-full items-center gap-[var(--bh-space-5xl-24)] ps-[var(--bh-space-md-8)]"
    >
      <span
        data-slot="calendar-time-label"
        dir="auto"
        className="shrink-0 text-[length:var(--bh-text-body-2xs-regular-font-size)] font-[var(--bh-text-body-2xs-regular-font-weight)] leading-[var(--bh-text-body-2xs-regular-line-height)] tracking-[var(--bh-text-body-2xs-regular-letter-spacing)] text-[var(--bh-content-subtle)]"
      >
        {label}
      </span>
      <span
        data-slot="calendar-time-input"
        className="flex min-h-[var(--bh-date-time-input-min-height)] min-w-0 flex-1 items-center gap-[var(--bh-space-xs-4)] rounded-[var(--bh-radius-lg-8)] bg-[var(--bh-interactive-input-default)] px-[var(--bh-space-md-8)] py-[var(--bh-space-sm-6)] shadow-[var(--shadow-date-time-input)]"
      >
        <CalendarTimeSegment active>{resolved.hours}</CalendarTimeSegment>
        <span className="text-[var(--bh-content-subtle)]">:</span>
        <CalendarTimeSegment>{resolved.minutes}</CalendarTimeSegment>
        <CalendarTimeSegment>{resolved.period}</CalendarTimeSegment>
        <CalendarTimeSegment subtle>{resolved.zone}</CalendarTimeSegment>
      </span>
    </div>
  )
}

function CalendarTimeSegment({
  active,
  children,
  subtle,
}: {
  active?: boolean
  children: React.ReactNode
  subtle?: boolean
}) {
  return (
    <span
      data-active={active ? "true" : undefined}
      className={cn(
        "inline-flex min-w-0 items-center justify-center rounded-[var(--bh-radius-sm-4)] px-[var(--bh-space-xxs-2)] text-[length:var(--bh-text-body-2xs-medium-font-size)] font-[var(--bh-text-body-2xs-medium-font-weight)] leading-[var(--bh-text-body-2xs-medium-line-height)] tracking-[var(--bh-text-body-2xs-medium-letter-spacing)]",
        active && "bg-[var(--bh-interactive-ghost-hover)]",
        subtle ? "text-[var(--bh-content-subtle)]" : "text-[var(--bh-content-default)]"
      )}
    >
      {children}
    </span>
  )
}

function MonthYearList({ items }: { items: CalendarMonthYearItem[] }) {
  return (
    <div
      data-slot="calendar-month-year-list"
      className="absolute bottom-[var(--bh-space-md-8)] left-1/2 top-[var(--bh-date-month-year-list-top)] flex w-[var(--bh-date-month-year-list-width)] -translate-x-1/2 flex-col justify-center overflow-hidden rounded-[var(--bh-radius-md-6)] bg-[var(--bh-bg-default)] p-[var(--bh-space-md-8)] shadow-[var(--shadow-date-month-year-list)] rtl:translate-x-1/2"
    >
      {items.map((item, index) => (
        <button
          className={cn(
            "grid h-[var(--bh-date-month-year-item-height)] grid-cols-2 items-center rounded-[var(--bh-radius-md-6)] px-[var(--bh-space-md-8)] py-[var(--bh-space-xs-4)] text-center outline-none transition-colors",
            "text-[length:var(--bh-text-body-md-regular-font-size)] font-[var(--bh-text-body-md-regular-font-weight)] leading-[var(--bh-text-body-md-regular-line-height)] tracking-[var(--bh-text-body-md-regular-letter-spacing)] text-[var(--bh-content-default)]",
            "hover:bg-[var(--bh-interactive-ghost-hover)] focus-visible:bg-[var(--bh-interactive-ghost-hover)] disabled:text-[var(--bh-content-disabled)]",
            item.selected && "bg-[var(--bh-interactive-ghost-hover)]"
          )}
          data-selected={item.selected ? "true" : undefined}
          data-slot="calendar-month-year-item"
          disabled={item.disabled}
          key={item.id || index}
          type="button"
        >
          <span className="truncate">{item.month}</span>
          <span className="truncate">{item.year}</span>
        </button>
      ))}
    </div>
  )
}

type CalendarCell = {
  date: Date
  weekday: number
}

function getCalendarCells(month: Date): CalendarCell[] {
  const first = startOfMonth(month)
  const start = addDays(first, -first.getDay())
  const last = new Date(first.getFullYear(), first.getMonth() + 1, 0)
  const end = addDays(last, 6 - last.getDay())
  const dayCount = differenceInCalendarDays(end, start) + 1

  return Array.from({ length: dayCount }, (_, index) => {
    const date = addDays(start, index)
    return {
      date,
      weekday: date.getDay(),
    }
  })
}

function getNextRange(currentRange: CalendarRange, date: Date): CalendarRange {
  const nextDate = startOfDay(date)
  const from = currentRange.from ? startOfDay(currentRange.from) : undefined
  const to = currentRange.to ? startOfDay(currentRange.to) : undefined

  if (!from || to) {
    return { from: nextDate, to: undefined }
  }

  if (isBeforeDay(nextDate, from)) {
    return { from: nextDate, to: from }
  }

  return { from, to: nextDate }
}

function normalizeRange(range: CalendarRange): CalendarRange {
  if (!range.from || !range.to) return range

  return isBeforeDay(range.to, range.from)
    ? { from: range.to, to: range.from }
    : range
}

function formatCalendarRange(range: CalendarRange) {
  if (!range.from && !range.to) return undefined
  if (range.from && !range.to) {
    return `${formatCalendarDate(range.from, { month: "short" })} -`
  }
  if (!range.from && range.to) {
    return `- ${formatCalendarDate(range.to, { month: "short" })}`
  }

  return `${formatCalendarDate(range.from, {
    month: "short",
  })} - ${formatCalendarDate(range.to, { month: "short" })}`
}

function formatCalendarDate(date: Date | undefined, options?: { month?: "short" | "long" }) {
  if (!date) return ""
  const month = date.toLocaleString("en", { month: options?.month ?? "short" })
  return `${month} ${date.getDate()}`
}

function formatMonthTitle(date: Date) {
  return `${date.toLocaleString("en", { month: "long" })} ${date.getFullYear()}`
}

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

function startOfMonth(date: Date) {
  return new Date(date.getFullYear(), date.getMonth(), 1)
}

function addDays(date: Date, amount: number) {
  const next = new Date(date)
  next.setDate(next.getDate() + amount)
  return startOfDay(next)
}

function addMonths(date: Date, amount: number) {
  return new Date(date.getFullYear(), date.getMonth() + amount, 1)
}

function differenceInCalendarDays(left: Date, right: Date) {
  const dayLength = 24 * 60 * 60 * 1000
  return Math.round((startOfDay(left).getTime() - startOfDay(right).getTime()) / dayLength)
}

function isSameDay(left: Date, right: Date) {
  return startOfDay(left).getTime() === startOfDay(right).getTime()
}

function isBeforeDay(left: Date, right: Date) {
  return startOfDay(left).getTime() < startOfDay(right).getTime()
}

function isAfterOrSameDay(left: Date, right: Date) {
  return startOfDay(left).getTime() >= startOfDay(right).getTime()
}

function isBeforeOrSameDay(left: Date, right: Date) {
  return startOfDay(left).getTime() <= startOfDay(right).getTime()
}

function hasRenderableContent(content: React.ReactNode) {
  return (
    content !== undefined &&
    content !== null &&
    content !== false &&
    content !== ""
  )
}

export {
  Calendar,
  DatePicker,
  DatePickerTrigger,
  DateRangePicker,
  RangeCalendar,
  calendarSurface,
  pickerTrigger,
}
export type {
  CalendarMonthYearItem,
  CalendarPreset,
  CalendarProps,
  CalendarRange,
  CalendarTimeValue,
  CalendarView,
  DatePickerDirection,
  DatePickerProps,
  DateRangePickerProps,
  RangeCalendarProps,
  RangeCalendarType,
}
