///|
/// Render the event section of `TraceStats` as a Markdown table.
pub fn TraceStats::event_table(self : TraceStats) -> String {
  let out = StringBuilder()
  out.write_string("| Event | Count | First step | Last step |\n")
  out.write_string("|---|---:|---:|---:|\n")
  if self.event_counts.is_empty() {
    out.write_string("| none | 0 | - | - |\n")
  } else {
    for item in self.event_counts {
      out.write_string(
        "| \{escape_table_cell(item.name)} | \{item.count} | \{format_step(item.first_step)} | \{format_step(item.last_step)} |\n",
      )
    }
  }
  out.to_string()
}

///|
/// Render the object section of `TraceStats` as a Markdown table.
pub fn TraceStats::object_table(self : TraceStats) -> String {
  let out = StringBuilder()
  out.write_string(
    "| Object | Kind | Appearances | Max entities | First step | Last step |\n",
  )
  out.write_string("|---|---|---:|---:|---:|---:|\n")
  if self.object_usage.is_empty() {
    out.write_string("| none | - | 0 | 0 | - | - |\n")
  } else {
    for item in self.object_usage {
      out.write_string(
        "| \{escape_table_cell(item.object_id)} | \{item.object_kind} | \{item.appearances} | \{item.max_entities} | \{format_step(item.first_step)} | \{format_step(item.last_step)} |\n",
      )
    }
  }
  out.to_string()
}

///|
/// Render the most referenced targets as a Markdown table.
pub fn TraceStats::target_table(self : TraceStats, limit? : Int = 10) -> String {
  let out = StringBuilder()
  out.write_string(
    "| Target | References | Event refs | Highlight refs | Events | Roles |\n",
  )
  out.write_string("|---|---:|---:|---:|---|---|\n")
  let targets = self.top_targets(limit~)
  if targets.is_empty() {
    out.write_string("| none | 0 | 0 | 0 | - | - |\n")
  } else {
    for item in targets {
      let label = escape_table_cell(
        target_label(item.object_id, item.entity_id),
      )
      let event_names = escape_table_cell(item.event_names.join(", "))
      let roles = escape_table_cell(item.highlight_roles.join(", "))
      out.write_string(
        "| \{label} | \{item.references} | \{item.event_references} | \{item.highlight_references} | \{event_names} | \{roles} |\n",
      )
    }
  }
  out.to_string()
}

///|
/// Render the full analysis as Markdown for READMEs, reports, and CI artifacts.
pub fn TraceStats::to_markdown(
  self : TraceStats,
  target_limit? : Int = 10,
) -> String {
  let out = StringBuilder()
  out.write_string("# FrontierLab trace analysis\n\n")
  out.write_string("- Title: \{self.title}\n")
  out.write_string("- Algorithm: \{self.algorithm}\n")
  out.write_string("- Steps: \{self.step_count}\n")
  out.write_string("- Completed: \{self.completed}\n")
  out.write_string("- Initial objects: \{self.initial_object_count}\n")
  out.write_string("- Max objects per scene: \{self.max_objects_per_scene}\n")
  out.write_string("- Max entities per scene: \{self.max_entities_per_scene}\n")
  out.write_string("- Annotations: \{self.annotation_count}\n")
  out.write_string("- Highlights: \{self.highlight_count}\n")
  out.write_string("- Summary attributes: \{self.summary_count}\n\n")
  out.write_string("## Events\n\n")
  out.write_string(self.event_table())
  out.write_string("\n## Objects\n\n")
  out.write_string(self.object_table())
  out.write_string("\n## Targets\n\n")
  out.write_string(self.target_table(limit=target_limit))
  out.to_string()
}

///|
pub fn AlgorithmTrace::analysis_markdown(
  self : AlgorithmTrace,
  target_limit? : Int = 10,
) -> String {
  self.analyze().to_markdown(target_limit~)
}

///|
fn escape_table_cell(value : String) -> String {
  value.replace_all(old="|", new="\\|").replace_all(old="\n", new="
") } ///| fn format_step(step : Int) -> String { if step < 0 { "initial" } else { step.to_string() } } ///| fn target_label(object_id : String, entity_id : String?) -> String { match entity_id { Some(entity) => "\{object_id}/\{entity}" None => object_id } }