///|
/// Mutable, ordered header pairs with ASCII lowercase names.
/// Equality compares normalized pairs, including their order.
///
/// ```mbt check
/// test {
///   let headers = @http.Headers::from_array([("Accept", "text/plain")])
///   assert_eq(headers.get("ACCEPT"), Some("text/plain"))
/// }
/// ```
pub struct Headers {
  priv mut pairs : Array[(String, String)]
} derive(Eq, Debug)

///|
/// Explicit equality methods for normalized, ordered pairs.
pub extend Headers with Eq::{equal, not_equal}

///|
/// Debug representation of the header pairs.
pub extend Headers with @debug.Debug::{to_repr}

///|
/// Creates an empty header collection.
pub fn Headers::new() -> Headers {
  { pairs: [], }
}

///|
/// Copies pairs in their original order and normalizes their names.
pub fn Headers::from_array(pairs : Array[(String, String)]) -> Headers {
  { pairs: pairs.map(pair => (pair.0.to_lower(), pair.1)), }
}

///|
/// Replaces all values for a name at its first position, or appends a new name.
pub fn Headers::set(self : Headers, name : String, value : String) -> Unit {
  let name = name.to_lower()
  let pairs = []
  let mut found = false
  for pair in self.pairs {
    if pair.0 == name {
      if !found {
        pairs.push((name, value))
        found = true
      }
    } else {
      pairs.push(pair)
    }
  }
  if !found {
    pairs.push((name, value))
  }
  self.pairs = pairs
}

///|
/// Appends one pair, retaining all existing values.
pub fn Headers::append(self : Headers, name : String, value : String) -> Unit {
  self.pairs.push((name.to_lower(), value))
}

///|
/// Returns the first value for a name, ignoring ASCII case.
pub fn Headers::get(self : Headers, name : String) -> String? {
  let name = name.to_lower()
  for (key, value) in self.pairs {
    if key == name {
      return Some(value)
    }
  }
  None
}

///|
/// Returns a fresh array containing every matching value in insertion order.
pub fn Headers::get_all(self : Headers, name : String) -> Array[String] {
  let name = name.to_lower()
  self.pairs.filter_map(pair => if pair.0 == name { Some(pair.1) } else { None })
}

///|
/// Removes all pairs for a name, ignoring ASCII case.
pub fn Headers::remove(self : Headers, name : String) -> Unit {
  let name = name.to_lower()
  self.pairs = self.pairs.filter(pair => pair.0 != name)
}

///|
/// Reports whether at least one value exists for a name.
pub fn Headers::contains(self : Headers, name : String) -> Bool {
  self.get(name) is Some(_)
}

///|
/// Iterates over a snapshot of normalized pairs in insertion order.
pub fn Headers::iter(self : Headers) -> Iter[(String, String)] {
  self.pairs.copy().iter()
}

///|
/// Visits a snapshot of normalized pairs in insertion order.
pub fn Headers::each(
  self : Headers,
  f : (String, String) -> Unit raise?,
) -> Unit raise? {
  for (name, value) in self.iter() {
    f(name, value)
  }
}

///|
/// Returns the number of pairs, counting repeated names separately.
pub fn Headers::length(self : Headers) -> Int {
  self.pairs.length()
}

///|
fn Headers::copy(self : Headers) -> Headers {
  { pairs: self.pairs.copy(), }
}