///|
/// Joined validation result used before execution or release.
pub(all) struct ReadinessReport {
  catalog_diagnostics : Array[Diagnostic]
  observation_diagnostics : Array[Diagnostic]
  plan_diagnostics : Array[Diagnostic]
  error_count : Int
  warning_count : Int
  ready : Bool
}

///|
fn append_diagnostics(
  target : Array[Diagnostic],
  source : Array[Diagnostic],
) -> Array[Diagnostic] {
  for diagnostic in source {
    target.push(diagnostic)
  }
  target
}

///|
/// Assess catalog, plan, and observation readiness together.
pub fn assess_readiness(
  features~ : Array[Feature],
  devices~ : Array[Device],
  plan~ : MeasurementPlan,
  observations~ : Array[Observation],
) -> ReadinessReport {
  let catalog_diagnostics = validate_catalog(features, devices)
  let observation_diagnostics = validate_observations(features, observations)
  let plan_diagnostics = validate_plan(plan, features)
  let all = []
  ignore(append_diagnostics(all, catalog_diagnostics))
  ignore(append_diagnostics(all, observation_diagnostics))
  ignore(append_diagnostics(all, plan_diagnostics))
  let summary = validation_summary(all)
  {
    catalog_diagnostics,
    observation_diagnostics,
    plan_diagnostics,
    error_count: summary.error_count,
    warning_count: summary.warning_count,
    ready: summary.is_valid && plan.summary.coverage >= 1.0,
  }
}

///|
/// Return the number of readiness errors.
pub fn readiness_error_count(report : ReadinessReport) -> Int {
  report.error_count
}

///|
/// Return the number of readiness warnings.
pub fn readiness_warning_count(report : ReadinessReport) -> Int {
  report.warning_count
}

///|
/// Return a stable readiness label.
pub fn readiness_status(report : ReadinessReport) -> String {
  if report.ready {
    "ready"
  } else if report.error_count > 0 {
    "blocked"
  } else {
    "review"
  }
}

///|
/// Return all readiness diagnostics in boundary order.
pub fn readiness_diagnostics(report : ReadinessReport) -> Array[Diagnostic] {
  let result = []
  ignore(append_diagnostics(result, report.catalog_diagnostics))
  ignore(append_diagnostics(result, report.observation_diagnostics))
  ignore(append_diagnostics(result, report.plan_diagnostics))
  result
}

///|
/// Render readiness as stable key-value text.
pub fn render_readiness(report : ReadinessReport) -> String {
  "status=" +
  readiness_status(report) +
  " errors=" +
  report.error_count.to_string() +
  " warnings=" +
  report.warning_count.to_string()
}

///|
/// Return whether there are no readiness warnings or errors.
pub fn readiness_is_green(report : ReadinessReport) -> Bool {
  report.ready && report.warning_count == 0
}

///|
/// Return whether readiness requires human review but is not blocked.
pub fn readiness_needs_review(report : ReadinessReport) -> Bool {
  !report.ready && report.error_count == 0
}

///|
/// Return whether a readiness report has catalog errors.
pub fn readiness_catalog_is_valid(report : ReadinessReport) -> Bool {
  !has_errors(report.catalog_diagnostics)
}

///|
/// Return whether a readiness report has plan errors.
pub fn readiness_plan_is_valid(report : ReadinessReport) -> Bool {
  !has_errors(report.plan_diagnostics)
}