///|
pub struct HarRequest {
  method_value : String
  url_value : String
  http_version_value : String
  cookies_value : Array[HarCookie]
  headers_value : Array[HarNameValue]
  query_value : Array[HarNameValue]
  post_data_value : HarPostData?
  headers_size_value : Double
  body_size_value : Double
  comment_value : String?
} derive(Eq, Debug)

///|
pub fn HarRequest::request_method(self : HarRequest) -> String {
  self.method_value
}

///|
pub fn HarRequest::url(self : HarRequest) -> String {
  self.url_value
}

///|
pub fn HarRequest::http_version(self : HarRequest) -> String {
  self.http_version_value
}

///|
pub fn HarRequest::cookies(self : HarRequest) -> Array[HarCookie] {
  self.cookies_value.copy()
}

///|
pub fn HarRequest::headers(self : HarRequest) -> Array[HarNameValue] {
  self.headers_value.copy()
}

///|
pub fn HarRequest::query(self : HarRequest) -> Array[HarNameValue] {
  self.query_value.copy()
}

///|
pub fn HarRequest::post_data(self : HarRequest) -> HarPostData? {
  self.post_data_value
}

///|
pub fn HarRequest::headers_size(self : HarRequest) -> Double {
  self.headers_size_value
}

///|
pub fn HarRequest::body_size(self : HarRequest) -> Double {
  self.body_size_value
}

///|
pub fn HarRequest::known_headers_size(self : HarRequest) -> Double? {
  known_message_size(self.headers_size_value)
}

///|
pub fn HarRequest::known_body_size(self : HarRequest) -> Double? {
  known_message_size(self.body_size_value)
}

///|
pub fn HarRequest::comment(self : HarRequest) -> String? {
  self.comment_value
}

///|
pub fn HarRequest::header_values(
  self : HarRequest,
  name : String,
) -> Array[String] {
  let values : Array[String] = []
  for header in self.headers_value {
    if header.name_equals(name) {
      values.push(header.value())
    }
  }
  values
}

///|
pub fn HarRequest::query_values(
  self : HarRequest,
  name : String,
) -> Array[String] {
  let values : Array[String] = []
  for parameter in self.query_value {
    if parameter.name() == name {
      values.push(parameter.value())
    }
  }
  values
}

///|
pub fn HarRequest::cookie_values(
  self : HarRequest,
  name : String,
) -> Array[String] {
  let values : Array[String] = []
  for cookie in self.cookies_value {
    if cookie.name() == name {
      values.push(cookie.value())
    }
  }
  values
}

///|
pub fn HarRequest::has_body(self : HarRequest) -> Bool {
  self.post_data_value is Some(_) || self.body_size_value > 0.0
}

///|
pub fn HarRequest::is_secure(self : HarRequest) -> Bool {
  self.url_value.to_lower().has_prefix("https://")
}

///|
pub fn HarRequest::method_is(self : HarRequest, expected : String) -> Bool {
  self.method_value.to_upper() == expected.to_upper()
}

///|
pub struct HarResponse {
  status_value : Double
  status_text_value : String
  http_version_value : String
  cookies_value : Array[HarCookie]
  headers_value : Array[HarNameValue]
  content_value : HarContent
  redirect_url_value : String
  headers_size_value : Double
  body_size_value : Double
  comment_value : String?
} derive(Eq, Debug)

///|
pub fn HarResponse::status(self : HarResponse) -> Double {
  self.status_value
}

///|
pub fn HarResponse::status_text(self : HarResponse) -> String {
  self.status_text_value
}

///|
pub fn HarResponse::http_version(self : HarResponse) -> String {
  self.http_version_value
}

///|
pub fn HarResponse::cookies(self : HarResponse) -> Array[HarCookie] {
  self.cookies_value.copy()
}

///|
pub fn HarResponse::headers(self : HarResponse) -> Array[HarNameValue] {
  self.headers_value.copy()
}

///|
pub fn HarResponse::content(self : HarResponse) -> HarContent {
  self.content_value
}

///|
pub fn HarResponse::redirect_url(self : HarResponse) -> String {
  self.redirect_url_value
}

///|
pub fn HarResponse::headers_size(self : HarResponse) -> Double {
  self.headers_size_value
}

///|
pub fn HarResponse::body_size(self : HarResponse) -> Double {
  self.body_size_value
}

///|
pub fn HarResponse::known_headers_size(self : HarResponse) -> Double? {
  known_message_size(self.headers_size_value)
}

///|
pub fn HarResponse::known_body_size(self : HarResponse) -> Double? {
  known_message_size(self.body_size_value)
}

///|
pub fn HarResponse::comment(self : HarResponse) -> String? {
  self.comment_value
}

///|
pub fn HarResponse::header_values(
  self : HarResponse,
  name : String,
) -> Array[String] {
  let values : Array[String] = []
  for header in self.headers_value {
    if header.name_equals(name) {
      values.push(header.value())
    }
  }
  values
}

///|
pub fn HarResponse::status_class(self : HarResponse) -> String {
  if self.status_value >= 100.0 && self.status_value < 200.0 {
    "informational"
  } else if self.status_value < 300.0 {
    "success"
  } else if self.status_value < 400.0 {
    "redirect"
  } else if self.status_value < 500.0 {
    "client_error"
  } else if self.status_value < 600.0 {
    "server_error"
  } else {
    "other"
  }
}

///|
pub fn HarResponse::is_success(self : HarResponse) -> Bool {
  self.status_value >= 200.0 && self.status_value < 300.0
}

///|
pub fn HarResponse::is_redirect(self : HarResponse) -> Bool {
  self.status_value >= 300.0 && self.status_value < 400.0
}

