/// engine_eval.mbt —— 反馈收敛(R111 · Evaluator-Optimizer schema 蒸馏)。
/// 把自由文本反馈归一为四段式 Defects/Evidence/Fix/Acceptance 契约 + 确定性 verdict:
/// 无缺陷且 acceptance 非空 → pass(optimizer 可收敛);否则 fail + 缺段/证据/修复标注。
/// 纯计算只读不写库(决策建议,执行权在调用方;与 plan_revise 反馈修订互补)。

///|
/// 列表项正文(行已 trim):`- ` / `* ` / `+ ` 或有序标记 `1. ` / `1) `。
/// 非列表行返回 None——`## Defects` 下写「(无)」这类自由文本仍按零缺陷处理。
fn list_item_text(line : String) -> String? {
  let n = line.length()
  if n >= 2 {
    let c0 = line[0]
    if (c0 == '-' || c0 == '*' || c0 == '+') && line[1] == ' ' {
      return Some(line[2:n].to_owned())
    }
  }
  let mut i : Int32 = 0
  while i < n && line[i] >= '0' && line[i] <= '9' {
    i = i + 1
  }
  if i > 0 &&
    i + 1 < n &&
    (line[i] == '.' || line[i] == ')') &&
    line[i + 1] == ' ' {
    return Some(line[i + 2:n].to_owned())
  }
  None
}

///|
/// 从反馈文本解析某 `## 段名` 下的列表项(遇到下一个 `## ` 标题即结束)。
/// R135:只认 `- ` 会把 `* ` / `+ ` / 有序列表写下的缺陷静默吞成「零缺陷 → pass」。
fn parse_section(text : String, header : String) -> Array[String] {
  let out : Array[String] = []
  let mut in_section = false
  for line in text.split("\n") {
    let t = line.trim()
    if t.has_prefix("## ") {
      in_section = t == "## " + header
    } else if in_section {
      match list_item_text(t.to_owned()) {
        Some(item) => out.push(item)
        None => ()
      }
    }
  }
  out
}

///|
/// 反馈文本中是否存在某 `## 段名` 标题(段"在场"判定,与条目数无关)。
fn section_present(text : String, header : String) -> Bool {
  for line in text.split("\n") {
    if line.trim() == "## " + header {
      return true
    }
  }
  false
}

///|
/// 反馈收敛:归一为四段式 schema + verdict(E/O 蒸馏:生成→评估→定向修订→收敛)。
/// 返回 { ok, task_id, schema:{defects,evidence,fix,acceptance}, counts, verdict, flags, note }。
pub fn FistEngine::eval_feedback(
  _self : FistEngine,
  feedback : String,
  task_id~ : String = "",
) -> Json {
  let defects = parse_section(feedback, "Defects")
  let evidence = parse_section(feedback, "Evidence")
  let fix = parse_section(feedback, "Fix")
  let acceptance = parse_section(feedback, "Acceptance")
  let d = defects.length()
  let e = evidence.length()
  let f = fix.length()
  let a = acceptance.length()
  let flags : Array[String] = []
  if d > 0 && e == 0 {
    flags.push("evidence_missing")
  }
  if d > 0 && f == 0 {
    flags.push("fix_missing")
  }
  let missing : Array[String] = []
  let has_defects = section_present(feedback, "Defects")
  if !has_defects {
    missing.push("Defects")
  }
  let has_evidence = section_present(feedback, "Evidence")
  if !has_evidence {
    missing.push("Evidence")
  }
  let has_fix = section_present(feedback, "Fix")
  if !has_fix {
    missing.push("Fix")
  }
  let has_acceptance = section_present(feedback, "Acceptance")
  if !has_acceptance {
    missing.push("Acceptance")
  }
  if missing.length() > 0 {
    flags.push("section_missing:" + missing.join(","))
  }
  let verdict = if d == 0 && a > 0 { "pass" } else { "fail" }
  let (sc_d, sc_e, sc_f, sc_a) = (
    d.to_double(),
    e.to_double(),
    f.to_double(),
    a.to_double(),
  )
  Json::object({
    "ok": Json::boolean(true),
    "task_id": Json::string(task_id),
    "schema": Json::object({
      "defects": Json::array(defects.map(Json::string)),
      "evidence": Json::array(evidence.map(Json::string)),
      "fix": Json::array(fix.map(Json::string)),
      "acceptance": Json::array(acceptance.map(Json::string)),
    }),
    "counts": Json::object({
      "defects": Json::number(sc_d),
      "evidence": Json::number(sc_e),
      "fix": Json::number(sc_f),
      "acceptance": Json::number(sc_a),
    }),
    "verdict": Json::string(verdict),
    "flags": Json::array(flags.map(Json::string)),
    "note": Json::string(
      "反馈收敛(Evaluator-Optimizer schema 蒸馏):feedback 归一为 Defects/Evidence/Fix/Acceptance 四段式——无缺陷且 acceptance 非空 → pass 可收敛;否则 fail 定向回灌(Defects→Fix→再 eval→pass 闭环,硬上限防死循环)。",
    ),
  })
}