///|
pub(all) enum AuditPolicyAction {
  PolicyAccept
  PolicyReview
  PolicyReject
} derive(Eq, Debug, ToJson)

///|
pub(all) enum AuditPolicyCheck {
  CheckScore
  CheckErrors
  CheckWarnings
  CheckContract
  CheckReproducibility
  CheckRuntime
  CheckVisualQuality
  CheckExportQuality
  CheckPerformance
  CheckTarget
} derive(Eq, Debug, ToJson)

///|
pub(all) struct AuditPolicy {
  name : String
  minimum_score : Double
  maximum_warnings : Int
  maximum_errors : Int
  minimum_metric_rate : Double
  runtime_budget : Int
  require_contract : Bool
  require_reproducible : Bool
  require_gallery_target : Bool
  strict : Bool
} derive(Eq, Debug, ToJson)

///|
pub(all) struct AuditPolicyResult {
  policy : String
  mut action : AuditPolicyAction
  mut score : Double
  checks : Array[AuditPolicyCheck]
  passed_checks : Array[AuditPolicyCheck]
  failed_checks : Array[AuditPolicyCheck]
  reasons : Array[String]
  mut warnings : Int
  mut errors : Int
  mut runtime_delta : Int
} derive(Eq, Debug, ToJson)

///|
pub(all) struct AuditPolicyBatchResult {
  policy : String
  mut total : Int
  mut accepted : Int
  mut review : Int
  mut rejected : Int
  mut score : Double
  results : Array[AuditPolicyResult]
} derive(Eq, Debug, ToJson)

///|
fn ap_action_name(action : AuditPolicyAction) -> String {
  match action {
    PolicyAccept => "accept"
    PolicyReview => "review"
    PolicyReject => "reject"
  }
}

///|
fn ap_check_name(check : AuditPolicyCheck) -> String {
  match check {
    CheckScore => "score"
    CheckErrors => "errors"
    CheckWarnings => "warnings"
    CheckContract => "contract"
    CheckReproducibility => "reproducibility"
    CheckRuntime => "runtime"
    CheckVisualQuality => "visual-quality"
    CheckExportQuality => "export-quality"
    CheckPerformance => "performance"
    CheckTarget => "target"
  }
}

///|
fn ap_clamp(value : Double) -> Double {
  value.max(0.0).min(1.0)
}

///|
pub fn AuditPolicy::default() -> AuditPolicy {
  {
    name: "default",
    minimum_score: 0.5,
    maximum_warnings: 8,
    maximum_errors: 0,
    minimum_metric_rate: 0.7,
    runtime_budget: 100000,
    require_contract: true,
    require_reproducible: true,
    require_gallery_target: false,
    strict: false,
  }
}

///|
pub fn AuditPolicy::ci() -> AuditPolicy {
  {
    name: "ci",
    minimum_score: 0.75,
    maximum_warnings: 0,
    maximum_errors: 0,
    minimum_metric_rate: 0.9,
    runtime_budget: 50000,
    require_contract: true,
    require_reproducible: true,
    require_gallery_target: false,
    strict: true,
  }
}

///|
pub fn AuditPolicy::gallery() -> AuditPolicy {
  {
    name: "gallery",
    minimum_score: 0.8,
    maximum_warnings: 2,
    maximum_errors: 0,
    minimum_metric_rate: 0.8,
    runtime_budget: 250000,
    require_contract: true,
    require_reproducible: true,
    require_gallery_target: true,
    strict: false,
  }
}

///|
pub fn AuditPolicy::preview() -> AuditPolicy {
  {
    name: "preview",
    minimum_score: 0.25,
    maximum_warnings: 16,
    maximum_errors: 0,
    minimum_metric_rate: 0.4,
    runtime_budget: 500000,
    require_contract: false,
    require_reproducible: false,
    require_gallery_target: false,
    strict: false,
  }
}

///|
pub fn AuditPolicy::batch() -> AuditPolicy {
  {
    name: "batch",
    minimum_score: 0.6,
    maximum_warnings: 4,
    maximum_errors: 0,
    minimum_metric_rate: 0.75,
    runtime_budget: 1000000,
    require_contract: true,
    require_reproducible: true,
    require_gallery_target: false,
    strict: false,
  }
}

///|
pub fn AuditPolicy::named(self : AuditPolicy, name : String) -> AuditPolicy {
  { ..self, name, }
}

///|
pub fn AuditPolicy::with_score(
  self : AuditPolicy,
  score : Double,
) -> AuditPolicy {
  { ..self, minimum_score: score.max(0.0).min(1.0) }
}

///|
pub fn AuditPolicy::with_warnings(
  self : AuditPolicy,
  warnings : Int,
) -> AuditPolicy {
  { ..self, maximum_warnings: warnings.max(0) }
}

///|
pub fn AuditPolicy::with_errors(
  self : AuditPolicy,
  errors : Int,
) -> AuditPolicy {
  { ..self, maximum_errors: errors.max(0) }
}

///|
pub fn AuditPolicy::with_metric_rate(
  self : AuditPolicy,
  rate : Double,
) -> AuditPolicy {
  { ..self, minimum_metric_rate: rate.max(0.0).min(1.0) }
}

///|
pub fn AuditPolicy::with_runtime(
  self : AuditPolicy,
  budget : Int,
) -> AuditPolicy {
  { ..self, runtime_budget: budget.max(0) }
}

///|
pub fn AuditPolicy::with_contract(
  self : AuditPolicy,
  required : Bool,
) -> AuditPolicy {
  { ..self, require_contract: required }
}

