///|
fn BidiProtocol::handle_network_set_cache_behavior(
  self : BidiProtocol,
  request_id : Int,
  params : Json?,
) -> Unit {
  let map = match params {
    Some(Object(map)) => map
    _ => {
      self.send_error(
        request_id, "invalid argument", "params must be an object",
      )
      return
    }
  }
  let cache_behavior = match
    get_map_field_with_alias(map, "cacheBehavior", "cache_behavior") {
    Some(String(cache_behavior)) => cache_behavior
    Some(_) => {
      self.send_error(
        request_id, "invalid argument", "cacheBehavior must be a string",
      )
      return
    }
    None => {
      self.send_error(request_id, "invalid argument", "Missing cacheBehavior")
      return
    }
  }
  if cache_behavior != "default" && cache_behavior != "bypass" {
    self.send_error(request_id, "invalid argument", "cacheBehavior is invalid")
    return
  }
  match map.get("contexts") {
    None | Some(Null) => {
      self.network_state.network_cache_behavior_global = cache_behavior
      let keys : Array[String] = []
      for key, _ in self.network_state.network_cache_behavior_by_context {
        keys.push(key)
      }
      for key in keys {
        self.network_state.network_cache_behavior_by_context.remove(key)
      }
    }
    Some(Array(items)) => {
      if items.length() == 0 {
        self.send_error(
          request_id, "invalid argument", "contexts must not be empty",
        )
        return
      }
      for item in items {
        match item {
          String(ctx_id) =>
            if !self.manager.has_session(ctx_id) {
              self.send_error(
                request_id,
                "no such frame",
                "Unknown context: " + ctx_id,
              )
              return
            } else {
              self.network_state.network_cache_behavior_by_context[ctx_id] = cache_behavior
            }
          _ => {
            self.send_error(
              request_id, "invalid argument", "contexts entries must be strings",
            )
            return
          }
        }
      }
    }
    Some(_) => {
      self.send_error(
        request_id, "invalid argument", "contexts must be an array",
      )
      return
    }
  }
  self.send_success(request_id, Some(make_object({})))
}

///|
fn BidiProtocol::handle_network_resolve_synthetic_response_overrides(
  self : BidiProtocol,
  request_id : Int,
  params : Json?,
) -> Unit {
  let map = match params {
    Some(Object(map)) => map
    _ => {
      self.send_error(
        request_id, "invalid argument", "params must be an object",
      )
      return
    }
  }
  let context_id = match map.get("context") {
    Some(String(context_id)) => context_id
    _ => {
      self.send_error(
        request_id, "invalid argument", "context must be a string",
      )
      return
    }
  }
  if !self.manager.has_session(context_id) {
    self.send_error(
      request_id,
      "no such frame",
      "Unknown context: " + context_id,
    )
    return
  }
  let url = match map.get("url") {
    Some(String(url)) => url
    _ => {
      self.send_error(request_id, "invalid argument", "url must be a string")
      return
    }
  }
  let http_method = match map.get("method") {
    Some(String(http_method)) => http_method
    _ => {
      self.send_error(request_id, "invalid argument", "method must be a string")
      return
    }
  }
  let request_headers = match
    self.normalize_synthetic_network_header_entries(
      request_id,
      get_map_field_with_alias(map, "requestHeaders", "request_headers"),
    ) {
    Some(request_headers) => request_headers
    None => return
  }
  let resolved_request_headers = self.resolve_network_extra_header_entries(
    context_id, request_headers,
  )
  let update_cache = match
    get_map_field_with_alias(map, "updateCache", "update_cache") {
    None | Some(Null) => true
    Some(True) => true
    Some(False) => false
    Some(_) => {
      self.send_error(
        request_id, "invalid argument", "updateCache must be a boolean",
      )
      return
    }
  }
  let overrides = self.resolve_synthetic_response_overrides_internal(
    context_id, url, http_method, resolved_request_headers, update_cache,
  )
  self.send_success(
    request_id,
    Some(
      make_object({
        "responseOverrides": self.synthetic_network_response_overrides_to_json(
          overrides,
        ),
      }),
    ),
  )
}

