///|
pub fn normalize_plan(plan : EvaluationPlan) -> EvaluationPlan {
  {
    name: plan.name,
    cutoffs: normalize_cutoffs(plan.cutoffs),
    threshold: Int::max(plan.threshold, 0),
    gain: plan.gain,
  }
}

///|
pub fn execute_plan(
  plan : EvaluationPlan,
  qrels : Array[JudgedDoc],
  run : Array[RetrievedDoc],
) -> PlanResult {
  let normalized = normalize_plan(plan)
  let validation = validate_dataset(qrels, run)
  let report = evaluate_benchmark(
    qrels,
    run,
    config=EvalConfig::new(
      cutoffs=normalized.cutoffs,
      relevant_threshold=normalized.threshold,
      gain_scheme=normalized.gain,
    ),
  )
  { name: normalized.name, report, validation }
}

///|
pub fn render_plan_summary(result : PlanResult) -> String {
  let builder = StringBuilder()
  let cutoff_text = result.report.cutoffs
    .map(fn(item) { item.to_string() })
    .join(",")
  builder.write_string("# Evaluation plan: \{result.name}\n\n")
  builder.write_string(
    "queries=\{result.report.query_count} cutoffs=\{cutoff_text}\n",
  )
  builder.write_string(
    "valid=\{result.validation.is_valid()} errors=\{result.validation.error_count} warnings=\{result.validation.warning_count}\n\n",
  )
  builder.write_string(render_compact_report(result.report))
  builder.to_string()
}

///|
pub fn plan_metric(result : PlanResult, name : String) -> MetricDistribution {
  summarize_report_metric(result.report, name)
}

///|
pub fn plan_has_metric(result : PlanResult, name : String) -> Bool {
  for metric in result.report.summary {
    if metric.name == name {
      return true
    }
  }
  false
}

///|
pub fn plan_validation_markdown(result : PlanResult) -> String {
  render_validation_report(result.validation)
}

///|
pub fn plan_to_json(result : PlanResult) -> String {
  ToJson::to_json(result).stringify(indent=2)
}

///|
pub fn execute_plan_with_profile(
  plan : EvaluationPlan,
  qrels : Array[JudgedDoc],
  run : Array[RetrievedDoc],
) -> String {
  let result = execute_plan(plan, qrels, run)
  let builder = StringBuilder()
  builder.write_string(render_plan_summary(result))
  builder.write_string("\n\n## Profile\n\n")
  builder.write_string(render_profile_text(profile_dataset(qrels, run)))
  builder.to_string()
}