///|
fn default_version_date() -> Date {
  Date::parse("0001-01-01").unwrap()
}

///|
fn is_within_version(version : CalendarVersion, date : Date) -> Bool {
  version.valid_from <= date && date <= version.valid_to
}

///|
fn observed_closure_date(calendar : Calendar, date : Date) -> Date {
  if !calendar.weekend.is_weekend(date.weekday()) {
    date
  } else {
    let mut candidate = date
    let mut searching = true
    while searching {
      candidate = match candidate.try_add_days(1) {
        Ok(next) => next
        Err(_) => return date
      }
      if !calendar.weekend.is_weekend(candidate.weekday()) {
        searching = false
      }
    }
    candidate
  }
}

///|
pub fn Calendar::observed_date(self : Calendar, date : Date) -> Date {
  observed_closure_date(self, date)
}

///|
pub fn Calendar::with_observed_holidays(
  self : Calendar,
  observed_holidays : Array[Date],
) -> Result[Calendar, CalendarVersionError] {
  if !self.versioned {
    return Err(MissingVersionMetadata)
  }
  let observed = self.observed_holidays.copy()
  for holiday in observed_holidays {
    let closure_date = observed_closure_date(self, holiday)
    if !is_within_version(self.version_metadata(), holiday) ||
      !is_within_version(self.version_metadata(), closure_date) {
      return Err(ObservationOutOfRange(holiday))
    }
    observed.push(closure_date)
  }
  observed.sort()
  observed.dedup()
  Ok({
    name: self.name,
    version: self.version,
    region: self.region,
    source: self.source,
    valid_from: self.valid_from,
    valid_to: self.valid_to,
    versioned: self.versioned,
    weekend: self.weekend,
    holidays: self.holidays,
    extra_workdays: self.extra_workdays,
    observed_holidays: observed,
  })
}

///|
fn Calendar::version_metadata(self : Calendar) -> CalendarVersion {
  {
    name: self.name,
    region: self.region,
    version: self.version,
    source: self.source,
    valid_from: self.valid_from,
    valid_to: self.valid_to,
  }
}