///|
fn BidiProtocol::normalize_synthetic_network_header_entries(
  self : BidiProtocol,
  request_id : Int,
  raw_headers : Json?,
) -> Array[Json]? {
  match
    @crater_network.synthetic_network_normalize_header_entries(
      raw_headers, "requestHeaders",
    ) {
    Ok(headers) => Some(headers)
    Err(message) => {
      self.send_error(request_id, "invalid argument", message)
      None
    }
  }
}

///|
fn BidiProtocol::apply_network_extra_headers_options(
  self : BidiProtocol,
  options : Map[String, Json],
) -> Unit {
  let normalized = match options.get("headers") {
    Some(Array(headers)) =>
      synthetic_network_header_string_map_from_entries(headers)
    _ => {}
  }
  match options.get("contexts") {
    Some(Array(items)) => {
      for item in items {
        match item {
          String(ctx_id) =>
            if normalized.length() == 0 {
              self.network_state.network_extra_headers_by_context.remove(ctx_id)
            } else {
              self.network_state.network_extra_headers_by_context[ctx_id] = synthetic_network_copy_string_map(
                normalized,
              )
            }
          _ => ()
        }
      }
      return
    }
    _ => ()
  }
  match options.get("userContexts") {
    Some(Array(items)) => {
      for item in items {
        match item {
          String(user_context_id) =>
            if normalized.length() == 0 {
              self.network_state.network_extra_headers_by_user_context.remove(
                user_context_id,
              )
            } else {
              self.network_state.network_extra_headers_by_user_context[user_context_id] = synthetic_network_copy_string_map(
                normalized,
              )
            }
          _ => ()
        }
      }
      return
    }
    _ => ()
  }
  synthetic_network_replace_string_map(
    self.network_state.network_extra_headers_global,
    normalized,
  )
}

///|
fn BidiProtocol::resolve_network_extra_header_entries(
  self : BidiProtocol,
  context_id : String,
  request_headers : Array[Json],
) -> Array[Json] {
  let merged : Map[String, String] = {}
  synthetic_network_merge_string_map(
    merged,
    self.network_state.network_extra_headers_global,
  )
  let user_context_id = self.resolve_storage_user_context(context_id)
  match
    self.network_state.network_extra_headers_by_user_context.get(
      user_context_id,
    ) {
    Some(headers) => synthetic_network_merge_string_map(merged, headers)
    None => ()
  }
  for scope_id in self.storage_context_ancestry(context_id) {
    match self.network_state.network_extra_headers_by_context.get(scope_id) {
      Some(headers) => synthetic_network_merge_string_map(merged, headers)
      None => ()
    }
  }
  synthetic_network_merge_string_map(
    merged,
    synthetic_network_header_string_map_from_entries(request_headers),
  )
  synthetic_network_header_entries_from_string_map(merged)
}

///|
fn BidiProtocol::build_network_headers_echo_payload(
  self : BidiProtocol,
  context_id : String,
  request_headers : Array[Json],
) -> String {
  let headers : Map[String, Json] = {}
  let mut has_user_agent = false
  let mut has_accept_language = false
  for
    header in self.resolve_network_extra_header_entries(
      context_id, request_headers,
    ) {
    let name = match synthetic_network_header_entry_name(header) {
      Some(name) => name
      None => continue
    }
    let value = match synthetic_network_header_entry_string_value(header) {
      Some(value) => value
      None => continue
    }
    if name == "user-agent" {
      has_user_agent = true
    }
    if name == "accept-language" {
      has_accept_language = true
    }
    headers[name] = Json::array([Json::string(value)])
  }
  if !has_user_agent {
    headers["user-agent"] = Json::array([
      Json::string(self.resolve_effective_user_agent(context_id)),
    ])
  }
  if !has_accept_language {
    headers["accept-language"] = Json::array([
      Json::string(self.resolve_effective_locale(context_id)),
    ])
  }
  make_object({ "headers": make_object(headers) }).stringify()
}

///|
fn synthetic_network_copy_string_map(
  source : Map[String, String],
) -> Map[String, String] {
  let copied : Map[String, String] = {}
  synthetic_network_merge_string_map(copied, source)
  copied
}

///|
fn synthetic_network_replace_string_map(
  target : Map[String, String],
  source : Map[String, String],
) -> Unit {
  let keys : Array[String] = []
  for key, _ in target {
    keys.push(key)
  }
  for key in keys {
    target.remove(key)
  }
  synthetic_network_merge_string_map(target, source)
}

///|
fn synthetic_network_merge_string_map(
  target : Map[String, String],
  source : Map[String, String],
) -> Unit {
  for key, value in source {
    target[key] = value
  }
}

///|
fn synthetic_network_header_string_map_from_entries(
  headers : Array[Json],
) -> Map[String, String] {
  let normalized : Map[String, String] = {}
  for header in headers {
    let name = match synthetic_network_header_entry_name(header) {
      Some(name) => name.to_lower()
      None => continue
    }
    let value = match synthetic_network_header_entry_string_value(header) {
      Some(value) => value
      None => continue
    }
    normalized[name] = value
  }
  normalized
}

///|
fn synthetic_network_header_entries_from_string_map(
  headers : Map[String, String],
) -> Array[Json] {
  let entries : Array[Json] = []
  for name, value in headers {
    entries.push(synthetic_network_header_string_entry(name, value))
  }
  entries
}