///|
fn BidiProtocol::resolve_synthetic_response_overrides_internal(
  self : BidiProtocol,
  context_id : String,
  url : String,
  http_method : String,
  request_headers : Array[Json],
  update_cache : Bool,
) -> @crater_network.SyntheticNetworkResponseOverrides {
  let mut status = 200
  let mut from_cache = false
  let mut protocol = "http/1.1"
  let mut auth_challenges : Json? = None
  let headers : Array[Json] = []
  let path = synthetic_network_request_path(url)
  let method_upper = http_method.to_upper()
  let cache_enabled = self.synthetic_network_cache_enabled(context_id)
  let cache_key = synthetic_network_cache_key(method_upper, url)
  let context_cache = self.network_state.network_cached_requests_by_context
    .get(context_id)
    .unwrap_or({})

  if url.has_prefix("data:") {
    protocol = "data"
  }

  if path.has_suffix("/redirect.py") {
    status = synthetic_network_int_query_param(url, "status", 302)
  } else if path.has_suffix("/status.py") {
    status = synthetic_network_int_query_param(url, "status", 200)
  } else if path.has_suffix("/cached.py") {
    status = synthetic_network_int_query_param(url, "status", 200)
    if cache_enabled && method_upper == "GET" {
      if context_cache.contains(cache_key) {
        from_cache = true
      } else if update_cache {
        context_cache[cache_key] = true
        self.network_state.network_cached_requests_by_context[context_id] = context_cache
      }
    }
  } else if path.has_suffix("/must-revalidate.py") {
    let return_304 = synthetic_network_request_header_string_value(
      request_headers, "return-304",
    )
    if cache_enabled &&
      (
        method_upper == "GET" ||
        method_upper == "HEAD" ||
        method_upper == "OPTIONS"
      ) &&
      return_304 == Some("true") &&
      context_cache.contains(cache_key) {
      status = 304
    } else {
      status = 200
      if cache_enabled &&
        (
          method_upper == "GET" ||
          method_upper == "HEAD" ||
          method_upper == "OPTIONS"
        ) &&
        update_cache {
        context_cache[cache_key] = true
        self.network_state.network_cached_requests_by_context[context_id] = context_cache
      }
    }
  } else if path.has_suffix("/authentication.py") {
    status = 401
    let realm = synthetic_network_first_query_value(url, "realm").unwrap_or(
      "testrealm",
    )
    auth_challenges = Some(
      Json::array([
        make_object({
          "scheme": Json::string("Basic"),
          "realm": Json::string(realm),
        }),
      ]),
    )
    headers.push(
      synthetic_network_header_string_entry(
        "WWW-Authenticate",
        "Basic realm=\"" + realm + "\"",
      ),
    )
  }

  if path.has_suffix("/headers.py") {
    for raw_header in synthetic_network_query_values(url, "header") {
      match find_substring(raw_header, ":", 0) {
        Some(idx) => {
          let name = raw_header.unsafe_substring(start=0, end=idx)
          let value_start = idx + 1
          let value = if value_start <= raw_header.length() {
            raw_header.unsafe_substring(
              start=value_start,
              end=raw_header.length(),
            )
          } else {
            ""
          }
          headers.push(synthetic_network_header_string_entry(name, value))
        }
        None => ()
      }
    }
  }

  let mime_type = synthetic_network_infer_mime_type(url, path)
  if mime_type != "" {
    headers.push(
      synthetic_network_header_string_entry("Content-Type", mime_type),
    )
  }

  let mut status_text = synthetic_network_status_text_for_code(status)
  if path.has_suffix("/serviceworker.html") {
    status_text = "OK from serviceworker"
  }
  let missing_auth_challenges = match auth_challenges {
    None => true
    Some(_) => false
  }
  if (status == 401 || status == 407) && missing_auth_challenges {
    auth_challenges = Some(Json::array([]))
  }

  {
    from_cache,
    headers,
    mime_type,
    protocol,
    status,
    status_text,
    auth_challenges,
  }
}

///|
fn BidiProtocol::synthetic_network_response_overrides_to_json(
  self : BidiProtocol,
  overrides : @crater_network.SyntheticNetworkResponseOverrides,
) -> Json {
  let _ = self
  @crater_network.synthetic_network_response_overrides_to_json(overrides)
}

///|
fn BidiProtocol::synthetic_network_cache_enabled(
  self : BidiProtocol,
  context_id : String,
) -> Bool {
  self.synthetic_network_cache_behavior(context_id) == "default"
}

///|
fn BidiProtocol::synthetic_network_cache_behavior(
  self : BidiProtocol,
  context_id : String,
) -> String {
  self.network_state.network_cache_behavior_by_context
  .get(context_id)
  .unwrap_or(self.network_state.network_cache_behavior_global)
}