///|
pub fn AuditPolicy::with_reproducibility(
  self : AuditPolicy,
  required : Bool,
) -> AuditPolicy {
  { ..self, require_reproducible: required }
}

///|
pub fn AuditPolicy::with_gallery(
  self : AuditPolicy,
  required : Bool,
) -> AuditPolicy {
  { ..self, require_gallery_target: required }
}

///|
pub fn AuditPolicy::with_strict(
  self : AuditPolicy,
  strict : Bool,
) -> AuditPolicy {
  { ..self, strict, }
}

///|
pub fn AuditPolicy::is_strict(self : AuditPolicy) -> Bool {
  self.strict
}

///|
pub fn AuditPolicy::score_threshold(self : AuditPolicy) -> Double {
  self.minimum_score
}

///|
pub fn AuditPolicy::warning_limit(self : AuditPolicy) -> Int {
  self.maximum_warnings
}

///|
pub fn AuditPolicy::error_limit(self : AuditPolicy) -> Int {
  self.maximum_errors
}

///|
pub fn AuditPolicy::runtime_limit(self : AuditPolicy) -> Int {
  self.runtime_budget
}

///|
pub fn AuditPolicy::metric_rate_threshold(self : AuditPolicy) -> Double {
  self.minimum_metric_rate
}

///|
pub fn AuditPolicy::requires_contract(self : AuditPolicy) -> Bool {
  self.require_contract
}

///|
pub fn AuditPolicy::requires_reproducibility(self : AuditPolicy) -> Bool {
  self.require_reproducible
}

///|
pub fn AuditPolicy::requires_gallery(self : AuditPolicy) -> Bool {
  self.require_gallery_target
}

///|
pub fn AuditPolicy::to_json_string(self : AuditPolicy, indent? : Int) -> String {
  ToJson::to_json(self).stringify(indent=indent.unwrap_or(0))
}

///|
pub fn AuditPolicyResult::new(policy : AuditPolicy) -> AuditPolicyResult {
  {
    policy: policy.name,
    action: PolicyAccept,
    score: 1.0,
    checks: [],
    passed_checks: [],
    failed_checks: [],
    reasons: [],
    warnings: 0,
    errors: 0,
    runtime_delta: 0,
  }
}

///|
pub fn AuditPolicyResult::record(
  self : AuditPolicyResult,
  check : AuditPolicyCheck,
  passed : Bool,
  reason : String,
) -> Unit {
  self.checks.push(check)
  if passed {
    self.passed_checks.push(check)
  } else {
    self.failed_checks.push(check)
    if reason.length() > 0 {
      self.reasons.push(reason)
    }
    self.score = (self.score - 0.1).max(0.0)
  }
}

///|
pub fn AuditPolicyResult::set_action(
  self : AuditPolicyResult,
  action : AuditPolicyAction,
) -> Unit {
  self.action = action
}

///|
pub fn AuditPolicyResult::set_score(
  self : AuditPolicyResult,
  score : Double,
) -> Unit {
  self.score = ap_clamp(score)
}

///|
pub fn AuditPolicyResult::set_runtime_delta(
  self : AuditPolicyResult,
  delta : Int,
) -> Unit {
  self.runtime_delta = delta
}

///|
pub fn AuditPolicyResult::add_reason(
  self : AuditPolicyResult,
  reason : String,
) -> Unit {
  if reason.length() > 0 && !self.reasons.contains(reason) {
    self.reasons.push(reason)
  }
}

///|
pub fn AuditPolicyResult::fail(
  self : AuditPolicyResult,
  reason : String,
) -> Unit {
  self.set_action(PolicyReject)
  self.add_reason(reason)
}

///|
pub fn AuditPolicyResult::review(
  self : AuditPolicyResult,
  reason : String,
) -> Unit {
  if self.action == PolicyAccept {
    self.set_action(PolicyReview)
  }
  self.add_reason(reason)
}

///|
pub fn AuditPolicyResult::warning_count(self : AuditPolicyResult) -> Int {
  self.warnings
}

///|
pub fn AuditPolicyResult::error_count(self : AuditPolicyResult) -> Int {
  self.errors
}

///|
pub fn AuditPolicyResult::is_accepted(self : AuditPolicyResult) -> Bool {
  self.action == PolicyAccept
}

///|
pub fn AuditPolicyResult::is_review(self : AuditPolicyResult) -> Bool {
  self.action == PolicyReview
}

///|
pub fn AuditPolicyResult::is_rejected(self : AuditPolicyResult) -> Bool {
  self.action == PolicyReject
}

///|
pub fn AuditPolicyResult::status(self : AuditPolicyResult) -> String {
  ap_action_name(self.action)
}

///|
pub fn AuditPolicyResult::check_count(self : AuditPolicyResult) -> Int {
  self.checks.length()
}

///|
pub fn AuditPolicyResult::passed_count(self : AuditPolicyResult) -> Int {
  self.passed_checks.length()
}

///|
pub fn AuditPolicyResult::failed_count(self : AuditPolicyResult) -> Int {
  self.failed_checks.length()
}

///|
pub fn AuditPolicyResult::pass_rate(self : AuditPolicyResult) -> Double {
  if self.checks.length() == 0 {
    1.0
  } else {
    self.passed_checks.length().to_double() / self.checks.length().to_double()
  }
}

///|
pub fn AuditPolicyResult::has_check(
  self : AuditPolicyResult,
  check : AuditPolicyCheck,
) -> Bool {
  self.checks.contains(check)
}

