///|
pub(all) enum BoundaryRole {
  RelatedPriorWork
  AuxiliaryCompatibility
  NewContribution
  OutOfScope
} derive(Eq, Debug)

///|
pub(all) struct BoundaryItem {
  id : String
  claim : String
  role : BoundaryRole
  related_package : String
  implementation : String
  proof : String
  reviewer_note : String
} derive(Eq, Debug)

///|
pub fn BoundaryItem::BoundaryItem(
  id? : String = "",
  claim? : String = "",
  role? : BoundaryRole = NewContribution,
  related_package? : String = "",
  implementation? : String = "",
  proof? : String = "",
  reviewer_note? : String = "",
) -> BoundaryItem {
  { id, claim, role, related_package, implementation, proof, reviewer_note }
}

///|
pub(all) struct BoundaryMatrix {
  project_title : String
  package_name : String
  repository : String
  related_prior_work : String
  repository_name_note : String
  items : Array[BoundaryItem]
} derive(Eq, Debug)

///|
pub fn BoundaryMatrix::BoundaryMatrix(
  project_title? : String = "",
  package_name? : String = "",
  repository? : String = "",
  related_prior_work? : String = "",
  repository_name_note? : String = "",
  items? : Array[BoundaryItem] = [],
) -> BoundaryMatrix {
  {
    project_title,
    package_name,
    repository,
    related_prior_work,
    repository_name_note,
    items,
  }
}

///|
pub(all) struct BoundaryFinding {
  id : String
  severity : Severity
  title : String
  detail : String
  action : String
  evidence : String
  penalty : Int
} derive(Eq, Debug)

///|
fn boundary_finding(
  id : String,
  severity : Severity,
  title : String,
  detail : String,
  action : String,
  evidence : String,
  penalty : Int,
) -> BoundaryFinding {
  { id, severity, title, detail, action, evidence, penalty }
}

///|
pub(all) struct BoundaryReport {
  verdict : String
  score : Int
  duplicate_risk : String
  new_contribution_count : Int
  auxiliary_count : Int
  related_count : Int
  out_of_scope_count : Int
  incomplete_new_count : Int
  findings : Array[BoundaryFinding]
} derive(Eq, Debug)

///|
pub fn boundary_matrix_report(matrix : BoundaryMatrix) -> BoundaryReport {
  let findings = Array::new(capacity=12)
  push_boundary_identity_findings(findings, matrix)
  push_boundary_relation_findings(findings, matrix)
  push_boundary_item_findings(findings, matrix)
  let mut penalty = 0
  let mut fail_count = 0
  for finding in findings {
    penalty += finding.penalty
    if finding.severity == Fail {
      fail_count += 1
    }
  }
  let new_count = boundary_role_count(matrix.items, NewContribution)
  let auxiliary_count = boundary_role_count(
    matrix.items,
    AuxiliaryCompatibility,
  )
  let related_count = boundary_role_count(matrix.items, RelatedPriorWork)
  let out_count = boundary_role_count(matrix.items, OutOfScope)
  let incomplete_new = incomplete_new_contribution_count(matrix.items)
  let score = clamp_score(100 - penalty)
  let ready = fail_count == 0 &&
    score >= 90 &&
    new_count >= 3 &&
    incomplete_new == 0
  let verdict = if ready { "distinct" } else { "boundary-risk" }
  let duplicate_risk = if ready {
    "low"
  } else if fail_count > 0 || new_count < 2 {
    "high"
  } else {
    "medium"
  }
  {
    verdict,
    score,
    duplicate_risk,
    new_contribution_count: new_count,
    auxiliary_count,
    related_count,
    out_of_scope_count: out_count,
    incomplete_new_count: incomplete_new,
    findings,
  }
}

///|
pub fn boundary_matrix_markdown(matrix : BoundaryMatrix) -> String {
  boundary_matrix_report(matrix).to_markdown(matrix)
}

///|
pub fn BoundaryReport::to_markdown(
  self : BoundaryReport,
  matrix : BoundaryMatrix,
) -> String {
  let out = StringBuilder()
  out.write_string("# ReviewProof Non-Duplication Boundary\n\n")
  out.write_string("- Project: " + matrix.project_title + "\n")
  out.write_string("- Package: " + matrix.package_name + "\n")
  out.write_string("- Repository: " + matrix.repository + "\n")
  out.write_string("- Related prior work: " + matrix.related_prior_work + "\n")
  out.write_string("- Verdict: " + self.verdict + "\n")
  out.write_string("- Score: " + self.score.to_string() + "/100\n")
  out.write_string("- Duplicate risk: " + self.duplicate_risk + "\n")
  out.write_string(
    "- New contributions: " + self.new_contribution_count.to_string() + "\n\n",
  )
  if matrix.repository_name_note != "" {
    out.write_string("## Repository Name Note\n\n")
    out.write_string(matrix.repository_name_note + "\n\n")
  }
  out.write_string("## Boundary Matrix\n\n")
  out.write_string("| id | role | claim | related package | proof |\n")
  out.write_string("| --- | --- | --- | --- | --- |\n")
  for item in matrix.items {
    out.write_string("| `" + item.id + "` | ")
    out.write_string(boundary_role_text(item.role) + " | ")
    out.write_string(boundary_table_cell(item.claim) + " | ")
    out.write_string(boundary_table_cell(item.related_package) + " | ")
    out.write_string(boundary_table_cell(item.proof) + " |\n")
  }
  out.write_string("\n## Reviewer Notes\n\n")
  for item in matrix.items {
    if item.reviewer_note != "" {
      out.write_string("- `" + item.id + "` ")
      out.write_string(item.reviewer_note + "\n")
    }
  }
  out.write_string("\n## Findings\n\n")
  for finding in self.findings {
    out.write_string("- [" + severity_text(finding.severity) + "] ")
    out.write_string(finding.id + ": " + finding.title)
    if finding.evidence != "" {
      out.write_string(" -- " + finding.evidence)
    }
    if finding.action != "" && finding.severity != Pass {
      out.write_string(" Action: " + finding.action)
    }
    out.write_string("\n")
  }
  out.to_string()
}

