///| Small deterministic reports used by the executable demo and by callers

///|
/// that need a text artifact without pulling in JSON or terminal libraries.
pub fn render_metrics(metrics : DecodeMetrics) -> String {
  "target_batches=" +
  metrics.target_batches.to_string() +
  "\ndraft_tokens=" +
  metrics.draft_tokens.to_string() +
  "\nemitted_tokens=" +
  metrics.emitted_tokens.to_string() +
  "\naccepted_tokens=" +
  metrics.accepted_tokens.to_string() +
  "\nrejected_rounds=" +
  metrics.rejected_rounds.to_string() +
  "\nacceptance_rate=" +
  metrics.acceptance_rate().to_string() +
  "\ntokens_per_target_batch=" +
  metrics.tokens_per_target_batch().to_string() +
  "\n"
}

///|
pub fn render_simulation(result : SimulationResult) -> String {
  let mut tokens = ""
  let mut first = true
  for token in result.generated {
    if !first {
      tokens = tokens + ","
    }
    tokens = tokens + token.to_string()
    first = false
  }
  "TreeSpec offline simulation\nrounds=" +
  result.rounds.to_string() +
  "\ngenerated_tokens=[" +
  tokens +
  "]\n" +
  render_metrics(result.metrics)
}

///|
pub fn demo_schedule() -> Array[SimulationRound] {
  [
    {
      draft_logits: [[0.0, 2.0, -1.0], [2.0, 0.0, -1.0]],
      draft_uniforms: [0.5, 0.5],
      target_logits: [[0.0, 3.0, -1.0], [3.0, 0.0, -1.0]],
      accept_uniforms: [0.1, 0.1],
      fallback_uniforms: [0.1, 0.1],
    },
    {
      draft_logits: [[0.0, 2.0, -1.0]],
      draft_uniforms: [0.5],
      target_logits: [[2.0, 0.0, -1.0]],
      accept_uniforms: [0.9],
      fallback_uniforms: [0.1],
    },
  ]
}

///|
pub fn run_demo() -> Result[String, SimulationError] {
  match simulate([42], demo_schedule()) {
    Ok(result) => Ok(render_simulation(result))
    Err(error) => Err(error)
  }
}