///|
/// Hard, objectively verifiable semantic issue.
pub(all) enum ValidationIssueKind {
  InvalidTypeUri
  InvalidInstanceUri
  InvalidStatusCode
} derive(Eq, Debug)

///|
pub struct ValidationIssue {
  kind : ValidationIssueKind
  context : String
} derive(Eq, Debug)

///|
pub fn ValidationIssue::kind(self : ValidationIssue) -> ValidationIssueKind {
  self.kind
}

///|
pub fn ValidationIssue::context(self : ValidationIssue) -> String {
  self.context
}

///|
pub fn ValidationIssueKind::name(self : ValidationIssueKind) -> String {
  match self {
    InvalidTypeUri => "InvalidTypeUri"
    InvalidInstanceUri => "InvalidInstanceUri"
    InvalidStatusCode => "InvalidStatusCode"
  }
}

///|
/// Validate semantic constraints separately from parsing and audit advice.
pub fn validate_problem(problem : ProblemDetails) -> Array[ValidationIssue] {
  let issues : Array[ValidationIssue] = []
  match problem.type_raw() {
    Some(value) =>
      match validate_uri_reference(value) {
        Err(error) =>
          issues.push({ kind: InvalidTypeUri, context: error.context() })
        Ok(_) => ()
      }
    None => ()
  }
  match problem.instance() {
    Some(value) =>
      match validate_uri_reference(value) {
        Err(error) =>
          issues.push({ kind: InvalidInstanceUri, context: error.context() })
        Ok(_) => ()
      }
    None => ()
  }
  match problem.status() {
    Some(code) if !is_valid_http_status(code) =>
      issues.push({
        kind: InvalidStatusCode,
        context: "status must be between 100 and 599",
      })
    _ => ()
  }
  issues
}

///|
pub fn is_valid_problem(problem : ProblemDetails) -> Bool {
  validate_problem(problem).is_empty()
}