///|
pub struct HarContent {
  size_value : Double
  compression_value : Double?
  mime_type_value : String
  text_value : String?
  encoding_value : String?
  comment_value : String?
} derive(Eq, Debug)

///|
pub fn HarContent::new(
  size : Double,
  mime_type : String,
  compression? : Double,
  text? : String,
  encoding? : String,
  comment? : String,
) -> Result[HarContent, HarError] {
  build_content(
    size, mime_type, compression, text, encoding, comment, "$.content",
  )
}

///|
pub fn HarContent::size(self : HarContent) -> Double {
  self.size_value
}

///|
pub fn HarContent::known_size(self : HarContent) -> Double? {
  if self.size_value < 0.0 {
    None
  } else {
    Some(self.size_value)
  }
}

///|
pub fn HarContent::compression(self : HarContent) -> Double? {
  self.compression_value
}

///|
pub fn HarContent::mime_type(self : HarContent) -> String {
  self.mime_type_value
}

///|
pub fn HarContent::text(self : HarContent) -> String? {
  self.text_value
}

///|
pub fn HarContent::encoding(self : HarContent) -> String? {
  self.encoding_value
}

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

///|
pub fn HarContent::is_base64(self : HarContent) -> Bool {
  match self.encoding_value {
    Some(value) => value.to_lower() == "base64"
    None => false
  }
}

///|
pub fn HarContent::media_type(self : HarContent) -> String {
  let parts : Array[String] = self.mime_type_value
    .split(";")
    .map(value => value.to_owned())
    .collect()
  match parts {
    [] => ""
    [first, ..] => first.trim().to_owned().to_lower()
  }
}

///|
pub fn HarContent::type_group(self : HarContent) -> String {
  let media_type = self.media_type()
  if media_type.has_prefix("text/") {
    "text"
  } else if media_type.has_prefix("image/") {
    "image"
  } else if media_type.has_prefix("audio/") {
    "audio"
  } else if media_type.has_prefix("video/") {
    "video"
  } else if media_type.contains("javascript") {
    "script"
  } else if media_type.contains("json") || media_type.contains("xml") {
    "data"
  } else if media_type == "" {
    "unknown"
  } else {
    "other"
  }
}

///|
fn build_content(
  size : Double,
  mime_type : String,
  compression : Double?,
  text : String?,
  encoding : String?,
  comment : String?,
  path : String,
) -> Result[HarContent, HarError] {
  if size.trunc() != size {
    return Err(
      HarError::new(
        InvalidSize,
        append_json_field(path, "size"),
        "content size must be an integer",
      ),
    )
  }
  if size < -1.0 {
    return Err(
      HarError::new(
        InvalidSize,
        append_json_field(path, "size"),
        "content size must be -1 or non-negative",
      ),
    )
  }
  match compression {
    Some(value) =>
      if value.trunc() != value {
        return Err(
          HarError::new(
            InvalidSize,
            append_json_field(path, "compression"),
            "content compression must be an integer",
          ),
        )
      } else if value < 0.0 {
        return Err(
          HarError::new(
            InvalidSize,
            append_json_field(path, "compression"),
            "content compression must be non-negative",
          ),
        )
      }
    None => ()
  }
  match encoding {
    Some(value) =>
      if value.to_lower() != "base64" {
        return Err(
          HarError::new(
            InvalidEncoding,
            append_json_field(path, "encoding"),
            "HAR content encoding must be base64 when present",
          ),
        )
      }
    None => ()
  }
  Ok({
    size_value: size,
    compression_value: compression,
    mime_type_value: mime_type,
    text_value: text,
    encoding_value: encoding,
    comment_value: comment,
  })
}

///|
fn parse_content_cursor(cursor : JsonCursor) -> Result[HarContent, HarError] {
  let size = match cursor.required_number("size") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let compression = match cursor.optional_number("compression") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let mime_type = match cursor.required_string("mimeType") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let text = match cursor.optional_string("text") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let encoding = match cursor.optional_string("encoding") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let comment = match cursor.optional_string("comment") {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  match
    build_content(
      size,
      mime_type,
      compression,
      text,
      encoding,
      comment,
      cursor.path(),
    ) {
    Ok(value) => Ok(value)
    Err(error) => Err(error)
  }
}

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