///|
fn markdown_escape(value : String) -> String {
  value.replace_all(old="|", new="\\|").replace_all(old="\n", new=" ")
}

///|
fn severity_badge(severity : Severity) -> String {
  match severity {
    Error => "ERROR"
    Warn => "WARN"
    Info => "INFO"
  }
}

///|
fn append_heading(builder : StringBuilder, level : Int, title : String) -> Unit {
  let hashes = "#".repeat(level)
  builder.write_string(hashes + " " + title + "\n\n")
}

///|
fn append_score_table(builder : StringBuilder, report : DoctorReport) -> Unit {
  builder.write_string(
    "| Grade | Conclusion | Errors | Warnings | Info | Profile |\n",
  )
  builder.write_string("| --- | --- | ---: | ---: | ---: | --- |\n")
  builder.write_string(
    "| " +
    report.score.grade +
    " | " +
    report.conclusion() +
    " | " +
    "\{report.score.errors}" +
    " | " +
    "\{report.score.warnings}" +
    " | " +
    "\{report.score.infos}" +
    " | " +
    report.profile +
    " |\n\n",
  )
}

///|
fn append_diagnostic_table(
  builder : StringBuilder,
  diagnostics : Array[Diagnostic],
) -> Unit {
  if diagnostics.length() == 0 {
    builder.write_string(
      "No diagnostics. The selected policy considers this project ready.\n\n",
    )
    return
  }
  builder.write_string("| Severity | Rule | Finding | Location |\n")
  builder.write_string("| --- | --- | --- | --- |\n")
  for diagnostic in diagnostics {
    let location = match diagnostic.location {
      Some(value) => value.path + ":" + "\{value.line}"
      None => "-"
    }
    builder.write_string(
      "| " +
      severity_badge(diagnostic.severity) +
      " | `" +
      diagnostic.code +
      "` | " +
      markdown_escape(diagnostic.message) +
      " | `" +
      markdown_escape(location) +
      "` |\n",
    )
  }
  builder.write_string("\n")
}

///|
fn append_checklist(builder : StringBuilder, checklist : Array[String]) -> Unit {
  for item in checklist {
    builder.write_string("- [ ] " + item + "\n")
  }
  builder.write_string("\n")
}

///|
fn append_remediation(builder : StringBuilder, plan : RemediationPlan) -> Unit {
  if plan.tasks.length() == 0 {
    builder.write_string("No remediation tasks are required.\n\n")
    return
  }
  let mut index = 1
  for task in plan.tasks {
    builder.write_string("### " + "\{index}" + ". " + task.title + "\n\n")
    builder.write_string("Rules: `" + task.rule_ids.join("`, `") + "`\n\n")
    for action in task.actions {
      builder.write_string("- " + action + "\n")
    }
    builder.write_string("\n")
    index += 1
  }
}

///|
/// Render a reviewer-friendly report for pull requests and release notes.
pub fn render_markdown_report(report : DoctorReport) -> String {
  let builder = StringBuilder()
  append_heading(builder, 1, "Moon Doctor report")
  builder.write_string("Project: `" + report.root + "`\n\n")
  append_score_table(builder, report)
  append_heading(builder, 2, "Diagnostics")
  append_diagnostic_table(builder, report.diagnostics)
  append_heading(builder, 2, "Release checklist")
  append_checklist(builder, report.checklist)
  append_heading(builder, 2, "Remediation plan")
  append_remediation(builder, build_remediation_plan(report))
  builder.to_string()
}

///|
pub fn markdown_report_json(report : DoctorReport) -> Json {
  let plan = build_remediation_plan(report)
  Json::object({
    "markdown": Json::string(render_markdown_report(report)),
    "remediation": remediation_plan_to_json(plan),
  })
}