///|
/// Evidence Capsule converts an Intent Gate report into a deterministic
/// release-review artifact. It gives reviewers a stable fingerprint, a
/// remediation checklist, and a concise non-overlap statement that sits above
/// ordinary robots.txt parsing or crawl planning.
pub(all) enum CapsuleReadiness {
  CapsuleReady
  CapsuleNeedsReview
  CapsuleBlocked
} derive(Eq, Debug)

///|
pub(all) enum RemediationSeverity {
  RemediationInfo
  RemediationWarning
  RemediationCritical
} derive(Eq, Debug)

///|
pub(all) enum RemediationKind {
  RemediationNoIntent
  RemediationRobotsBlock
  RemediationExplicitAuthorization
  RemediationMissingEvidence
  RemediationHumanReview
  RemediationHighRisk
  RemediationRetention
} derive(Eq, Debug)

///|
pub(all) struct RemediationItem {
  id : String
  intent_label : String
  kind : RemediationKind
  severity : RemediationSeverity
  verdict : IntentVerdict
  action : String
  evidence_hint : String
} derive(Eq, Debug)

///|
pub(all) struct EvidenceCapsule {
  project_name : String
  version : String
  fingerprint : String
  readiness : CapsuleReadiness
  source_summary : String
  novelty_statement : String
  reviewer_note : String
  checklist : Array[String]
  remediation_items : Array[RemediationItem]
  report : IntentGateReport
} derive(Eq, Debug)

///|
pub fn CapsuleReadiness::label(self : CapsuleReadiness) -> String {
  match self {
    CapsuleReady => "ready"
    CapsuleNeedsReview => "needs-review"
    CapsuleBlocked => "blocked"
  }
}

///|
pub fn RemediationSeverity::label(self : RemediationSeverity) -> String {
  match self {
    RemediationInfo => "info"
    RemediationWarning => "warning"
    RemediationCritical => "critical"
  }
}

///|
pub fn RemediationKind::label(self : RemediationKind) -> String {
  match self {
    RemediationNoIntent => "no-intent"
    RemediationRobotsBlock => "robots-block"
    RemediationExplicitAuthorization => "explicit-authorization"
    RemediationMissingEvidence => "missing-evidence"
    RemediationHumanReview => "human-review"
    RemediationHighRisk => "high-risk"
    RemediationRetention => "retention"
  }
}

///|
pub fn build_evidence_capsule(
  report : IntentGateReport,
  project_name : StringView,
  version : StringView,
) -> EvidenceCapsule {
  let project = project_name.to_owned()
  let release_version = version.to_owned()
  let remediation_items = capsule_remediations(report)
  let readiness = capsule_readiness(report, remediation_items)
  let source_summary = capsule_source_summary(report)
  let novelty_statement = capsule_novelty_statement()
  let reviewer_note = capsule_reviewer_note(readiness, remediation_items)
  let checklist = capsule_checklist(report, readiness)
  let fingerprint = capsule_fingerprint(report, project, release_version)
  {
    project_name: project,
    version: release_version,
    fingerprint,
    readiness,
    source_summary,
    novelty_statement,
    reviewer_note,
    checklist,
    remediation_items,
    report,
  }
}

///|
pub fn IntentGateReport::to_evidence_capsule(
  self : IntentGateReport,
  project_name : StringView,
  version : StringView,
) -> EvidenceCapsule {
  build_evidence_capsule(self, project_name, version)
}

///|
pub fn EvidenceCapsule::is_release_ready(self : EvidenceCapsule) -> Bool {
  self.readiness == CapsuleReady && self.remediation_items.is_empty()
}

///|
pub fn EvidenceCapsule::has_remediation(
  self : EvidenceCapsule,
  kind : RemediationKind,
) -> Bool {
  self.remediation_items.any(item => item.kind == kind)
}

///|
pub fn EvidenceCapsule::to_line(self : EvidenceCapsule) -> String {
  self.project_name +
  "@" +
  self.version +
  " readiness=" +
  self.readiness.label() +
  " fingerprint=" +
  self.fingerprint +
  " remediations=" +
  self.remediation_items.length().to_string()
}

