///|
/// Human-facing simulation report.
pub(all) struct SimulationReport {
  name : String
  size : Size
  steps : Int
  mass : Double
  max_speed : Double
  kinetic_energy : Double
  density_variation : Double
  stable : Bool
  health_pass : Bool
} derive(Debug)

///|
/// Build a report from a reusable simulation.
pub fn Simulation::report(
  self : Simulation,
  name? : String = "simulation",
) -> SimulationReport {
  let stability = self.stability()
  let health = self.health()
  {
    name,
    size: self.size,
    steps: self.step_count,
    mass: stability.mass,
    max_speed: stability.max_speed,
    kinetic_energy: self.kinetic_energy(),
    density_variation: self.density_variation(),
    stable: stability.recommended,
    health_pass: health.pass,
  }
}

///|
/// Format a report as CSV.
pub fn SimulationReport::to_csv(self : SimulationReport) -> String {
  "\{self.name},\{self.size.width},\{self.size.height},\{self.steps},\{self.mass},\{self.max_speed},\{self.kinetic_energy},\{self.density_variation},\{self.stable},\{self.health_pass}\n"
}

///|
/// Format a report as a Markdown table row.
pub fn SimulationReport::to_markdown(self : SimulationReport) -> String {
  "| \{self.name} | \{self.size.width}x\{self.size.height} | \{self.steps} | \{self.mass} | \{self.max_speed} | \{self.kinetic_energy} | \{self.density_variation} | \{self.stable} | \{self.health_pass} |"
}

///|
/// Format a report as a small JSON object.
pub fn SimulationReport::to_json(self : SimulationReport) -> String {
  "{\"name\":\"\{self.name}\",\"width\":\{self.size.width},\"height\":\{self.size.height},\"steps\":\{self.steps},\"mass\":\{self.mass},\"max_speed\":\{self.max_speed},\"kinetic_energy\":\{self.kinetic_energy},\"stable\":\{self.stable},\"health_pass\":\{self.health_pass}}"
}

///|
/// Report CSV header.
pub fn simulation_report_header() -> String {
  "name,width,height,steps,mass,max_speed,kinetic_energy,density_variation,stable,health_pass\n"
}

///|
/// Combine several reports into a Markdown table.
pub fn reports_to_markdown(reports : ArrayView[SimulationReport]) -> String {
  let builder = StringBuilder(size_hint=256 + reports.length() * 128)
  builder.write_string(
    "| name | size | steps | mass | max speed | energy | density variation | stable | health |\n",
  )
  builder.write_string("|---|---:|---:|---:|---:|---:|---:|:---:|:---:|\n")
  for report in reports {
    builder.write_string(report.to_markdown())
    builder.write_char('\n')
  }
  builder.to_string()
}

///|
/// Return true when every report passes its numerical health gate.
pub fn reports_are_healthy(reports : ArrayView[SimulationReport]) -> Bool {
  let mut result = true
  for report in reports {
    result = result && report.health_pass
  }
  result
}