///|
fn state_label(state : AttributeState) -> String {
  match state {
    Set => "set"
    Unset => "unset"
    Value(value) => value
    Unspecified => "unspecified"
  }
}

///|
fn severity_label(severity : Severity) -> String {
  match severity {
    Info => "info"
    Warning => "warning"
    Error => "error"
  }
}

///|
fn sorted_attributes(
  attributes : Array[ResolvedAttribute],
) -> Array[ResolvedAttribute] {
  let result = attributes.copy()
  result.sort_by((left, right) => left.name.lexical_compare(right.name))
  result
}

///|
/// Renders an evaluation as compact, stable text suitable for terminals.
pub fn Evaluation::to_text(self : Evaluation) -> String {
  let output = StringBuilder()
  output.write_string("path: \{self.path}\n")
  let attributes = sorted_attributes(self.attributes)
  if attributes.length() == 0 {
    output.write_string("attributes: (none)\n")
  } else {
    output.write_string("attributes:\n")
    for attribute in attributes {
      output.write_string(
        "  \{attribute.name}: \{state_label(attribute.state)} " +
        "(\{attribute.source}:\{attribute.line}, \{attribute.pattern})\n",
      )
    }
  }
  output.to_string()
}

///|
/// Renders an evaluation as pretty JSON with attributes sorted by name.
pub fn Evaluation::to_json_string(self : Evaluation) -> String {
  let normalized : Evaluation = {
    path: self.path,
    attributes: sorted_attributes(self.attributes),
    matched_rules: self.matched_rules.copy(),
  }
  ToJson::to_json(normalized).stringify(indent=2)
}

///|
/// Renders diagnostics and policy findings as a Markdown table.
pub fn AuditReport::to_markdown(self : AuditReport) -> String {
  let output = StringBuilder()
  output.write_string("# MoonGitAttrs audit\n\n")
  if self.diagnostics.length() == 0 && self.findings.length() == 0 {
    output.write_string("No diagnostics or policy findings.\n")
    return output.to_string()
  }
  output.write_string("| Severity | Code | Source | Line | Message |\n")
  output.write_string("| --- | --- | --- | ---: | --- |\n")
  for item in self.diagnostics {
    output.write_string(
      "| \{severity_label(item.severity)} | \{item.code} | " +
      "\{item.source} | \{item.line} | \{item.message} |\n",
    )
  }
  for item in self.findings {
    output.write_string(
      "| \{severity_label(item.severity)} | \{item.code} | " +
      "\{item.source} | \{item.line} | \{item.message} |\n",
    )
  }
  output.to_string()
}

///|
/// Renders an audit report as pretty JSON.
pub fn AuditReport::to_json_string(self : AuditReport) -> String {
  ToJson::to_json(self).stringify(indent=2)
}