///|
pub fn HarResponse::is_error(self : HarResponse) -> Bool {
  self.status_value >= 400.0
}

///|
fn known_message_size(value : Double) -> Double? {
  if value < 0.0 {
    None
  } else {
    Some(value)
  }
}

///|
fn validate_message_size(
  value : Double,
  path : String,
) -> Result[Unit, HarError] {
  if value.trunc() != value {
    Err(HarError::new(InvalidSize, path, "message size must be an integer"))
  } else if value < -1.0 {
    Err(
      HarError::new(
        InvalidSize,
        path,
        "message size must be -1 or non-negative",
      ),
    )
  } else {
    Ok(())
  }
}

///|
fn validate_request_method(
  verb : String,
  path : String,
) -> Result[Unit, HarError] {
  if verb.trim().is_empty() {
    return Err(HarError::new(InvalidMethod, path, "method cannot be empty"))
  }
  for character in verb {
    let code = character.to_int()
    if code <= 32 || code >= 127 {
      return Err(
        HarError::new(
          InvalidMethod,
          path,
          "method must contain visible ASCII token characters",
        ),
      )
    }
  }
  Ok(())
}

///|
fn validate_request_url(url : String, path : String) -> Result[Unit, HarError] {
  if url.trim().is_empty() {
    Err(HarError::new(InvalidUrl, path, "request URL cannot be empty"))
  } else {
    Ok(())
  }
}

///|
fn parse_request_cursor(cursor : JsonCursor) -> Result[HarRequest, HarError] {
  let verb = match cursor.required_string("method") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  match
    validate_request_method(verb, append_json_field(cursor.path(), "method")) {
    Ok(_) => ()
    Err(error) => return Err(error)
  }
  let url = match cursor.required_string("url") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  match validate_request_url(url, append_json_field(cursor.path(), "url")) {
    Ok(_) => ()
    Err(error) => return Err(error)
  }
  let http_version = match cursor.required_string("httpVersion") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let cookie_cursors = match cursor.required_array("cookies") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let cookies = match parse_cookie_array(cookie_cursors) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let header_cursors = match cursor.required_array("headers") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let headers = match parse_name_value_array(header_cursors) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let query_cursors = match cursor.required_array("queryString") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let query = match parse_name_value_array(query_cursors) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let post_cursor = match cursor.optional_object("postData") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let post_data = match post_cursor {
    Some(value) =>
      match parse_post_data_cursor(value) {
        Ok(post) => Some(post)
        Err(error) => return Err(error)
      }
    None => None
  }
  let headers_size = match cursor.required_number("headersSize") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  match
    validate_message_size(
      headers_size,
      append_json_field(cursor.path(), "headersSize"),
    ) {
    Ok(_) => ()
    Err(error) => return Err(error)
  }
  let body_size = match cursor.required_number("bodySize") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  match
    validate_message_size(
      body_size,
      append_json_field(cursor.path(), "bodySize"),
    ) {
    Ok(_) => ()
    Err(error) => return Err(error)
  }
  let comment = match cursor.optional_string("comment") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  Ok({
    method_value: verb,
    url_value: url,
    http_version_value: http_version,
    cookies_value: cookies,
    headers_value: headers,
    query_value: query,
    post_data_value: post_data,
    headers_size_value: headers_size,
    body_size_value: body_size,
    comment_value: comment,
  })
}

///|
fn parse_response_cursor(cursor : JsonCursor) -> Result[HarResponse, HarError] {
  let status = match cursor.required_number("status") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  if status.trunc() != status {
    return Err(
      HarError::new(
        InvalidStatus,
        append_json_field(cursor.path(), "status"),
        "status must be an integer",
      ),
    )
  }
  if status < 0.0 || status > 999.0 {
    return Err(
      HarError::new(
        InvalidStatus,
        append_json_field(cursor.path(), "status"),
        "status must be between 0 and 999",
      ),
    )
  }
  let status_text = match cursor.required_string("statusText") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let http_version = match cursor.required_string("httpVersion") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let cookie_cursors = match cursor.required_array("cookies") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let cookies = match parse_cookie_array(cookie_cursors) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let header_cursors = match cursor.required_array("headers") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let headers = match parse_name_value_array(header_cursors) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let content_cursor = match cursor.required_object("content") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let content = match parse_content_cursor(content_cursor) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let redirect_url = match cursor.required_string("redirectURL") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let headers_size = match cursor.required_number("headersSize") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  match
    validate_message_size(
      headers_size,
      append_json_field(cursor.path(), "headersSize"),
    ) {
    Ok(_) => ()
    Err(error) => return Err(error)
  }
  let body_size = match cursor.required_number("bodySize") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  match
    validate_message_size(
      body_size,
      append_json_field(cursor.path(), "bodySize"),
    ) {
    Ok(_) => ()
    Err(error) => return Err(error)
  }
  let comment = match cursor.optional_string("comment") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  Ok({
    status_value: status,
    status_text_value: status_text,
    http_version_value: http_version,
    cookies_value: cookies,
    headers_value: headers,
    content_value: content,
    redirect_url_value: redirect_url,
    headers_size_value: headers_size,
    body_size_value: body_size,
    comment_value: comment,
  })
}

///|
pub fn parse_request(input : String) -> Result[HarRequest, HarError] {
  let cursor = match parse_json_cursor(input) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  parse_request_cursor(cursor)
}

///|
pub fn parse_response(input : String) -> Result[HarResponse, HarError] {
  let cursor = match parse_json_cursor(input) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  parse_response_cursor(cursor)
}