///|
/// Deterministic operation counters used by the benchmark CLI and CI.
pub struct BenchmarkResult {
  name : String
  iterations : Int
  input_chars : Int
  parsed_traces : Int
  observed_changes : Int
  checksum : Int64
} derive(Debug, Eq)

///|
/// A collection of benchmark results for one fixture.
pub struct BenchmarkSuite {
  fixture : String
  results : Array[BenchmarkResult]
} derive(Debug, Eq)

///|
/// Build a repeatable VCD fixture with scalar and vector signals.
pub fn synthetic_vcd(signal_count : Int, time_count : Int) -> String {
  let signals = Int::max(1, signal_count)
  let times = Int::max(1, time_count)
  let out = StringBuilder()
  out.write_string("$date benchmark $end\n")
  out.write_string("$version moonbit-vcd $end\n")
  out.write_string("$timescale 1ns $end\n")
  out.write_string("$scope module bench $end\n")
  for signal in 0.. BenchmarkResult {
  let safe_iterations = Int::max(1, iterations)
  let mut parsed = 0
  let mut changes = 0
  let mut checksum : Int64 = 0L
  for _ in 0.. {
        parsed += 1
        changes += trace.changes.length()
        checksum += trace.end_time + trace.signals.length().to_int64()
      }
      Err(_) => checksum -= 1L
    }
  }
  {
    name: "parse",
    iterations: safe_iterations,
    input_chars: input.length(),
    parsed_traces: parsed,
    observed_changes: changes,
    checksum,
  }
}

///|
/// Run repeated indexed queries over a parsed fixture.
pub fn benchmark_queries(trace : Trace, iterations : Int) -> BenchmarkResult {
  let safe_iterations = Int::max(1, iterations)
  let index = build_index(trace)
  let mut changes = 0
  let mut checksum : Int64 = 0L
  for _ in 0.. value.width().to_int64()
        None => 0L
      }
    }
  }
  {
    name: "indexed-queries",
    iterations: safe_iterations,
    input_chars: 0,
    parsed_traces: 1,
    observed_changes: changes,
    checksum,
  }
}

///|
/// Run repeated edge and state analysis over a parsed fixture.
pub fn benchmark_analysis(trace : Trace, iterations : Int) -> BenchmarkResult {
  let safe_iterations = Int::max(1, iterations)
  let mut transitions = 0
  let mut checksum : Int64 = 0L
  for _ in 0.. BenchmarkSuite {
  let fixture = synthetic_vcd(signal_count, time_count)
  let parsed = match parse(fixture) {
    Ok(trace) => trace
    Err(_) =>
      {
        header: { date: None, version: None, timescale: None, comment: None },
        signals: [],
        changes: [],
        end_time: 0L,
      }
  }
  {
    fixture: "synthetic-" +
    signal_count.to_string() +
    "x" +
    time_count.to_string(),
    results: [
      benchmark_parse(fixture, iterations),
      benchmark_queries(parsed, iterations),
      benchmark_analysis(parsed, iterations),
    ],
  }
}

///|
/// Render benchmark counters as stable text.
pub fn BenchmarkResult::to_text(self : BenchmarkResult) -> String {
  self.name +
  " iterations=" +
  self.iterations.to_string() +
  " input_chars=" +
  self.input_chars.to_string() +
  " parsed=" +
  self.parsed_traces.to_string() +
  " changes=" +
  self.observed_changes.to_string() +
  " checksum=" +
  self.checksum.to_string()
}

///|
/// Render a suite as CSV for archival benchmark evidence.
pub fn BenchmarkSuite::to_csv(self : BenchmarkSuite) -> String {
  let out : Array[String] = [
    "fixture,name,iterations,input_chars,parsed,changes,checksum",
  ]
  for result in self.results {
    out.push(
      self.fixture +
      "," +
      result.name +
      "," +
      result.iterations.to_string() +
      "," +
      result.input_chars.to_string() +
      "," +
      result.parsed_traces.to_string() +
      "," +
      result.observed_changes.to_string() +
      "," +
      result.checksum.to_string(),
    )
  }
  out.join("\n")
}