///|
pub fn EvidenceCapsule::to_markdown(self : EvidenceCapsule) -> String {
  let lines : Array[String] = []
  lines.push("# RoboPolicy Evidence Capsule")
  lines.push("")
  lines.push("- Project: " + self.project_name)
  lines.push("- Version: " + self.version)
  lines.push("- Fingerprint: " + self.fingerprint)
  lines.push("- Readiness: " + self.readiness.label())
  lines.push("- Source: " + self.source_summary)
  lines.push("- Non-overlap: " + self.novelty_statement)
  lines.push("- Reviewer note: " + self.reviewer_note)
  lines.push("")
  lines.push("## Release Checklist")
  lines.push("")
  for item in self.checklist {
    lines.push("- " + item)
  }
  lines.push("")
  lines.push("## Remediation Items")
  lines.push("")
  if self.remediation_items.is_empty() {
    lines.push("- none")
  } else {
    lines.push(
      "| ID | Intent | Kind | Severity | Verdict | Action | Evidence |",
    )
    lines.push("| --- | --- | --- | --- | --- | --- | --- |")
    for item in self.remediation_items {
      lines.push(item.to_markdown_row())
    }
  }
  lines.join("\n")
}

///|
pub fn EvidenceCapsule::to_json(self : EvidenceCapsule) -> String {
  "{" +
  "\"project\":\"" +
  capsule_json_escape(self.project_name) +
  "\",\"version\":\"" +
  capsule_json_escape(self.version) +
  "\",\"fingerprint\":\"" +
  capsule_json_escape(self.fingerprint) +
  "\",\"readiness\":\"" +
  self.readiness.label() +
  "\",\"source_summary\":\"" +
  capsule_json_escape(self.source_summary) +
  "\",\"novelty_statement\":\"" +
  capsule_json_escape(self.novelty_statement) +
  "\",\"reviewer_note\":\"" +
  capsule_json_escape(self.reviewer_note) +
  "\",\"checklist\":" +
  capsule_string_array_to_json(self.checklist) +
  ",\"remediation_items\":" +
  capsule_remediations_to_json(self.remediation_items) +
  ",\"intent_summary\":" +
  self.report.summary.to_json() +
  "}"
}

///|
pub fn RemediationItem::to_markdown_row(self : RemediationItem) -> String {
  "| " +
  capsule_cell(self.id) +
  " | " +
  capsule_cell(self.intent_label) +
  " | " +
  self.kind.label() +
  " | " +
  self.severity.label() +
  " | " +
  self.verdict.label() +
  " | " +
  capsule_cell(self.action) +
  " | " +
  capsule_cell(self.evidence_hint) +
  " |"
}

///|
pub fn RemediationItem::to_json(self : RemediationItem) -> String {
  "{" +
  "\"id\":\"" +
  capsule_json_escape(self.id) +
  "\",\"intent\":\"" +
  capsule_json_escape(self.intent_label) +
  "\",\"kind\":\"" +
  self.kind.label() +
  "\",\"severity\":\"" +
  self.severity.label() +
  "\",\"verdict\":\"" +
  self.verdict.label() +
  "\",\"action\":\"" +
  capsule_json_escape(self.action) +
  "\",\"evidence_hint\":\"" +
  capsule_json_escape(self.evidence_hint) +
  "\"}"
}

///|
pub fn sample_evidence_capsule_markdown() -> String {
  review_intents(parse(sample_robots()), sample_intents())
  .to_evidence_capsule("RoboPolicy Intent Gate", "0.2.0")
  .to_markdown()
}