///|
pub fn boundary_summary_line(report : BoundaryReport) -> String {
  "boundary=" +
  report.verdict +
  " score=" +
  report.score.to_string() +
  " risk=" +
  report.duplicate_risk +
  " new=" +
  report.new_contribution_count.to_string() +
  " incomplete=" +
  report.incomplete_new_count.to_string()
}

///|
pub fn example_boundary_matrix() -> BoundaryMatrix {
  BoundaryMatrix(
    project_title="ReviewProof: MoonBit review feedback and resubmission proof toolkit",
    package_name="WB-ai-nb/reviewproof-kit",
    repository="https://github.com/WB-ai-nb/harborcheck",
    related_prior_work="EJJ-ai-nb/harborcheck@0.1.2 was named by initial review as overlapping prior work.",
    repository_name_note="The GitHub repository path remains harborcheck only to preserve contest-period history and CI links. The submitted package, README, API and proposal identify the corrected project as ReviewProof.",
    items=[
      BoundaryItem(
        id="old-readme-checklist",
        claim="Generic README example and package checklist checks",
        role=AuxiliaryCompatibility,
        related_package="EJJ-ai-nb/harborcheck",
        implementation="audit, doc_proof and supporting snapshot checks",
        proof="README.md and docs/research.md disclose this overlap",
        reviewer_note="Kept only as auxiliary acceptance evidence, not claimed as the new contribution.",
      ),
      BoundaryItem(
        id="feedback-model",
        claim="Initial-review feedback is converted into typed MoonBit data",
        role=NewContribution,
        related_package="",
        implementation="ReviewFeedback, ReviewResponse and review_feedback_response",
        proof="reviewproof.mbt and review feedback tests",
        reviewer_note="This is the corrected primary API boundary.",
      ),
      BoundaryItem(
        id="prior-work-disclosure",
        claim="Prior package, license, overlap and relation are first-class records",
        role=NewContribution,
        related_package="EJJ-ai-nb/harborcheck",
        implementation="PriorWork and ExtensionEvidence",
        proof="docs/review-response.md and SUBMISSION.md",
        reviewer_note="This directly answers the official feedback about missing extension relationship.",
      ),
      BoundaryItem(
        id="resubmission-dossier",
        claim="Resubmission dossier checks proof paths, timeline and risk closure",
        role=NewContribution,
        related_package="",
        implementation="DossierProfile, DossierReport and resubmission_dossier",
        proof="dossier.mbt and dossier tests",
        reviewer_note="This is not a release checklist; it is a reviewer-facing correction dossier.",
      ),
      BoundaryItem(
        id="non-duplication-boundary",
        claim="Non-duplication matrix separates overlap, auxiliary compatibility and new work",
        role=NewContribution,
        related_package="EJJ-ai-nb/harborcheck",
        implementation="BoundaryMatrix and boundary_matrix_report",
        proof="boundary.mbt and boundary tests",
        reviewer_note="This file exists to prevent another same-scope misunderstanding.",
      ),
      BoundaryItem(
        id="auto-mooncakes-crawler",
        claim="Automatically crawling all Mooncakes packages",
        role=OutOfScope,
        related_package="",
        implementation="",
        proof="README unsupported scope",
        reviewer_note="The library remains pure MoonBit data logic and does not perform network scans.",
      ),
    ],
  )
}

///|
fn push_boundary_identity_findings(
  findings : Array[BoundaryFinding],
  matrix : BoundaryMatrix,
) -> Unit {
  if matrix.project_title == "" ||
    matrix.package_name == "" ||
    matrix.repository == "" {
    findings.push(
      boundary_finding(
        "boundary.identity",
        Fail,
        "Project, package and repository are identified",
        "A boundary matrix must point reviewers to the corrected project.",
        "Fill project_title, package_name and repository.",
        matrix.project_title + " / " + matrix.package_name,
        20,
      ),
    )
  } else {
    findings.push(
      boundary_finding(
        "boundary.identity",
        Pass,
        "Project, package and repository are identified",
        "",
        "",
        matrix.package_name + " / " + matrix.repository,
        0,
      ),
    )
  }
}

