///|
/// Mutable fluent builder. Build returns a structured error on invalid state.
pub struct ProblemBuilder {
  mut type_raw : String?
  mut title : String?
  mut status : Int?
  mut detail : String?
  mut instance : String?
  extensions : Array[ExtensionMember]
  mut error : ProblemError?
}

///|
pub fn ProblemBuilder::new() -> ProblemBuilder {
  {
    type_raw: None,
    title: None,
    status: None,
    detail: None,
    instance: None,
    extensions: [],
    error: None,
  }
}

///|
pub fn ProblemBuilder::type_uri(
  self : ProblemBuilder,
  value : String,
) -> ProblemBuilder {
  self.type_raw = Some(value)
  self
}

///|
pub fn ProblemBuilder::title(
  self : ProblemBuilder,
  value : String,
) -> ProblemBuilder {
  self.title = Some(value)
  self
}

///|
pub fn ProblemBuilder::status(
  self : ProblemBuilder,
  value : Int,
) -> ProblemBuilder {
  self.status = Some(value)
  self
}

///|
pub fn ProblemBuilder::detail(
  self : ProblemBuilder,
  value : String,
) -> ProblemBuilder {
  self.detail = Some(value)
  self
}

///|
pub fn ProblemBuilder::instance(
  self : ProblemBuilder,
  value : String,
) -> ProblemBuilder {
  self.instance = Some(value)
  self
}

///|
pub fn ProblemBuilder::extension(
  self : ProblemBuilder,
  name : String,
  value : Json,
) -> ProblemBuilder {
  if is_reserved_member(name) {
    self.error = Some(
      problem_error(
        Builder,
        DuplicateReservedExtension,
        "extension collides with reserved member: \{name}",
      ),
    )
  } else {
    let mut replaced = false
    for i = 0; i < self.extensions.length(); i = i + 1 {
      if self.extensions[i].name() == name {
        self.extensions[i] = extension_member(name, value)
        replaced = true
      }
    }
    if !replaced {
      self.extensions.push(extension_member(name, value))
    }
  }
  self
}

///|
pub fn ProblemBuilder::build(
  self : ProblemBuilder,
) -> Result[ProblemDetails, ProblemError] {
  match self.error {
    Some(error) => Err(error)
    None => {
      let value = problem_details(
        self.type_raw,
        self.title,
        self.status,
        self.detail,
        self.instance,
        self.extensions.copy(),
      )
      let issues = validate_problem(value)
      if issues.is_empty() {
        Ok(value)
      } else {
        Err(problem_error(Builder, InvalidBuilderState, issues[0].context()))
      }
    }
  }
}

///|
pub fn problem(
  status : Int,
  title : String,
) -> Result[ProblemDetails, ProblemError] {
  ProblemBuilder::new().status(status).title(title).build()
}

///|
pub fn about_blank(status : Int) -> Result[ProblemDetails, ProblemError] {
  let builder = ProblemBuilder::new().status(status)
  match status_reason(status) {
    Some(reason) => builder.title(reason).build()
    None => builder.build()
  }
}