///|
fn capsule_remediations(report : IntentGateReport) -> Array[RemediationItem] {
  let items : Array[RemediationItem] = []
  if report.summary.total_intents == 0 {
    items.push(
      remediation(
        "CAP-000",
        "release",
        RemediationNoIntent,
        RemediationCritical,
        IntentDenied,
        "add at least one crawl intent before release review",
        "intent-list",
      ),
    )
  }
  for review in report.reviews {
    if !review.robots_allowed {
      items.push(
        remediation_for_review(
          review,
          RemediationRobotsBlock,
          RemediationCritical,
          "remove the target or obtain an updated robots policy that permits it",
          "robots-snapshot",
        ),
      )
    }
    if review.intent.needs_explicit_authorization() &&
      !review.intent.has_explicit_authorization() {
      items.push(
        remediation_for_review(
          review,
          RemediationExplicitAuthorization,
          RemediationCritical,
          "attach owner approval, contractual consent, or internal-test scope",
          "authorization-ticket",
        ),
      )
    }
    if !review.intent.has_evidence() {
      items.push(
        remediation_for_review(
          review,
          RemediationMissingEvidence,
          RemediationWarning,
          "add a reviewer-visible evidence id for this intent",
          "evidence-id",
        ),
      )
    }
    if review.requires_human_review {
      items.push(
        remediation_for_review(
          review,
          RemediationHumanReview,
          RemediationWarning,
          "record a human release decision before running the crawler",
          "review-record",
        ),
      )
    }
    if review.risk_band == IntentRiskOrange || review.risk_band == IntentRiskRed {
      items.push(
        remediation_for_review(
          review,
          RemediationHighRisk,
          RemediationWarning,
          "lower sensitivity, shorten retention, or split this intent into a safer scope",
          "risk-review",
        ),
      )
    }
    if review.intent.retention_days > 90 &&
      review.intent.consent != ConsentContractual {
      items.push(
        remediation_for_review(
          review,
          RemediationRetention,
          RemediationWarning,
          "reduce retention to 90 days or attach contractual consent",
          "retention-plan",
        ),
      )
    }
  }
  items
}

///|
fn capsule_readiness(
  report : IntentGateReport,
  remediation_items : Array[RemediationItem],
) -> CapsuleReadiness {
  if report.summary.total_intents == 0 || report.summary.denied_intents > 0 {
    CapsuleBlocked
  } else if report.summary.review_intents > 0 ||
    report.summary.high_risk_intents > 0 ||
    report.summary.missing_evidence > 0 ||
    !remediation_items.is_empty() {
    CapsuleNeedsReview
  } else {
    CapsuleReady
  }
}

///|
fn capsule_source_summary(report : IntentGateReport) -> String {
  report.summary.to_line() +
  "; targets=" +
  report.target_summary.total_targets.to_string() +
  "; target-score=" +
  report.target_summary.score.to_string()
}

///|
fn capsule_novelty_statement() -> String {
  "captures AI intent verdicts, authorization evidence, remediation gaps, and a stable release fingerprint above robots access checks"
}

///|
fn capsule_reviewer_note(
  readiness : CapsuleReadiness,
  remediation_items : Array[RemediationItem],
) -> String {
  match readiness {
    CapsuleReady =>
      "ready to attach to CI, release notes, and acceptance materials"
    CapsuleNeedsReview =>
      "resolve " +
      remediation_items.length().to_string() +
      " review item(s) before production release"
    CapsuleBlocked =>
      "blocked until denied intents or missing intent evidence are corrected"
  }
}

///|
fn capsule_checklist(
  report : IntentGateReport,
  readiness : CapsuleReadiness,
) -> Array[String] {
  let checklist : Array[String] = []
  checklist.push("MoonBit report generated from deterministic offline inputs")
  checklist.push("Intent verdicts recorded: " + report.summary.to_line())
  checklist.push(
    "Robots evidence attached for " +
    report.summary.total_intents.to_string() +
    " intent(s)",
  )
  checklist.push("Release readiness: " + readiness.label())
  if report.summary.explicit_authorization > 0 {
    checklist.push("At least one intent records explicit authorization")
  } else {
    checklist.push("No explicit authorization recorded yet")
  }
  if report.summary.missing_evidence == 0 {
    checklist.push("Every intent has a reusable evidence id")
  } else {
    checklist.push(
      report.summary.missing_evidence.to_string() +
      " intent(s) still need reusable evidence id",
    )
  }
  checklist
}