///|
pub fn AuditPolicyResult::passed_check(
  self : AuditPolicyResult,
  check : AuditPolicyCheck,
) -> Bool {
  self.passed_checks.contains(check)
}

///|
pub fn AuditPolicyResult::failed_check(
  self : AuditPolicyResult,
  check : AuditPolicyCheck,
) -> Bool {
  self.failed_checks.contains(check)
}

///|
pub fn AuditPolicyResult::reason_text(self : AuditPolicyResult) -> String {
  self.reasons.join("; ")
}

///|
pub fn AuditPolicyResult::check_text(self : AuditPolicyResult) -> String {
  self.checks.map(fn(check) { ap_check_name(check) }).join(",")
}

///|
pub fn AuditPolicyResult::to_text(self : AuditPolicyResult) -> String {
  let builder = StringBuilder()
  builder.write_string("policy=" + self.policy + "\n")
  builder.write_string("status=" + self.status() + "\n")
  builder.write_string("score=" + self.score.to_string() + "\n")
  builder.write_string("checks=" + self.check_text() + "\n")
  builder.write_string("passed=" + self.passed_count().to_string() + "\n")
  builder.write_string("failed=" + self.failed_count().to_string() + "\n")
  builder.write_string("warnings=" + self.warnings.to_string() + "\n")
  builder.write_string("errors=" + self.errors.to_string() + "\n")
  builder.write_string("runtime_delta=" + self.runtime_delta.to_string() + "\n")
  builder.write_string("reasons=" + self.reason_text() + "\n")
  builder.to_string()
}

///|
pub fn AuditPolicyResult::to_json_string(
  self : AuditPolicyResult,
  indent? : Int,
) -> String {
  ToJson::to_json(self).stringify(indent=indent.unwrap_or(0))
}

///|
fn ap_check_score(
  report : RenderAuditReport,
  policy : AuditPolicy,
  result : AuditPolicyResult,
) -> Unit {
  let passed = report.score >= policy.minimum_score
  result.record(
    CheckScore,
    passed,
    "score " +
    report.score.to_string() +
    " is below " +
    policy.minimum_score.to_string(),
  )
  if !passed {
    result.fail("quality score is below policy threshold")
  }
}

///|
fn ap_check_errors(
  report : RenderAuditReport,
  policy : AuditPolicy,
  result : AuditPolicyResult,
) -> Unit {
  let errors = report.error_count()
  let passed = errors <= policy.maximum_errors
  result.errors = errors
  result.record(
    CheckErrors,
    passed,
    "error count " +
    errors.to_string() +
    " exceeds " +
    policy.maximum_errors.to_string(),
  )
  if !passed {
    result.fail("blocking audit errors are present")
  }
}

///|
fn ap_check_warnings(
  report : RenderAuditReport,
  policy : AuditPolicy,
  result : AuditPolicyResult,
) -> Unit {
  let warnings = report.warning_count()
  let passed = warnings <= policy.maximum_warnings
  result.warnings = warnings
  result.record(
    CheckWarnings,
    passed,
    "warning count " +
    warnings.to_string() +
    " exceeds " +
    policy.maximum_warnings.to_string(),
  )
  if !passed {
    result.review("warning budget is exceeded")
  }
}

///|
fn ap_check_contract(
  report : RenderAuditReport,
  policy : AuditPolicy,
  result : AuditPolicyResult,
) -> Unit {
  let passed = !policy.require_contract || audit_report_full_contract(report)
  result.record(CheckContract, passed, "audit report contract is invalid")
  if !passed {
    result.fail("audit report contract is invalid")
  }
}

///|
fn ap_check_reproducibility(
  report : RenderAuditReport,
  policy : AuditPolicy,
  result : AuditPolicyResult,
) -> Unit {
  let passed = !policy.require_reproducible ||
    audit_report_is_reproducible(report)
  result.record(
    CheckReproducibility,
    passed,
    "report has no stable reproducibility key",
  )
  if !passed {
    result.fail("reproducibility evidence is missing")
  }
}

///|
fn ap_check_runtime(
  report : RenderAuditReport,
  policy : AuditPolicy,
  result : AuditPolicyResult,
) -> Unit {
  let delta = audit_report_budget_delta(report, policy.runtime_budget)
  let passed = delta >= 0
  result.runtime_delta = delta
  result.record(CheckRuntime, passed, "runtime estimate exceeds policy budget")
  if !passed {
    result.review("runtime budget is exceeded")
  }
}

///|
fn ap_check_visual(
  report : RenderAuditReport,
  policy : AuditPolicy,
  result : AuditPolicyResult,
) -> Unit {
  let rate = audit_metric_pass_rate(report)
  let passed = rate >= policy.minimum_metric_rate &&
    audit_domain_score(report, AuditGeometry) >= policy.minimum_metric_rate
  result.record(
    CheckVisualQuality,
    passed,
    "visual metrics are below the policy threshold",
  )
  if !passed {
    result.review("visual quality needs manual review")
  }
}

///|
fn ap_check_export(
  report : RenderAuditReport,
  policy : AuditPolicy,
  result : AuditPolicyResult,
) -> Unit {
  let passed = audit_domain_score(report, AuditExport) >=
    policy.minimum_metric_rate &&
    !audit_has_domain_error(report, AuditExport)
  result.record(
    CheckExportQuality,
    passed,
    "export metrics contain a blocking issue",
  )
  if !passed {
    result.fail("export quality is below policy threshold")
  }
}

