///|
/// Application quality gates for local acceptance and regression checks.
///
/// Gates turn model-level invariants into a compact report: feasibility,
/// coverage, deterministic signatures, and metric thresholds can be checked
/// together before a CLI or CI job declares a scenario ready.
pub enum QualityStatus {
  GatePassed
  GateWarning
  GateFailed
}

///|
/// One quality-gate result.
pub struct QualityGateResult {
  name : String
  status : QualityStatus
  observed : Int
  threshold : Int
  detail : String
}

///|
/// Construct a result.
pub fn quality_gate_result(
  name : String,
  status : QualityStatus,
  observed : Int,
  threshold : Int,
  detail : String,
) -> QualityGateResult {
  { name, status, observed, threshold, detail }
}

///|
/// Return a status label.
pub fn QualityGateResult::status_name(self : QualityGateResult) -> String {
  match self.status {
    GatePassed => "passed"
    GateWarning => "warning"
    GateFailed => "failed"
  }
}

///|
/// Return whether this gate passes.
pub fn QualityGateResult::passed(self : QualityGateResult) -> Bool {
  self.status is GatePassed
}

///|
/// Return a stable line.
pub fn QualityGateResult::describe(self : QualityGateResult) -> String {
  "\{self.status_name()}:\{self.name}: observed=\{self.observed}, threshold=\{self.threshold}, \{self.detail}"
}

///|
/// A collection of quality-gate results.
pub struct QualityReview {
  results : Array[QualityGateResult]
}

///|
/// Create an empty review.
pub fn quality_review() -> QualityReview {
  { results: [] }
}

///|
/// Add a gate.
pub fn QualityReview::add(
  self : QualityReview,
  result : QualityGateResult,
) -> Unit {
  self.results.push(result)
}

///|
/// Add a boolean gate.
pub fn QualityReview::require(
  self : QualityReview,
  name : String,
  condition : Bool,
  detail : String,
) -> Unit {
  self.add(
    quality_gate_result(
      name,
      if condition {
        GatePassed
      } else {
        GateFailed
      },
      if condition {
        1
      } else {
        0
      },
      1,
      detail,
    ),
  )
}

///|
/// Add a non-blocking informational warning.
pub fn QualityReview::note(
  self : QualityReview,
  name : String,
  detail : String,
) -> Unit {
  self.add(quality_gate_result(name, GateWarning, 0, 0, detail))
}

///|
/// Add a minimum threshold gate.
pub fn QualityReview::minimum(
  self : QualityReview,
  name : String,
  observed : Int,
  threshold : Int,
  detail : String,
) -> Unit {
  self.add(
    quality_gate_result(
      name,
      if observed >= threshold {
        GatePassed
      } else {
        GateFailed
      },
      observed,
      threshold,
      detail,
    ),
  )
}

///|
/// Add a maximum threshold gate.
pub fn QualityReview::maximum(
  self : QualityReview,
  name : String,
  observed : Int,
  threshold : Int,
  detail : String,
) -> Unit {
  self.add(
    quality_gate_result(
      name,
      if observed <= threshold {
        GatePassed
      } else {
        GateFailed
      },
      observed,
      threshold,
      detail,
    ),
  )
}

///|
/// Return gate count.
pub fn QualityReview::length(self : QualityReview) -> Int {
  self.results.length()
}

///|
/// Return failure count.
pub fn QualityReview::failure_count(self : QualityReview) -> Int {
  let mut result = 0
  for gate in self.results {
    if gate.status is GateFailed {
      result += 1
    }
  }
  result
}

///|
/// Return warning count.
pub fn QualityReview::warning_count(self : QualityReview) -> Int {
  let mut result = 0
  for gate in self.results {
    if gate.status is GateWarning {
      result += 1
    }
  }
  result
}

///|
/// Return whether the review passes.
pub fn QualityReview::passed(self : QualityReview) -> Bool {
  self.failure_count() == 0
}

///|
/// Return copied results.
pub fn QualityReview::results(self : QualityReview) -> Array[QualityGateResult] {
  self.results.copy()
}

///|
/// Render the review.
pub fn QualityReview::describe(self : QualityReview) -> String {
  let builder = StringBuilder()
  for index, result in self.results {
    if index > 0 {
      builder.write_char('\n')
    }
    builder.write_string(result.describe())
  }
  builder.to_string()
}

///|
/// Check a solver model and optional solution.
pub fn review_solver(solver : Solver, solution : Solution?) -> QualityReview {
  let review = quality_review()
  let validation = solver_validation_report(solver, solution)
  review.require("model-valid", validation.valid(), "model validation report")
  review.minimum("variables", solver.variable_count(), 1, "model has variables")
  review.minimum(
    "constraints",
    solver.constraint_count(),
    1,
    "model has constraints",
  )
  review.maximum(
    "validation-errors",
    validation.error_count(),
    0,
    "validation errors",
  )
  review
}

///|
/// Check a route plan.
pub fn review_routing(
  instance : RoutingInstance,
  plan : RoutingPlan,
) -> QualityReview {
  let review = quality_review()
  let report = routing_report(instance, plan)
  review.require("routing-feasible", report.feasible(), report.describe())
  review.minimum(
    "routing-visits",
    report.visits(),
    instance.customers().length(),
    "customer coverage",
  )
  review.maximum(
    "routing-errors",
    report.errors.length(),
    0,
    "route validation",
  )
  review
}

///|
/// Check a project schedule.
pub fn review_project(
  project : ProjectPlan,
  starts : Array[Int],
) -> QualityReview {
  let review = quality_review()
  let errors = project.validate_schedule(starts)
  review.require(
    "schedule-valid",
    errors.length() == 0,
    "project schedule validation",
  )
  review.minimum(
    "scheduled-tasks",
    starts.length(),
    project.task_count(),
    "task coverage",
  )
  review.maximum(
    "schedule-errors",
    errors.length(),
    0,
    "precedence and capacity",
  )
  review
}

///|
/// Check a resource timeline.
pub fn review_timeline(timeline : ResourceTimeline) -> QualityReview {
  let review = quality_review()
  review.require(
    "timeline-feasible",
    timeline.feasible(),
    "capacity and interval checks",
  )
  review.maximum(
    "timeline-conflicts",
    timeline.conflicts().length(),
    0,
    "interval conflicts",
  )
  review.minimum("timeline-intervals", timeline.length(), 0, "interval input")
  review
}

///|
/// Return a combined score where failures dominate.
pub fn QualityReview::score(self : QualityReview) -> Int {
  self.failure_count() * 1000000 + self.warning_count() * 1000 + self.length()
}

///|
/// Return a stable review signature.
pub fn QualityReview::signature(self : QualityReview) -> Int {
  let mut result = 31
  for gate in self.results {
    for character in gate.name {
      result = result * 37 + character.to_int()
    }
    result = result * 41 + gate.observed + gate.threshold
  }
  result
}

///|
/// Return a concise review status.
pub fn QualityReview::status_line(self : QualityReview) -> String {
  if self.passed() {
    "passed"
  } else {
    "failed"
  }
}