// ============================================================
// Application scenario harness
//
// A Scenario gives a project an explicit, repeatable way to exercise a tree
// with a named blackboard fixture. This is intentionally backend-neutral and
// is useful for acceptance demos, performance regressions, and examples.
// ============================================================

///|
/// A named tree workload with a fixed frame budget.
pub struct Scenario {
  name : String
  root : Node
  bb : Blackboard
  frames : Int
}

///|
/// Construct a scenario.
pub fn Scenario::new(
  name : String,
  root : Node,
  bb : Blackboard,
  frames : Int,
) -> Scenario {
  { name, root, bb, frames: if frames > 0 { frames } else { 0 } }
}

///|
/// Execute a scenario and return its deterministic outcome report.
pub fn Scenario::run(self : Scenario) -> BenchmarkResult {
  benchmark_node(self.name, self.root, self.bb, self.frames)
}

///|
/// Execute several scenarios in declaration order.
pub fn run_scenarios(scenarios : Array[Scenario]) -> Array[BenchmarkResult] {
  let results : Array[BenchmarkResult] = []
  for scenario in scenarios {
    results.push(scenario.run())
  }
  results
}

///|
/// Format results as a CSV document with a stable header.
pub fn benchmark_csv(results : Array[BenchmarkResult]) -> String {
  let output = StringBuilder::new()
  output.write_string("scenario,frames,successes,failures,running\n")
  for result in results {
    output.write_string(result.to_csv())
    output.write_string("\n")
  }
  output.to_string()
}

///|
/// Build a small but representative workload matrix for local regression.
pub fn standard_scenarios() -> Array[Scenario] {
  let patrol_bb = Blackboard::new()
  patrol_bb.set_bool("enabled", true)
  patrol_bb.set_int("work", 0)
  let combat_bb = Blackboard::new()
  combat_bb.set_bool("enabled", false)
  combat_bb.set_int("work", 0)
  [
    Scenario::new("enabled-workload", benchmark_workload(), patrol_bb, 12),
    Scenario::new("disabled-workload", benchmark_workload(), combat_bb, 12),
    Scenario::new(
      "budgeted-running",
      execution_budget_node(create_always_running(), 4),
      Blackboard::new(),
      8,
    ),
  ]
}

///|
/// Return a one-line health summary for a benchmark result.
pub fn benchmark_health(result : BenchmarkResult) -> String {
  if result.frames() == 0 {
    "empty"
  } else if result.running > 0 {
    "running"
  } else if result.failures > 0 {
    "failure-observed"
  } else {
    "complete"
  }
}

///|
/// Run the standard matrix and return portable CSV output.
pub fn standard_benchmark_csv() -> String {
  benchmark_csv(run_scenarios(standard_scenarios()))
}