///|
pub suberror BodyError {
  InvalidJsonCharset
  InvalidJson
  InvalidText
}

///|
fn read_body(
  req_headers : Map[StringView, StringView],
  body_bytes : BytesView,
) -> HttpBody raise BodyError {
  let content_type = req_headers.get("Content-Type")
  if content_type is Some(content_type) {
    let content_type = parse_content_type(content_type)
    if content_type is Some(content_type) {
      return match content_type {
        { subtype: "json", .. } => {
          let json = @encoding/utf8.decode(body_bytes) catch {
            _ => raise BodyError::InvalidJsonCharset
          }
          Json(@json.parse(json) catch { _ => raise BodyError::InvalidJson })
        }
        { media_type: "text", .. } =>
          Text(
            @encoding/utf8.decode(body_bytes) catch {
              _ => raise BodyError::InvalidText
            },
          )
        { subtype: "x-www-form-urlencoded", .. } =>
          Form(parse_form_data(body_bytes))
        { subtype: "form-data" | "multipart", params, .. } => {
          let boundary = match params.get("boundary") {
            Some(b) => Some(b)
            None => params.get("BOUNDARY")
          }
          match boundary {
            Some(b) => Multipart(parse_multipart(body_bytes, b.to_string()))
            None => Bytes(body_bytes)
          }
        }
        _ => Bytes(body_bytes)
      }
    }
  }
  Bytes(body_bytes)
}

///|
priv struct ContentType {
  media_type : StringView
  subtype : StringView
  params : Map[StringView, StringView]
} derive(Show)

///|
fn parse_content_type(s : StringView) -> ContentType? {
  let parts = s.split(";").to_array()
  if parts.is_empty() {
    None
  } else {
    let main_part_str = parts[0].trim_space()
    let media_type_parts = main_part_str.split("/").to_array()
    if media_type_parts.length() != 2 {
      None
    } else {
      let media_type = media_type_parts[0].trim_space()
      let subtype = media_type_parts[1].trim_space()
      let params = {}
      for i in 1.. {
              let key = param_part[0:idx].trim_space()
              let value = param_part[idx + 1:].trim_space()
              params[key] = value
            }
            None => () // Ignore malformed parameters
          }
        } catch {
          _ => ()
        }
      }
      Some({ media_type, subtype, params })
    }
  }
}

///|
test "parse_content_type" {
  inspect(
    parse_content_type("application/json; charset=utf-8"),
    content=(
      #|Some({media_type: "application", subtype: "json", params: {"charset": "utf-8"}})
    ),
  )
}

///|
test "parse_form_data" {
  inspect(
    parse_form_data(b"name=John+Doe&age=30"),
    content=(
      #|{"name": "John Doe", "age": "30"}
    ),
  )
}