///|
fn ap_check_performance(
  report : RenderAuditReport,
  policy : AuditPolicy,
  result : AuditPolicyResult,
) -> Unit {
  let passed = audit_domain_score(report, AuditPerformance) >=
    policy.minimum_metric_rate
  result.record(
    CheckPerformance,
    passed,
    "performance metrics are below the policy threshold",
  )
  if !passed {
    result.review("performance characteristics need review")
  }
}

///|
fn ap_check_target(
  report : RenderAuditReport,
  policy : AuditPolicy,
  result : AuditPolicyResult,
) -> Unit {
  let passed = !policy.require_gallery_target ||
    audit_report_ready_for_gallery(report)
  result.record(
    CheckTarget,
    passed,
    "report is not ready for the requested target",
  )
  if !passed {
    result.review("gallery target requirements are not met")
  }
}

///|
fn ap_finalize(policy : AuditPolicy, result : AuditPolicyResult) -> Unit {
  if result.is_rejected() {
    return
  }
  if result.failed_count() > 0 ||
    result.warnings > policy.maximum_warnings ||
    result.score < policy.minimum_score {
    result.set_action(PolicyReview)
  } else {
    result.set_action(PolicyAccept)
  }
  if policy.strict && result.is_review() {
    result.set_action(PolicyReject)
    result.add_reason("strict policy converts review findings into rejection")
  }
}

///|
pub fn evaluate_audit_policy(
  report : RenderAuditReport,
  policy : AuditPolicy,
) -> AuditPolicyResult {
  let result = AuditPolicyResult::new(policy)
  ap_check_score(report, policy, result)
  ap_check_errors(report, policy, result)
  ap_check_warnings(report, policy, result)
  ap_check_contract(report, policy, result)
  ap_check_reproducibility(report, policy, result)
  ap_check_runtime(report, policy, result)
  ap_check_visual(report, policy, result)
  ap_check_export(report, policy, result)
  ap_check_performance(report, policy, result)
  ap_check_target(report, policy, result)
  result.set_score((report.score + result.pass_rate()) / 2.0)
  ap_finalize(policy, result)
  result
}

///|
pub fn evaluate_default_policy(report : RenderAuditReport) -> AuditPolicyResult {
  evaluate_audit_policy(report, AuditPolicy::default())
}

///|
pub fn evaluate_ci_policy(report : RenderAuditReport) -> AuditPolicyResult {
  evaluate_audit_policy(report, AuditPolicy::ci())
}

///|
pub fn evaluate_gallery_policy(report : RenderAuditReport) -> AuditPolicyResult {
  evaluate_audit_policy(report, AuditPolicy::gallery())
}

///|
pub fn evaluate_preview_policy(report : RenderAuditReport) -> AuditPolicyResult {
  evaluate_audit_policy(report, AuditPolicy::preview())
}

///|
pub fn evaluate_batch_policy(report : RenderAuditReport) -> AuditPolicyResult {
  evaluate_audit_policy(report, AuditPolicy::batch())
}

///|
pub fn policy_result_accepts(result : AuditPolicyResult) -> Bool {
  result.is_accepted()
}

///|
pub fn policy_result_requires_review(result : AuditPolicyResult) -> Bool {
  result.is_review()
}

///|
pub fn policy_result_rejects(result : AuditPolicyResult) -> Bool {
  result.is_rejected()
}

///|
pub fn policy_result_status(result : AuditPolicyResult) -> String {
  result.status()
}

///|
pub fn policy_result_score(result : AuditPolicyResult) -> Double {
  result.score
}

///|
pub fn policy_result_reasons(result : AuditPolicyResult) -> Array[String] {
  result.reasons
}

///|
pub fn policy_result_checks(
  result : AuditPolicyResult,
) -> Array[AuditPolicyCheck] {
  result.checks
}

///|
pub fn policy_result_failed_checks(
  result : AuditPolicyResult,
) -> Array[AuditPolicyCheck] {
  result.failed_checks
}

///|
pub fn policy_result_failed_check_names(
  result : AuditPolicyResult,
) -> Array[String] {
  result.failed_checks.map(fn(check) { ap_check_name(check) })
}

///|
pub fn policy_result_reason_text(result : AuditPolicyResult) -> String {
  result.reason_text()
}

///|
pub fn policy_result_check_rate(result : AuditPolicyResult) -> Double {
  result.pass_rate()
}

///|
pub fn policy_result_runtime_ok(result : AuditPolicyResult) -> Bool {
  result.runtime_delta >= 0
}

///|
pub fn policy_result_quality_ok(result : AuditPolicyResult) -> Bool {
  result.passed_check(CheckScore) && result.passed_check(CheckVisualQuality)
}

///|
pub fn policy_result_export_ok(result : AuditPolicyResult) -> Bool {
  result.passed_check(CheckExportQuality) && result.passed_check(CheckContract)
}

///|
pub fn policy_result_reproducible(result : AuditPolicyResult) -> Bool {
  result.passed_check(CheckReproducibility)
}

///|
pub fn policy_result_performant(result : AuditPolicyResult) -> Bool {
  result.passed_check(CheckPerformance) && result.passed_check(CheckRuntime)
}

///|
pub fn policy_result_target_ok(result : AuditPolicyResult) -> Bool {
  result.passed_check(CheckTarget)
}

///|
pub fn policy_result_gate(result : AuditPolicyResult, strict : Bool) -> Bool {
  if strict {
    result.is_accepted()
  } else {
    !result.is_rejected()
  }
}

