///|
/// Pure response plan. The caller remains responsible for HTTP policy and I/O.
pub(all) enum ResponsePlan {
  Ignore
  Unsatisfiable(Int64)
  Single(ConcreteRange)
  Multiple(Array[ConcreteRange])
} derive(Eq, Debug)

///|
pub fn plan_byte_range_response(
  request : RangeRequest,
  representation_length : Int64,
  method_is_get : Bool,
  range_supported : Bool,
  apply_ranges : Bool,
) -> Result[ResponsePlan, RangeError] {
  if !method_is_get ||
    !range_supported ||
    !apply_ranges ||
    !request.unit().is_bytes() {
    return Ok(Ignore)
  }
  let resolved = match resolve_byte_ranges(request, representation_length) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  match resolved.ranges() {
    [] => Ok(Unsatisfiable(representation_length))
    [single] => Ok(Single(single))
    many => Ok(Multiple(many))
  }
}

///|
/// Plan a range response under an optional If-Range validator (RFC 9110 ยง13.1.5).
/// When a validator is present and does not match the current representation,
/// the Range field must be ignored and the caller serves the full
/// representation, so the plan is `Ignore` (status 200). A matching validator
/// falls through to the unconditional planner.
pub fn plan_conditional_range_response(
  request : RangeRequest,
  representation_length : Int64,
  if_range : IfRangeValue?,
  current_etag : String,
  current_last_modified_unix : Int64,
  method_is_get : Bool,
  range_supported : Bool,
  apply_ranges : Bool,
) -> Result[ResponsePlan, RangeError] {
  match if_range {
    Some(validator) =>
      if !if_range_matches(validator, current_etag, current_last_modified_unix) {
        return Ok(Ignore)
      }
    None => ()
  }
  plan_byte_range_response(
    request, representation_length, method_is_get, range_supported, apply_ranges,
  )
}

///|
/// Suggested status assuming the caller applies this plan.
pub fn ResponsePlan::recommended_status(self : ResponsePlan) -> Int {
  match self {
    Ignore => 200
    Single(_) | Multiple(_) => 206
    Unsatisfiable(_) => 416
  }
}

///|
pub fn ResponsePlan::ranges(self : ResponsePlan) -> Array[ConcreteRange] {
  match self {
    Single(range) => [range]
    Multiple(ranges) => ranges.copy()
    _ => []
  }
}