///|
/// Release decision with counts that can be exported to CI or a dashboard.
pub(all) struct ReleaseDecision {
  ready : Bool
  risk_score : Int
  errors : Int
  warnings : Int
  checked_files : Int
  checked_bytes : Int64
}

///|
/// Convert a manifest audit report into an explicit release decision.
pub fn AuditReport::decision(self : AuditReport) -> ReleaseDecision {
  let errors = self.count(AuditSeverity::Error)
  let warnings = self.count(AuditSeverity::Warning)
  let risk_score = errors * 100 + warnings * 10
  {
    ready: errors == 0,
    risk_score,
    errors,
    warnings,
    checked_files: self.files_checked,
    checked_bytes: self.total_bytes,
  }
}

///|
/// Convert a policy check directly into a release decision.
pub fn Manifest::release_decision(
  self : Manifest,
  policy : ReleasePolicy,
) -> ReleaseDecision {
  self.release_check(policy).decision()
}

///|
/// Return true only when no warnings or errors remain.
pub fn ReleaseDecision::is_clean(self : ReleaseDecision) -> Bool {
  self.errors == 0 && self.warnings == 0
}

///|
/// Return an exit code suitable for a CI shell wrapper.
pub fn ReleaseDecision::exit_code(self : ReleaseDecision) -> Int {
  if self.errors > 0 {
    2
  } else if self.warnings > 0 {
    1
  } else {
    0
  }
}

///|
/// Return a compact stable line for a build summary.
pub fn ReleaseDecision::to_text(self : ReleaseDecision) -> String {
  "ready=" +
  self.ready.to_string() +
  " risk_score=" +
  self.risk_score.to_string() +
  " errors=" +
  self.errors.to_string() +
  " warnings=" +
  self.warnings.to_string() +
  " files=" +
  self.checked_files.to_string() +
  " bytes=" +
  self.checked_bytes.to_string()
}

///|
/// Return all error codes in insertion order.
pub fn AuditReport::error_codes(self : AuditReport) -> Array[String] {
  let result : Array[String] = []
  for finding in self.findings {
    if finding.severity == AuditSeverity::Error {
      result.push(finding.code)
    }
  }
  result
}

///|
/// Return true only when the report has no findings at all.
pub fn AuditReport::is_clean(self : AuditReport) -> Bool {
  self.findings.length() == 0
}

///|
/// Format dependency audit counts for release evidence.
pub fn DependencyAudit::summary(self : DependencyAudit) -> String {
  "dependencies=" +
  self.dependencies_checked.to_string() +
  " errors=" +
  self.count(AuditSeverity::Error).to_string() +
  " warnings=" +
  self.count(AuditSeverity::Warning).to_string()
}

///|
/// Return the names of dependencies whose metadata contains blocking errors.
pub fn DependencyAudit::error_dependencies(
  self : DependencyAudit,
) -> Array[String] {
  let result : Array[String] = []
  for finding in self.findings {
    if finding.severity == AuditSeverity::Error {
      result.push(finding.path)
    }
  }
  result
}