///|
pub fn policy_result_publishable(result : AuditPolicyResult) -> Bool {
  result.is_accepted() &&
  policy_result_export_ok(result) &&
  policy_result_reproducible(result)
}

///|
pub fn policy_result_gallery_ready(result : AuditPolicyResult) -> Bool {
  result.is_accepted() &&
  policy_result_target_ok(result) &&
  policy_result_quality_ok(result)
}

///|
pub fn policy_result_preview_ready(result : AuditPolicyResult) -> Bool {
  !result.is_rejected() && result.passed_check(CheckErrors)
}

///|
pub fn policy_result_label(result : AuditPolicyResult) -> String {
  if policy_result_publishable(result) {
    "publishable"
  } else if policy_result_gallery_ready(result) {
    "gallery-ready"
  } else if policy_result_preview_ready(result) {
    "preview-ready"
  } else {
    "blocked"
  }
}

///|
pub fn policy_result_line(result : AuditPolicyResult) -> String {
  result.policy +
  " " +
  result.status() +
  " score=" +
  result.score.to_string() +
  " label=" +
  policy_result_label(result)
}

///|
pub fn policy_result_manifest(result : AuditPolicyResult) -> String {
  let builder = StringBuilder()
  builder.write_string("policy=" + result.policy + "\n")
  builder.write_string("action=" + result.status() + "\n")
  builder.write_string("label=" + policy_result_label(result) + "\n")
  builder.write_string("score=" + result.score.to_string() + "\n")
  builder.write_string("check_rate=" + result.pass_rate().to_string() + "\n")
  builder.write_string(
    "runtime_delta=" + result.runtime_delta.to_string() + "\n",
  )
  builder.write_string("reasons=" + result.reason_text() + "\n")
  builder.to_string()
}

///|
pub fn policy_result_digest(result : AuditPolicyResult) -> UInt {
  let text = result.to_text()
  let mut hash = 2166136261U
  for byte in @utf8.encode(text) {
    hash = (hash ^ byte.to_uint()) * 16777619U
  }
  hash
}

///|
pub fn policy_result_integrity(result : AuditPolicyResult) -> Bool {
  result.policy.length() > 0 &&
  result.check_count() == result.passed_count() + result.failed_count()
}

///|
pub fn policy_result_is_deterministic(
  first : AuditPolicyResult,
  second : AuditPolicyResult,
) -> Bool {
  policy_result_digest(first) == policy_result_digest(second)
}

///|
pub fn policy_result_copy(result : AuditPolicyResult) -> AuditPolicyResult {
  let copy = AuditPolicyResult::new(AuditPolicy::default().named(result.policy))
  copy.action = result.action
  copy.score = result.score
  copy.warnings = result.warnings
  copy.errors = result.errors
  copy.runtime_delta = result.runtime_delta
  for check in result.checks {
    copy.checks.push(check)
  }
  for check in result.passed_checks {
    copy.passed_checks.push(check)
  }
  for check in result.failed_checks {
    copy.failed_checks.push(check)
  }
  for reason in result.reasons {
    copy.reasons.push(reason)
  }
  copy
}

///|
pub fn policy_result_copy_preserves_digest(result : AuditPolicyResult) -> Bool {
  policy_result_digest(result) ==
  policy_result_digest(policy_result_copy(result))
}

///|
pub fn policy_result_copy_preserves_status(result : AuditPolicyResult) -> Bool {
  result.status() == policy_result_copy(result).status()
}

///|
pub fn policy_result_copy_preserves_checks(result : AuditPolicyResult) -> Bool {
  result.check_text() == policy_result_copy(result).check_text()
}

///|
pub fn policy_result_valid(result : AuditPolicyResult) -> Bool {
  policy_result_integrity(result) && result.score >= 0.0 && result.score <= 1.0
}

///|
pub fn policy_result_status_code(result : AuditPolicyResult) -> Int {
  match result.action {
    PolicyAccept => 0
    PolicyReview => 1
    PolicyReject => 2
  }
}

///|
pub fn policy_result_exit_code(result : AuditPolicyResult) -> Int {
  if result.is_accepted() {
    0
  } else if result.is_review() {
    2
  } else {
    1
  }
}

///|
pub fn policy_result_exit_success(
  result : AuditPolicyResult,
  allow_review : Bool,
) -> Bool {
  result.is_accepted() || (allow_review && result.is_review())
}

///|
pub fn policy_result_should_block(result : AuditPolicyResult) -> Bool {
  result.is_rejected() || result.errors > 0
}

///|
pub fn policy_result_should_notify(result : AuditPolicyResult) -> Bool {
  result.is_review() || result.warnings > 0
}

///|
pub fn policy_result_notification(result : AuditPolicyResult) -> String {
  if result.is_rejected() {
    "reject: " + result.reason_text()
  } else if result.is_review() {
    "review: " + result.reason_text()
  } else {
    "accept: audit passed"
  }
}

///|
pub fn policy_result_action_required(result : AuditPolicyResult) -> String {
  if result.is_rejected() {
    "fix blocking findings"
  } else if result.is_review() {
    "perform manual review"
  } else {
    "no action required"
  }
}

///|
pub fn policy_result_recommendation(result : AuditPolicyResult) -> String {
  result.policy + ": " + policy_result_action_required(result)
}

///|
pub fn policy_result_summary(result : AuditPolicyResult) -> String {
  policy_result_line(result) +
  " checks=" +
  result.passed_count().to_string() +
  "/" +
  result.check_count().to_string()
}