///|
fn push_boundary_relation_findings(
  findings : Array[BoundaryFinding],
  matrix : BoundaryMatrix,
) -> Unit {
  if matrix.related_prior_work == "" {
    findings.push(
      boundary_finding(
        "boundary.prior-work",
        Fail,
        "Related prior work is named",
        "A matrix without prior-work disclosure cannot answer an overlap rejection.",
        "Name the related package and review reason.",
        "",
        25,
      ),
    )
  } else {
    findings.push(
      boundary_finding(
        "boundary.prior-work",
        Pass,
        "Related prior work is named",
        "",
        "",
        matrix.related_prior_work,
        0,
      ),
    )
  }
  if matrix.repository.contains("harborcheck") &&
    matrix.repository_name_note == "" {
    findings.push(
      boundary_finding(
        "boundary.repository-name",
        Warn,
        "Repository name is explained",
        "The repository path still contains harborcheck, so reviewers need a history note.",
        "Explain that the repository name is retained for traceability.",
        matrix.repository,
        8,
      ),
    )
  } else {
    findings.push(
      boundary_finding(
        "boundary.repository-name",
        Pass,
        "Repository name is explained",
        "",
        "",
        matrix.repository_name_note,
        0,
      ),
    )
  }
}

///|
fn push_boundary_item_findings(
  findings : Array[BoundaryFinding],
  matrix : BoundaryMatrix,
) -> Unit {
  let new_count = boundary_role_count(matrix.items, NewContribution)
  let related_count = boundary_role_count(matrix.items, RelatedPriorWork) +
    boundary_role_count(matrix.items, AuxiliaryCompatibility)
  let incomplete = incomplete_new_contribution_count(matrix.items)
  if matrix.items.length() == 0 {
    findings.push(
      boundary_finding(
        "boundary.items",
        Fail,
        "Boundary items are present",
        "Reviewers cannot distinguish overlap from new work without item rows.",
        "Add overlap, auxiliary, new contribution and out-of-scope rows.",
        "",
        25,
      ),
    )
  } else {
    findings.push(
      boundary_finding(
        "boundary.items",
        Pass,
        "Boundary items are present",
        "",
        "",
        matrix.items.length().to_string() + " rows",
        0,
      ),
    )
  }
  if new_count < 3 {
    findings.push(
      boundary_finding(
        "boundary.new-work",
        Fail,
        "At least three new contribution rows are documented",
        "A corrected submission needs concrete new work, not only overlap disclosure.",
        "Add implemented ReviewProof-specific contribution rows.",
        new_count.to_string() + " new rows",
        25,
      ),
    )
  } else {
    findings.push(
      boundary_finding(
        "boundary.new-work",
        Pass,
        "At least three new contribution rows are documented",
        "",
        "",
        new_count.to_string() + " new rows",
        0,
      ),
    )
  }
  if related_count == 0 {
    findings.push(
      boundary_finding(
        "boundary.overlap",
        Warn,
        "Overlap or auxiliary rows are explicit",
        "The matrix should say which old functions are not claimed as new.",
        "Add RelatedPriorWork or AuxiliaryCompatibility rows.",
        "",
        8,
      ),
    )
  } else {
    findings.push(
      boundary_finding(
        "boundary.overlap",
        Pass,
        "Overlap or auxiliary rows are explicit",
        "",
        "",
        related_count.to_string() + " overlap-aware rows",
        0,
      ),
    )
  }
  if incomplete > 0 {
    findings.push(
      boundary_finding(
        "boundary.proof",
        Fail,
        "New contribution rows have implementation and proof",
        "Every new contribution row must name implementation and reviewer proof.",
        "Fill implementation and proof on all NewContribution rows.",
        incomplete.to_string() + " incomplete new rows",
        18 + incomplete * 4,
      ),
    )
  } else {
    findings.push(
      boundary_finding(
        "boundary.proof",
        Pass,
        "New contribution rows have implementation and proof",
        "",
        "",
        "all new rows complete",
        0,
      ),
    )
  }
}

///|
fn boundary_role_count(items : Array[BoundaryItem], role : BoundaryRole) -> Int {
  let mut count = 0
  for item in items {
    if item.role == role {
      count += 1
    }
  }
  count
}

///|
fn incomplete_new_contribution_count(items : Array[BoundaryItem]) -> Int {
  let mut count = 0
  for item in items {
    if item.role == NewContribution &&
      (item.implementation == "" || item.proof == "") {
      count += 1
    }
  }
  count
}

///|
fn boundary_role_text(role : BoundaryRole) -> String {
  match role {
    RelatedPriorWork => "related-prior-work"
    AuxiliaryCompatibility => "auxiliary-compatibility"
    NewContribution => "new-contribution"
    OutOfScope => "out-of-scope"
  }
}

///|
fn boundary_table_cell(text : String) -> String {
  text.replace_all(old="|", new="\\|").replace_all(old="\n", new="
") }