///|
/// Static analysis of one named template in a catalog.
pub struct CatalogEntryAudit {
  name : String
  variable_count : Int
  expression_count : Int
  max_level : Int
  findings : Array[PolicyFinding]
}

///|
pub fn CatalogEntryAudit::name(self : CatalogEntryAudit) -> String {
  self.name
}

///|
pub fn CatalogEntryAudit::variable_count(self : CatalogEntryAudit) -> Int {
  self.variable_count
}

///|
pub fn CatalogEntryAudit::expression_count(self : CatalogEntryAudit) -> Int {
  self.expression_count
}

///|
pub fn CatalogEntryAudit::max_level(self : CatalogEntryAudit) -> Int {
  self.max_level
}

///|
pub fn CatalogEntryAudit::findings(
  self : CatalogEntryAudit,
) -> Array[PolicyFinding] {
  self.findings.copy()
}

///|
/// Policy findings for every catalog entry, preserving catalog order.
pub struct CatalogAudit {
  entries : Array[CatalogEntryAudit]
  total_findings : Int
}

///|
pub fn CatalogAudit::entries(self : CatalogAudit) -> Array[CatalogEntryAudit] {
  self.entries.copy()
}

///|
pub fn CatalogAudit::total_findings(self : CatalogAudit) -> Int {
  self.total_findings
}

///|
pub fn CatalogAudit::is_clean(self : CatalogAudit) -> Bool {
  self.total_findings == 0
}

///|
/// Audit every named template for SDK or server startup checks. The audit
/// never reads values or produces URI text. An invalid policy is rejected even
/// when the catalog contains no templates.
pub fn TemplateCatalog::audit(
  self : TemplateCatalog,
  policy : TemplatePolicy,
) -> Result[CatalogAudit, PolicyError] {
  match policy.validate() {
    Ok(_) => ()
    Err(error) => return Err(error)
  }
  let entries : Array[CatalogEntryAudit] = []
  let mut total_findings = 0
  for name in self.names {
    match self.templates.get(name) {
      None => ()
      Some(template) => {
        let analysis = template.analyze()
        let findings = match template.check_policy(policy) {
          Ok(value) => value
          Err(error) => return Err(error)
        }
        total_findings += findings.length()
        entries.push({
          name,
          variable_count: template.variables().length(),
          expression_count: analysis.expressions().length(),
          max_level: analysis.max_level(),
          findings,
        })
      }
    }
  }
  Ok({ entries, total_findings, })
}