///|
pub fn policy_result_debug(result : AuditPolicyResult) -> String {
  policy_result_summary(result) +
  " digest=" +
  policy_result_digest(result).to_string()
}

///|
pub fn policy_result_markdown(result : AuditPolicyResult) -> String {
  let builder = StringBuilder()
  builder.write_string("## Policy result: " + result.policy + "\n\n")
  builder.write_string("- Action: " + result.status() + "\n")
  builder.write_string("- Label: " + policy_result_label(result) + "\n")
  builder.write_string("- Score: " + result.score.to_string() + "\n")
  builder.write_string(
    "- Check rate: " + result.pass_rate().to_string() + "\n\n",
  )
  builder.write_string("### Checks\n\n")
  for check in result.checks {
    builder.write_string("- " + ap_check_name(check) + "\n")
  }
  builder.write_string("\n### Reasons\n\n")
  for reason in result.reasons {
    builder.write_string("- " + reason + "\n")
  }
  builder.to_string()
}

///|
pub fn evaluate_policy_set(
  report : RenderAuditReport,
  policies : Array[AuditPolicy],
) -> Array[AuditPolicyResult] {
  policies.map(fn(policy) { evaluate_audit_policy(report, policy) })
}

///|
pub fn evaluate_standard_policies(
  report : RenderAuditReport,
) -> Array[AuditPolicyResult] {
  evaluate_policy_set(report, [
    AuditPolicy::preview(),
    AuditPolicy::default(),
    AuditPolicy::gallery(),
    AuditPolicy::ci(),
  ])
}

///|
pub fn policy_results_accepted(
  results : Array[AuditPolicyResult],
) -> Array[AuditPolicyResult] {
  results.filter(fn(result) { result.is_accepted() })
}

///|
pub fn policy_results_review(
  results : Array[AuditPolicyResult],
) -> Array[AuditPolicyResult] {
  results.filter(fn(result) { result.is_review() })
}

///|
pub fn policy_results_rejected(
  results : Array[AuditPolicyResult],
) -> Array[AuditPolicyResult] {
  results.filter(fn(result) { result.is_rejected() })
}

///|
pub fn policy_results_all_accepted(results : Array[AuditPolicyResult]) -> Bool {
  for result in results {
    if !result.is_accepted() {
      return false
    }
  }
  true
}

///|
pub fn policy_results_any_rejected(results : Array[AuditPolicyResult]) -> Bool {
  for result in results {
    if result.is_rejected() {
      return true
    }
  }
  false
}

///|
pub fn policy_results_best(
  results : Array[AuditPolicyResult],
) -> AuditPolicyResult? {
  if results.length() == 0 {
    None
  } else {
    let mut best = results[0]
    for result in results[1:] {
      if result.score > best.score {
        best = result
      }
    }
    Some(best)
  }
}

///|
pub fn policy_results_worst(
  results : Array[AuditPolicyResult],
) -> AuditPolicyResult? {
  if results.length() == 0 {
    None
  } else {
    let mut worst = results[0]
    for result in results[1:] {
      if result.score < worst.score {
        worst = result
      }
    }
    Some(worst)
  }
}

///|
pub fn policy_results_average_score(
  results : Array[AuditPolicyResult],
) -> Double {
  if results.length() == 0 {
    0.0
  } else {
    results.fold(init=0.0, fn(total, result) { total + result.score }) /
    results.length().to_double()
  }
}

///|
pub fn policy_results_digest(results : Array[AuditPolicyResult]) -> UInt {
  let mut hash = 2166136261U
  for result in results {
    hash = (hash ^ policy_result_digest(result)) * 16777619U
  }
  hash
}

///|
pub fn policy_results_text(results : Array[AuditPolicyResult]) -> String {
  let builder = StringBuilder()
  for result in results {
    builder.write_string(result.to_text() + "\n")
  }
  builder.to_string()
}

///|
pub fn policy_results_manifest(results : Array[AuditPolicyResult]) -> String {
  let builder = StringBuilder()
  builder.write_string("count=" + results.length().to_string() + "\n")
  builder.write_string(
    "accepted=" + policy_results_accepted(results).length().to_string() + "\n",
  )
  builder.write_string(
    "review=" + policy_results_review(results).length().to_string() + "\n",
  )
  builder.write_string(
    "rejected=" + policy_results_rejected(results).length().to_string() + "\n",
  )
  builder.write_string(
    "score=" + policy_results_average_score(results).to_string() + "\n",
  )
  builder.write_string(
    "digest=" + policy_results_digest(results).to_string() + "\n",
  )
  builder.to_string()
}

///|
pub fn policy_results_status(results : Array[AuditPolicyResult]) -> String {
  if policy_results_any_rejected(results) {
    "rejected"
  } else if policy_results_all_accepted(results) {
    "accepted"
  } else {
    "review"
  }
}

///|
pub fn policy_results_gate(
  results : Array[AuditPolicyResult],
  strict : Bool,
) -> Bool {
  if strict {
    policy_results_all_accepted(results)
  } else {
    !policy_results_any_rejected(results)
  }
}

///|
pub fn policy_results_ready_for_release(
  results : Array[AuditPolicyResult],
) -> Bool {
  policy_results_gate(results, true) &&
  policy_results_average_score(results) >= 0.75
}

///|
pub fn policy_results_ready_for_review(
  results : Array[AuditPolicyResult],
) -> Bool {
  results.length() > 0 && !policy_results_any_rejected(results)
}

///|
pub fn policy_results_release_line(
  results : Array[AuditPolicyResult],
) -> String {
  policy_results_status(results) +
  " score=" +
  policy_results_average_score(results).to_string() +
  " digest=" +
  policy_results_digest(results).to_string()
}

