///|
/// A request to reserve one or more resources at the same time.
pub struct BookingRequest {
  id : String
  duration : Int
  horizon : Interval
  capacity_needed : Int
  buffer_before : Int
  buffer_after : Int
  required_resources : Array[String]
} derive(Debug)

///|
pub enum RequestError {
  EmptyRequestId
  NonPositiveDuration(Int)
  NonPositiveCapacity(Int)
  NegativeBuffer(Int)
  EmptyRequiredResource
} derive(Eq, Debug)

///|
pub fn BookingRequest::new(
  id : String,
  duration : Int,
  horizon : Interval,
  capacity_needed? : Int = 1,
  buffer_before? : Int = 0,
  buffer_after? : Int = 0,
  required_resources? : Array[String] = [],
) -> Result[BookingRequest, RequestError] {
  if id.length() == 0 {
    Err(EmptyRequestId)
  } else if duration <= 0 {
    Err(NonPositiveDuration(duration))
  } else if capacity_needed <= 0 {
    Err(NonPositiveCapacity(capacity_needed))
  } else if buffer_before < 0 {
    Err(NegativeBuffer(buffer_before))
  } else if buffer_after < 0 {
    Err(NegativeBuffer(buffer_after))
  } else {
    for resource in required_resources {
      if resource.length() == 0 {
        return Err(EmptyRequiredResource)
      }
    }
    Ok({
      id,
      duration,
      horizon,
      capacity_needed,
      buffer_before,
      buffer_after,
      required_resources,
    })
  }
}

///|
pub fn BookingRequest::id(self : BookingRequest) -> String {
  self.id
}

///|
pub fn BookingRequest::duration(self : BookingRequest) -> Int {
  self.duration
}

///|
pub fn BookingRequest::horizon(self : BookingRequest) -> Interval {
  self.horizon
}

///|
pub fn BookingRequest::capacity_needed(self : BookingRequest) -> Int {
  self.capacity_needed
}

///|
pub fn BookingRequest::required_resources(
  self : BookingRequest,
) -> Array[String] {
  self.required_resources.copy()
}

///|
pub fn BookingRequest::reserved_duration(self : BookingRequest) -> Int {
  self.duration + self.buffer_before + self.buffer_after
}

///|
/// Convert a reserved envelope back to the customer-visible appointment.
pub fn BookingRequest::event_from_reserved(
  self : BookingRequest,
  reserved : Interval,
) -> Interval {
  {
    start: reserved.start() + self.buffer_before,
    end: reserved.end() - self.buffer_after,
  }
}

///|
pub fn BookingRequest::render(self : BookingRequest) -> String {
  self.id +
  " duration=" +
  self.duration.to_string() +
  " horizon=" +
  self.horizon.render()
}