// 一致性语料:行级 diff 统计的单一事实来源。
//
// `moon test` 与 `cmd/verify` 共用同一份用例。

///|
priv struct Case {
  name : String
  old : String
  new : String
  added : Int
  removed : Int
  changed : Int
  unchanged : Int
}

///|
fn case(
  name : String,
  old : String,
  new : String,
  added : Int,
  removed : Int,
  changed : Int,
  unchanged : Int,
) -> Case {
  { name, old, new, added, removed, changed, unchanged, }
}

///|
fn make_cases() -> Array[Case] {
  [
    case("identical", "a\nb\nc\n", "a\nb\nc\n", 0, 0, 0, 3),
    case("replace-middle", "a\nb\nc\n", "a\nx\nc\n", 0, 0, 1, 2),
    case("add-end", "a\nb\n", "a\nb\nc\n", 1, 0, 0, 2),
    case("delete-middle", "a\nb\nc\n", "a\nc\n", 0, 1, 0, 2),
    case("crlf", "a\r\nb\r\nc\r\n", "a\nb\nc\n", 0, 0, 0, 3),
    case("empty-to-one", "", "a\n", 1, 0, 0, 0),
    case("one-to-empty", "a\n", "", 0, 1, 0, 0),
    case("empty-both", "", "", 0, 0, 0, 0),
    case("replace-two", "a\nb\nc\n", "a\nx\ny\n", 0, 0, 2, 1),
    case("add-two", "a\n", "a\nb\nc\n", 2, 0, 0, 1),
    case("delete-two", "a\nb\nc\n", "a\n", 0, 2, 0, 1),
    case("one-to-three", "a\nb\n", "a\nx\ny\nz\n", 2, 0, 1, 1),
    case("three-to-one", "a\nx\ny\nz\n", "a\nb\n", 0, 2, 1, 1),
    case("chinese-change", "你好\n世界\n", "你好\n月亮\n", 0, 0, 1, 1),
    case("blank-lines", "a\n\nb\n", "a\n\n\nb\n", 1, 0, 0, 3),
    case("trailing-newline-ignored", "a\n", "a", 0, 0, 0, 1),
  ]
}

///|
/// 一致性语料总条数。
pub fn conformance_case_count() -> Int {
  22
}

///|
fn check(failures : Array[String], name : String, condition : Bool) -> Unit {
  if !condition {
    failures.push(name)
  }
}

///|
/// 运行一致性语料,返回失败描述(空数组表示全部通过)。
pub fn run_conformance() -> Array[String] {
  let failures : Array[String] = []
  for item in make_cases() {
    let result = diff_text(item.old, item.new, DiffOptions::new())
    let stats = result.stats
    if stats.added != item.added ||
      stats.removed != item.removed ||
      stats.changed != item.changed ||
      stats.unchanged != item.unchanged {
      failures.push(
        "\{item.name}: expected +\{item.added} -\{item.removed} ~\{item.changed} =\{item.unchanged}, got +\{stats.added} -\{stats.removed} ~\{stats.changed} =\{stats.unchanged}",
      )
    }
  }
  let change = diff_text("a\nb\n", "a\nc\n", DiffOptions::new())
  check(failures, "patch: 头", change.patch.contains("@@ -1,2 +1,2 @@"))
  check(failures, "patch: 删除行", change.patch.contains("-b"))
  check(failures, "patch: 插入行", change.patch.contains("+c"))
  check(
    failures,
    "html: change 行",
    render_html(change, "old", "new").contains(""),
  )
  let long_old = StringBuilder()
  let long_new = StringBuilder()
  let mut i = 0
  while i < 2500 {
    long_old.write_string("a")
    long_new.write_string("b")
    i = i + 1
  }
  let long_result = diff_text(
    long_old.to_string() + "\n",
    long_new.to_string() + "\n",
    DiffOptions::new(),
  )
  let mut has_spans = false
  for row in long_result.rows {
    if row.old_spans.length() > 0 {
      has_spans = true
    }
  }
  check(failures, "long-line: 回退整行", !has_spans)
  let json = render_json(diff_text("a\n", "a\nb\n", DiffOptions::new()))
  check(failures, "json: 单边行 null", json.contains("\"oldNo\":null"))
  failures
}