///|
pub fn policy_results_to_markdown(results : Array[AuditPolicyResult]) -> String {
  let builder = StringBuilder()
  builder.write_string("# Policy evaluation\n\n")
  builder.write_string("- Status: " + policy_results_status(results) + "\n")
  builder.write_string(
    "- Average score: " +
    policy_results_average_score(results).to_string() +
    "\n",
  )
  builder.write_string(
    "- Digest: " + policy_results_digest(results).to_string() + "\n\n",
  )
  for result in results {
    builder.write_string("- " + policy_result_summary(result) + "\n")
  }
  builder.to_string()
}

///|
pub fn AuditPolicyBatchResult::new(
  policy : AuditPolicy,
) -> AuditPolicyBatchResult {
  {
    policy: policy.name,
    total: 0,
    accepted: 0,
    review: 0,
    rejected: 0,
    score: 0.0,
    results: [],
  }
}

///|
pub fn AuditPolicyBatchResult::add(
  self : AuditPolicyBatchResult,
  result : AuditPolicyResult,
) -> Unit {
  self.results.push(result)
  self.total += 1
  self.score += result.score
  match result.action {
    PolicyAccept => self.accepted += 1
    PolicyReview => self.review += 1
    PolicyReject => self.rejected += 1
  }
}

///|
pub fn AuditPolicyBatchResult::finalize(self : AuditPolicyBatchResult) -> Unit {
  if self.total > 0 {
    self.score = self.score / self.total.to_double()
  }
}

///|
pub fn AuditPolicyBatchResult::is_accepted(
  self : AuditPolicyBatchResult,
) -> Bool {
  self.total > 0 && self.rejected == 0 && self.review == 0
}

///|
pub fn AuditPolicyBatchResult::is_review(self : AuditPolicyBatchResult) -> Bool {
  self.rejected == 0 && self.review > 0
}

///|
pub fn AuditPolicyBatchResult::is_rejected(
  self : AuditPolicyBatchResult,
) -> Bool {
  self.rejected > 0
}

///|
pub fn AuditPolicyBatchResult::status(self : AuditPolicyBatchResult) -> String {
  if self.is_rejected() {
    "rejected"
  } else if self.is_review() {
    "review"
  } else {
    "accepted"
  }
}

///|
pub fn AuditPolicyBatchResult::digest(self : AuditPolicyBatchResult) -> UInt {
  policy_results_digest(self.results)
}

///|
pub fn AuditPolicyBatchResult::to_text(self : AuditPolicyBatchResult) -> String {
  "policy=" +
  self.policy +
  " status=" +
  self.status() +
  " total=" +
  self.total.to_string() +
  " score=" +
  self.score.to_string() +
  " digest=" +
  self.digest().to_string()
}

///|
pub fn AuditPolicyBatchResult::to_json_string(
  self : AuditPolicyBatchResult,
  indent? : Int,
) -> String {
  ToJson::to_json(self).stringify(indent=indent.unwrap_or(0))
}

///|
pub fn evaluate_policy_batch(
  reports : Array[RenderAuditReport],
  policy : AuditPolicy,
) -> AuditPolicyBatchResult {
  let batch = AuditPolicyBatchResult::new(policy)
  for report in reports {
    batch.add(evaluate_audit_policy(report, policy))
  }
  batch.finalize()
  batch
}

///|
pub fn evaluate_ci_batch(
  reports : Array[RenderAuditReport],
) -> AuditPolicyBatchResult {
  evaluate_policy_batch(reports, AuditPolicy::ci())
}

///|
pub fn evaluate_gallery_batch(
  reports : Array[RenderAuditReport],
) -> AuditPolicyBatchResult {
  evaluate_policy_batch(reports, AuditPolicy::gallery())
}

///|
pub fn evaluate_default_batch(
  reports : Array[RenderAuditReport],
) -> AuditPolicyBatchResult {
  evaluate_policy_batch(reports, AuditPolicy::default())
}

///|
pub fn policy_batch_gate(batch : AuditPolicyBatchResult, strict : Bool) -> Bool {
  if strict {
    batch.is_accepted()
  } else {
    !batch.is_rejected()
  }
}

///|
pub fn policy_batch_release_gate(batch : AuditPolicyBatchResult) -> Bool {
  policy_batch_gate(batch, true) && batch.score >= 0.75
}

///|
pub fn policy_batch_review_gate(batch : AuditPolicyBatchResult) -> Bool {
  batch.total > 0 && !batch.is_rejected()
}

///|
pub fn policy_batch_summary(batch : AuditPolicyBatchResult) -> String {
  batch.to_text() +
  " accepted=" +
  batch.accepted.to_string() +
  " review=" +
  batch.review.to_string() +
  " rejected=" +
  batch.rejected.to_string()
}

///|
pub fn policy_batch_manifest(batch : AuditPolicyBatchResult) -> String {
  let builder = StringBuilder()
  builder.write_string("policy=" + batch.policy + "\n")
  builder.write_string("status=" + batch.status() + "\n")
  builder.write_string("total=" + batch.total.to_string() + "\n")
  builder.write_string("accepted=" + batch.accepted.to_string() + "\n")
  builder.write_string("review=" + batch.review.to_string() + "\n")
  builder.write_string("rejected=" + batch.rejected.to_string() + "\n")
  builder.write_string("score=" + batch.score.to_string() + "\n")
  builder.write_string("digest=" + batch.digest().to_string() + "\n")
  builder.to_string()
}

