/**
 * Shared date utilities for calendar and date-picker components
 * @countermeasure/web-components/utils/date
 */

export type WeekStart = 0 | 1

export const DAYS_SUN = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'] as const
export const DAYS_MON = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'] as const
export const MONTHS = [
  'January',
  'February',
  'March',
  'April',
  'May',
  'June',
  'July',
  'August',
  'September',
  'October',
  'November',
  'December',
] as const

/** Strip the time portion from a Date, returning midnight of that day */
export function stripTime(d: Date): Date {
  return new Date(d.getFullYear(), d.getMonth(), d.getDate())
}

/** Check whether two dates fall on the same calendar day */
export function isSameDay(a: Date, b: Date): boolean {
  return (
    a.getFullYear() === b.getFullYear() &&
    a.getMonth() === b.getMonth() &&
    a.getDate() === b.getDate()
  )
}

/** Check whether a date falls strictly between start and end (exclusive) */
export function isBetween(date: Date, start: Date, end: Date): boolean {
  const t = stripTime(date).getTime()
  const s = stripTime(start).getTime()
  const e = stripTime(end).getTime()
  const lo = Math.min(s, e)
  const hi = Math.max(s, e)
  return t > lo && t < hi
}

/**
 * Build a 6-week (42-day) grid of dates for a given month view.
 * Returns plain Date objects; the first entry is the start of the
 * first visible week (may be in the previous month).
 */
export function buildDayGrid(viewYear: number, viewMonth: number, weekStart: WeekStart): Date[] {
  const firstDay = new Date(viewYear, viewMonth, 1)
  let startDow = firstDay.getDay()
  if (weekStart === 1) {
    startDow = startDow === 0 ? 6 : startDow - 1
  }
  const startDate = new Date(firstDay)
  startDate.setDate(startDate.getDate() - startDow)

  const days: Date[] = []
  for (let i = 0; i < 42; i++) {
    const d = new Date(startDate)
    d.setDate(d.getDate() + i)
    days.push(d)
  }
  return days
}

/**
 * Check whether a date should be disabled based on min/max bounds
 * and an optional disabled-dates predicate or array.
 */
export function isDateDisabled(
  date: Date,
  minDate?: Date | null,
  maxDate?: Date | null,
  disabledDates?: Date[] | ((date: Date) => boolean)
): boolean {
  const d = stripTime(date)
  if (minDate !== undefined && minDate !== null && d < stripTime(minDate)) return true
  if (maxDate !== undefined && maxDate !== null && d > stripTime(maxDate)) return true
  if (disabledDates !== undefined) {
    if (typeof disabledDates === 'function') return disabledDates(d)
    return disabledDates.some(dd => isSameDay(dd, d))
  }
  return false
}