///|
fn capsule_fingerprint(
  report : IntentGateReport,
  project_name : String,
  version : String,
) -> String {
  let seed = capsule_fingerprint_seed(report, project_name, version)
  "rpec-" +
  seed.length().to_string() +
  "-" +
  capsule_checksum(seed).to_string() +
  "-" +
  report.summary.score.to_string()
}

///|
fn capsule_fingerprint_seed(
  report : IntentGateReport,
  project_name : String,
  version : String,
) -> String {
  let lines : Array[String] = []
  lines.push(project_name)
  lines.push(version)
  lines.push(report.summary.to_line())
  lines.push(report.target_summary.to_line())
  for review in report.reviews {
    lines.push(review.evidence_line)
    lines.push(review.risk_band.label())
    lines.push(review.verdict.label())
    lines.push(review.blockers.join(";"))
    lines.push(review.obligations.join(";"))
  }
  lines.join("\n")
}

///|
fn capsule_checksum(text : String) -> Int {
  let mut sum = 5381
  let mut index = 1
  for ch in text {
    sum = sum * 17 + capsule_char_weight(ch) + index
    while sum > 100000000 {
      sum = sum - 99998311
    }
    index = index + 1
  }
  if sum < 0 {
    0 - sum
  } else {
    sum
  }
}

///|
fn capsule_char_weight(ch : Char) -> Int {
  match ch {
    'a' => 11
    'e' => 13
    'i' => 17
    'o' => 19
    'u' => 23
    'r' => 29
    's' => 31
    't' => 37
    'n' => 41
    'p' => 43
    'd' => 47
    'c' => 53
    'm' => 59
    'g' => 61
    '/' => 67
    '-' => 71
    '_' => 73
    ':' => 79
    ';' => 83
    '|' => 89
    '\n' => 97
    '0' => 101
    '1' => 103
    '2' => 107
    '3' => 109
    '4' => 113
    '5' => 127
    '6' => 131
    '7' => 137
    '8' => 139
    '9' => 149
    _ => 151
  }
}

///|
fn remediation_for_review(
  review : IntentReview,
  kind : RemediationKind,
  severity : RemediationSeverity,
  action : StringView,
  evidence_hint : StringView,
) -> RemediationItem {
  remediation(
    "CAP-" + review.intent.label + "-" + kind.label(),
    review.intent.label,
    kind,
    severity,
    review.verdict,
    action,
    evidence_hint,
  )
}

///|
fn remediation(
  id : StringView,
  intent_label : StringView,
  kind : RemediationKind,
  severity : RemediationSeverity,
  verdict : IntentVerdict,
  action : StringView,
  evidence_hint : StringView,
) -> RemediationItem {
  {
    id: id.to_owned(),
    intent_label: intent_label.to_owned(),
    kind,
    severity,
    verdict,
    action: action.to_owned(),
    evidence_hint: evidence_hint.to_owned(),
  }
}

///|
fn capsule_remediations_to_json(items : Array[RemediationItem]) -> String {
  let rows : Array[String] = []
  for item in items {
    rows.push(item.to_json())
  }
  "[" + rows.join(",") + "]"
}

///|
fn capsule_string_array_to_json(values : Array[String]) -> String {
  let rows : Array[String] = []
  for value in values {
    rows.push("\"" + capsule_json_escape(value) + "\"")
  }
  "[" + rows.join(",") + "]"
}

///|
fn capsule_cell(value : String) -> String {
  value.replace_all(old="|", new="\\|")
}

///|
fn capsule_json_escape(value : String) -> String {
  value
  .replace_all(old="\\", new="\\\\")
  .replace_all(old="\"", new="\\\"")
  .replace_all(old="\n", new="\\n")
  .replace_all(old="\r", new="\\r")
  .replace_all(old="\t", new="\\t")
}