///|
pub fn business_calendar(
  weekend_days : Array[Int],
  holidays : Array[Int],
  cutoff_hour : Int,
) -> BusinessCalendar {
  { weekend_days, holidays, cutoff_hour: cutoff_hour.clamp(min=0, max=23) }
}

///|
pub fn is_business_day(calendar : BusinessCalendar, day : Int) -> Bool {
  !calendar.weekend_days.contains(day % 7) && !calendar.holidays.contains(day)
}

///|
pub fn add_business_days(
  calendar : BusinessCalendar,
  start_day : Int,
  amount : Int,
) -> Int {
  let mut day = start_day
  let mut remaining = amount.max(0)
  while remaining > 0 {
    day += 1
    if is_business_day(calendar, day) {
      remaining -= 1
    }
  }
  day
}

///|
pub fn service_deadline(
  calendar : BusinessCalendar,
  start_day : Int,
  start_hour : Int,
  transit_days : Int,
  buffer_days : Int,
) -> Int {
  let first_day = if start_hour >= calendar.cutoff_hour {
    add_business_days(calendar, start_day, 1)
  } else {
    start_day
  }
  add_business_days(calendar, first_day, transit_days + buffer_days)
}

///|
pub fn BusinessCalendar::is_valid(self : BusinessCalendar) -> Bool {
  self.cutoff_hour >= 0 && self.cutoff_hour <= 23
}

///|
pub fn BusinessCalendar::working_days_between(
  self : BusinessCalendar,
  start_day : Int,
  end_day : Int,
) -> Int {
  let mut total = 0
  for day in start_day..<=end_day {
    if is_business_day(self, day) {
      total += 1
    }
  }
  total
}

///|
pub fn BusinessCalendar::holiday_count(self : BusinessCalendar) -> Int {
  self.holidays.length()
}

///|
pub fn BusinessCalendar::weekend_count(self : BusinessCalendar) -> Int {
  self.weekend_days.length()
}