///|
/// A non-throwing validation message for host applications.
pub struct ValidationIssue {
  scope : String
  message : String
} derive(Debug, Eq)

///|
/// Result of preflight validation before running an analysis.
pub struct ValidationReport {
  valid : Bool
  issue_count : Int
  issues : Array[ValidationIssue]
} derive(Debug, Eq)

///|
fn push_issue(
  issues : Array[ValidationIssue],
  scope : String,
  message : String,
) -> Unit {
  issues.push({ scope, message })
  ()
}

///|
/// Validate a dimension chain without aborting on the first issue.
pub fn validate_chain(chain : Chain) -> ValidationReport {
  let issues = []
  if chain.name == "" {
    push_issue(issues, "chain", "chain name is empty")
  }
  if chain.dimensions.length() == 0 {
    push_issue(issues, "chain", "chain has no dimensions")
  }
  for dimension in chain.dimensions {
    if dimension.name == "" {
      push_issue(issues, "dimension", "dimension name is empty")
    }
    if dimension.tolerance < 0.0 {
      push_issue(issues, dimension.name, "tolerance is negative")
    }
  }
  { valid: issues.length() == 0, issue_count: issues.length(), issues }
}

///|
/// Validate an acceptance window without modifying it.
pub fn validate_window(window : AcceptanceWindow) -> ValidationReport {
  let issues = []
  if window.upper < window.lower {
    push_issue(issues, "window", "upper bound is below lower bound")
  }
  { valid: issues.length() == 0, issue_count: issues.length(), issues }
}

///|
/// Validate a batch and its acceptance window before inspection.
pub fn validate_measurements(
  values : Array[Double],
  window : AcceptanceWindow,
) -> ValidationReport {
  let issues = []
  if values.length() == 0 {
    push_issue(issues, "measurements", "measurement batch is empty")
  }
  if window.upper < window.lower {
    push_issue(issues, "window", "upper bound is below lower bound")
  }
  { valid: issues.length() == 0, issue_count: issues.length(), issues }
}

///|
/// Combine validation reports from a chain and measurement batch.
pub fn combine_validation(
  first : ValidationReport,
  second : ValidationReport,
) -> ValidationReport {
  let issues = []
  issues.append(first.issues)
  issues.append(second.issues)
  { valid: issues.length() == 0, issue_count: issues.length(), issues }
}