///|
/// Deterministic benchmark configuration shared by local and CI runs.
pub(all) struct BenchmarkConfig {
  recording_length : Int
  repetitions : Int
  baseline_rr : Double
  sample_rate_hz : Double
} derive(FromJson, ToJson, Debug, Eq)

///|
/// Aggregate benchmark output that can be copied into a report.
pub(all) struct BenchmarkSummary {
  samples : Int
  repetitions : Int
  feature_count : Int
  mean_rr : Double
  rmssd : Double
  total_power : Double
  quality_ratio : Double
} derive(FromJson, ToJson, Debug, Eq)

///|
/// The default workload is large enough to exercise every major layer.
pub fn BenchmarkConfig::default() -> BenchmarkConfig {
  {
    recording_length: 256,
    repetitions: 10,
    baseline_rr: 800.0,
    sample_rate_hz: 4.0,
  }
}

///|
/// Generate the deterministic workload for a benchmark run.
pub fn benchmark_input(config : BenchmarkConfig) -> Array[Double] {
  let base = generate_resting_fixture(
    config.recording_length,
    config.baseline_rr,
  )
  repeat_fixture(base, config.repetitions)
}

///|
/// Run the full analysis workload once.
pub fn run_benchmark_once(config : BenchmarkConfig) -> BenchmarkSummary {
  let input = benchmark_input(config)
  let defaults = AnalysisOptions::default()
  let options : AnalysisOptions = {
    cleaning_method: defaults.cleaning_method,
    sample_rate_hz: config.sample_rate_hz,
    remove_trend: defaults.remove_trend,
    window_function: defaults.window_function,
    segment_size: defaults.segment_size,
    segment_hop: defaults.segment_hop,
    nonlinear_enabled: defaults.nonlinear_enabled,
  }
  let report = analyze_rr(input, HrvConfig::default(), options)
  {
    samples: input.length(),
    repetitions: config.repetitions,
    feature_count: report.feature_vector.length(),
    mean_rr: report.metrics.mean_rr,
    rmssd: report.metrics.rmssd,
    total_power: report.frequency.total_power,
    quality_ratio: report.quality.clean_ratio,
  }
}

///|
/// Run repeated analyses and average stable numeric fields.
pub fn run_benchmark(config : BenchmarkConfig) -> BenchmarkSummary {
  let repetitions = if config.repetitions < 1 { 1 } else { config.repetitions }
  let one_config : BenchmarkConfig = {
    recording_length: config.recording_length,
    repetitions: 1,
    baseline_rr: config.baseline_rr,
    sample_rate_hz: config.sample_rate_hz,
  }
  let first = run_benchmark_once(one_config)
  let mut mean_rr = 0.0
  let mut rmssd = 0.0
  let mut power = 0.0
  for _ in 0.. String {
  to_csv([
    [
      "samples", "repetitions", "feature_count", "mean_rr", "rmssd", "total_power",
      "quality_ratio",
    ],
    [
      summary.samples.to_string(),
      summary.repetitions.to_string(),
      summary.feature_count.to_string(),
      summary.mean_rr.to_string(),
      summary.rmssd.to_string(),
      summary.total_power.to_string(),
      summary.quality_ratio.to_string(),
    ],
  ])
}

///|
/// Return whether a benchmark summary has usable output.
pub fn benchmark_is_valid(summary : BenchmarkSummary) -> Bool {
  summary.samples > 0 &&
  summary.feature_count > 0 &&
  summary.quality_ratio >= 0.0 &&
  summary.quality_ratio <= 1.0
}