///|
pub fn policy_batch_to_markdown(batch : AuditPolicyBatchResult) -> String {
  let builder = StringBuilder()
  builder.write_string("## Batch policy: " + batch.policy + "\n\n")
  builder.write_string("- Status: " + batch.status() + "\n")
  builder.write_string("- Total: " + batch.total.to_string() + "\n")
  builder.write_string("- Accepted: " + batch.accepted.to_string() + "\n")
  builder.write_string("- Review: " + batch.review.to_string() + "\n")
  builder.write_string("- Rejected: " + batch.rejected.to_string() + "\n")
  builder.write_string("- Score: " + batch.score.to_string() + "\n")
  for result in batch.results {
    builder.write_string("- " + policy_result_summary(result) + "\n")
  }
  builder.to_string()
}

///|
pub fn policy_batch_result_at(
  batch : AuditPolicyBatchResult,
  index : Int,
) -> AuditPolicyResult? {
  if index >= 0 && index < batch.results.length() {
    Some(batch.results[index])
  } else {
    None
  }
}

///|
pub fn policy_batch_failed(
  batch : AuditPolicyBatchResult,
) -> Array[AuditPolicyResult] {
  batch.results.filter(fn(result) { result.is_rejected() })
}

///|
pub fn policy_batch_needing_review(
  batch : AuditPolicyBatchResult,
) -> Array[AuditPolicyResult] {
  batch.results.filter(fn(result) { result.is_review() })
}

///|
pub fn policy_batch_successful(
  batch : AuditPolicyBatchResult,
) -> Array[AuditPolicyResult] {
  batch.results.filter(fn(result) { result.is_accepted() })
}

///|
pub fn policy_batch_all_contracts_valid(batch : AuditPolicyBatchResult) -> Bool {
  for result in batch.results {
    if !result.passed_check(CheckContract) {
      return false
    }
  }
  true
}

///|
pub fn policy_batch_all_reproducible(batch : AuditPolicyBatchResult) -> Bool {
  for result in batch.results {
    if !result.passed_check(CheckReproducibility) {
      return false
    }
  }
  true
}

///|
pub fn policy_batch_average_pass_rate(batch : AuditPolicyBatchResult) -> Double {
  if batch.results.length() == 0 {
    0.0
  } else {
    batch.results.fold(init=0.0, fn(total, result) {
      total + result.pass_rate()
    }) /
    batch.results.length().to_double()
  }
}

///|
pub fn policy_batch_runtime_delta(batch : AuditPolicyBatchResult) -> Int {
  batch.results.fold(init=0, fn(total, result) { total + result.runtime_delta })
}

///|
pub fn policy_batch_error_count(batch : AuditPolicyBatchResult) -> Int {
  batch.results.fold(init=0, fn(total, result) { total + result.errors })
}

///|
pub fn policy_batch_warning_count(batch : AuditPolicyBatchResult) -> Int {
  batch.results.fold(init=0, fn(total, result) { total + result.warnings })
}

///|
pub fn policy_batch_has_errors(batch : AuditPolicyBatchResult) -> Bool {
  policy_batch_error_count(batch) > 0
}

///|
pub fn policy_batch_has_warnings(batch : AuditPolicyBatchResult) -> Bool {
  policy_batch_warning_count(batch) > 0
}

///|
pub fn policy_batch_risk(batch : AuditPolicyBatchResult) -> String {
  if batch.is_rejected() || policy_batch_has_errors(batch) {
    "high"
  } else if batch.is_review() || policy_batch_has_warnings(batch) {
    "medium"
  } else {
    "low"
  }
}

///|
pub fn policy_batch_risk_line(batch : AuditPolicyBatchResult) -> String {
  "risk=" +
  policy_batch_risk(batch) +
  " status=" +
  batch.status() +
  " score=" +
  batch.score.to_string()
}

///|
pub fn policy_batch_notification(batch : AuditPolicyBatchResult) -> String {
  if batch.is_rejected() {
    "batch rejected: fix failed exports"
  } else if batch.is_review() {
    "batch needs review: inspect flagged exports"
  } else {
    "batch accepted: exports are ready"
  }
}

///|
pub fn policy_batch_recommendation(batch : AuditPolicyBatchResult) -> String {
  if batch.is_rejected() {
    "remove blockers and rerun the policy"
  } else if batch.is_review() {
    "review warnings before publishing"
  } else {
    "publish the reproducible artifacts"
  }
}

///|
pub fn policy_batch_action_line(batch : AuditPolicyBatchResult) -> String {
  policy_batch_notification(batch) + "; " + policy_batch_recommendation(batch)
}

///|
pub fn policy_batch_contract_line(batch : AuditPolicyBatchResult) -> String {
  "contracts=" +
  policy_batch_all_contracts_valid(batch).to_string() +
  " reproducible=" +
  policy_batch_all_reproducible(batch).to_string()
}

///|
pub fn policy_batch_runtime_line(batch : AuditPolicyBatchResult) -> String {
  "runtime_delta=" +
  policy_batch_runtime_delta(batch).to_string() +
  " average_pass_rate=" +
  policy_batch_average_pass_rate(batch).to_string()
}

///|
pub fn policy_batch_dashboard(batch : AuditPolicyBatchResult) -> String {
  policy_batch_summary(batch) +
  "\n" +
  policy_batch_risk_line(batch) +
  "\n" +
  policy_batch_contract_line(batch) +
  "\n" +
  policy_batch_runtime_line(batch)
}