///|
fn append_section(out : StringBuilder, title : String, body : String) -> Unit {
  out.write_string("\n## ")
  out.write_string(title)
  out.write_char('\n')
  out.write_string(body)
  if !body.has_suffix("\n") {
    out.write_char('\n')
  }
}

///|
fn append_result_section(
  out : StringBuilder,
  title : String,
  result : Result[String, PackError],
) -> Unit {
  match result {
    Ok(body) => append_section(out, title, body)
    Err(err) => append_section(out, title, error_message(err))
  }
}

///|
fn density_report_for_layout(layout : Layout) -> Result[String, PackError] {
  let cell_w = (layout.bin.width / 8).max(1)
  let cell_h = (layout.bin.height / 8).max(1)
  match density_grid(layout, cell_width=cell_w, cell_height=cell_h) {
    Err(err) => Err(err)
    Ok(report) => Ok(to_density_report(report))
  }
}

///|
fn constraint_report_for_layout(layout : Layout) -> Result[String, PackError] {
  match check_constraints(layout, LayoutConstraints::strict()) {
    Err(err) => Err(err)
    Ok(violations) => Ok(to_constraint_report(violations))
  }
}

///|
/// Build a complete text audit for a validated layout.
pub fn full_layout_audit(layout : Layout) -> Result[String, PackError] {
  match validate_layout(layout) {
    Err(err) => Err(err)
    Ok(_) => {
      let out = StringBuilder()
      out.write_string("rectloom full layout audit\n")
      out.write_string("signature=")
      match layout_signature(layout) {
        Ok(signature) => out.write_string(signature)
        Err(err) => out.write_string(error_message(err))
      }
      out.write_char('\n')
      append_section(out, "layout", to_report(layout))
      append_result_section(out, "statistics", to_analysis_report(layout))
      append_result_section(out, "diagnostics", diagnose_layout_report(layout))
      append_result_section(
        out,
        "constraints",
        constraint_report_for_layout(layout),
      )
      append_result_section(out, "density", density_report_for_layout(layout))
      Ok(out.to_string())
    }
  }
}

///|
/// Parse, pack, and audit a text spec in one call.
pub fn audit_spec(source : String) -> Result[String, PackError] {
  match pack_spec(source) {
    Err(err) => Err(err)
    Ok(layout) => full_layout_audit(layout)
  }
}

///|
fn append_source_summary(
  out : StringBuilder,
  rects : ArrayView[Rect],
  options : PackOptions,
) -> Unit {
  append_section(
    out,
    "source",
    to_rect_stats_report(summarize_rects_for_bin(rects, options.bin)),
  )
  append_section(
    out,
    "source-diagnostics",
    to_diagnostics_report(diagnose_rects_for_bin(rects, options.bin)),
  )
}

///|
/// Pack, compare option variants, and return a combined audit report.
pub fn packing_audit(
  rects : ArrayView[Rect],
  options : PackOptions,
) -> Result[String, PackError] {
  match compare_packers(rects, options) {
    Err(err) => Err(err)
    Ok(comparison) => {
      let out = StringBuilder()
      out.write_string("rectloom packing audit\n")
      append_source_summary(out, rects, options)
      append_section(out, "comparison", to_comparison_report(comparison))
      append_section(out, "best-layout", to_report(comparison.best.layout))
      append_result_section(
        out,
        "best-statistics",
        to_analysis_report(comparison.best.layout),
      )
      append_result_section(
        out,
        "best-diagnostics",
        diagnose_layout_report(comparison.best.layout),
      )
      Ok(out.to_string())
    }
  }
}

///|
/// Run a complete audit against a built-in fixture family.
pub fn fixture_audit(
  kind : FixtureKind,
  count : Int,
) -> Result[String, PackError] {
  let rects = fixture_rects(kind, count)
  let options = fixture_options(kind)
  match packing_audit(rects, options) {
    Err(err) => Err(err)
    Ok(report) => {
      let out = StringBuilder()
      out.write_string("fixture=")
      out.write_string(fixture_name(kind))
      out.write_char('\n')
      out.write_string("description=")
      out.write_string(fixture_description(kind))
      out.write_char('\n')
      out.write_string(report)
      Ok(out.to_string())
    }
  }
}

///|
/// Produce a compact one-screen summary for command line demos.
pub fn compact_audit_summary(layout : Layout) -> Result[String, PackError] {
  match analyze_layout(layout) {
    Err(err) => Err(err)
    Ok(stats) => {
      let out = StringBuilder()
      out.write_string("algorithm=")
      out.write_string(algorithm_name(layout.algorithm))
      out.write_string(" placed=")
      out.write_string("\{stats.placed_count}")
      out.write_string(" unpacked=")
      out.write_string("\{stats.unpacked_count}")
      out.write_string(" occupancy=")
      out.write_string("\{stats.occupancy_permille}/1000")
      out.write_string(" compactness=")
      out.write_string("\{stats.compactness_score}/1000")
      out.write_string(" fragmentation=")
      out.write_string("\{stats.fragmentation_score}/1000")
      Ok(out.to_string())
    }
  }
}