#!/usr/bin/env bash
# 验证 Rust 版 patch 与系统/原版 patch 行为一致
# 用法:
#   ./scripts/verify_patch_compat.sh              # 仅测试 Rust 版
#   REFERENCE_PATCH=/usr/bin/patch ./scripts/...   # 与系统 patch 逐项对比
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
# 待测 patch：优先环境变量，否则用 cargo 构建产物
PATCH_BIN="${PATCH_BIN:-}"
REFERENCE_PATCH="${REFERENCE_PATCH:-}"

if [[ -z "$PATCH_BIN" ]]; then
  PATCH_BIN="$REPO_ROOT/target/release/kylin-patch"
  if [[ ! -x "$PATCH_BIN" ]]; then
    echo "Building release binary: cargo build --release"
    (cd "$REPO_ROOT" && cargo build --release)
  fi
fi
if [[ -n "$REFERENCE_PATCH" ]] && ! command -v "$REFERENCE_PATCH" &>/dev/null; then
  echo "Warning: REFERENCE_PATCH=$REFERENCE_PATCH not found, skipping comparison"
  REFERENCE_PATCH=""
fi

RUN_DIR=""
cleanup() {
  [[ -n "$RUN_DIR" ]] && rm -rf "$RUN_DIR"
}
trap cleanup EXIT

RUN_DIR="$(mktemp -d)"
PASS=0
FAIL=0

run_test() {
  local name="$1"
  shift
  local want_ret="${1:-0}"
  shift
  if (cd "$RUN_DIR" && "$PATCH_BIN" "$@"); then
    local got=0
  else
    local got=$?
  fi
  if [[ "$got" -eq "$want_ret" ]]; then
    echo "  PASS: $name"
    ((PASS++)) || true
    return 0
  else
    echo "  FAIL: $name (exit $got, expected $want_ret)"
    ((FAIL++)) || true
    return 1
  fi
}

# 与参考 patch 对比：相同输入下，对比打补丁后的文件内容是否一致
run_compare() {
  local name="$1"
  shift
  local ref_dir="$RUN_DIR/ref"
  local rust_dir="$RUN_DIR/rust"
  mkdir -p "$ref_dir" "$rust_dir"
  cp -a "$RUN_DIR"/orig/* "$ref_dir" 2>/dev/null || true
  cp -a "$RUN_DIR"/orig/* "$rust_dir" 2>/dev/null || true
  cp "$RUN_DIR"/compare.patch "$ref_dir" 2>/dev/null || true
  cp "$RUN_DIR"/compare.patch "$rust_dir" 2>/dev/null || true

  (cd "$ref_dir" && "$REFERENCE_PATCH" "$@" -i compare.patch 2>/dev/null) || true
  (cd "$rust_dir" && "$PATCH_BIN" "$@" -i compare.patch 2>/dev/null) || true

  if diff -rq "$ref_dir" "$rust_dir" >/dev/null 2>&1; then
    echo "  PASS: $name (matches reference)"
    ((PASS++)) || true
    return 0
  else
    echo "  FAIL: $name (differs from reference)"
    diff -r "$ref_dir" "$rust_dir" || true
    ((FAIL++)) || true
    return 1
  fi
}

echo "=== 1. Unified diff，单文件，stdin ==="
mkdir -p "$RUN_DIR/1"
echo -e "a\nb\nc" > "$RUN_DIR/1/foo"
cat > "$RUN_DIR/1/foo.patch" << 'PATCH'
--- foo
+++ foo
@@ -1,3 +1,4 @@
 a
 b
+bar
 c
PATCH
(cd "$RUN_DIR/1" && "$PATCH_BIN" -i foo.patch -p0) 2>/dev/null || true
if grep -q bar "$RUN_DIR/1/foo" 2>/dev/null; then
  echo "  PASS: unified -i patch -p0"
  ((PASS++)) || true
else
  echo "  FAIL: unified -i patch -p0"
  ((FAIL++)) || true
fi
echo "  1b. 补丁从 stdin 读入（无 -i）"
echo -e "x\ny\nz" > "$RUN_DIR/1/baz"
cat > "$RUN_DIR/1/baz.patch" << 'PATCH'
--- baz
+++ baz
@@ -1,3 +1,4 @@
 x
 y
+stdin_ok
 z
PATCH
(cd "$RUN_DIR/1" && "$PATCH_BIN" -p0 < baz.patch) 2>/dev/null || true
if grep -q stdin_ok "$RUN_DIR/1/baz" 2>/dev/null; then
  echo "  PASS: unified from stdin"
  ((PASS++)) || true
else
  echo "  FAIL: unified from stdin"
  ((FAIL++)) || true
fi

echo ""
echo "=== 2. Unified diff，-i 指定补丁文件，-p strip ==="
mkdir -p "$RUN_DIR/2/a/b"
echo -e "old\nline" > "$RUN_DIR/2/a/b/file"
cat > "$RUN_DIR/2/p.patch" << 'PATCH'
--- a/b/file
+++ a/b/file
@@ -1,2 +1,3 @@
 old
+new
 line
PATCH
cp "$RUN_DIR/2/a/b/file" "$RUN_DIR/2/a/b/file.bak"
(cd "$RUN_DIR/2" && "$PATCH_BIN" -i p.patch -p2) 2>/dev/null
if grep -q new "$RUN_DIR/2/a/b/file"; then
  echo "  PASS: strip -p2 (a/b/file -> file)"
  ((PASS++)) || true
else
  echo "  FAIL: strip -p2"
  ((FAIL++)) || true
fi

echo ""
echo "=== 3. 备份 -b，默认 .orig ==="
mkdir -p "$RUN_DIR/3"
echo "only" > "$RUN_DIR/3/f"
cat > "$RUN_DIR/3/p.patch" << 'PATCH'
--- f
+++ f
@@ -1 +1,2 @@
 only
+backup
PATCH
(cd "$RUN_DIR/3" && "$PATCH_BIN" -i p.patch -p0 -b) 2>/dev/null
if [[ -f "$RUN_DIR/3/f.orig" ]] && grep -q "only" "$RUN_DIR/3/f.orig" && grep -q backup "$RUN_DIR/3/f"; then
  echo "  PASS: backup -b (.orig)"
  ((PASS++)) || true
else
  echo "  FAIL: backup -b"
  ((FAIL++)) || true
fi

echo ""
echo "=== 4. 反向 -R ==="
mkdir -p "$RUN_DIR/4"
echo -e "1\n2\n3" > "$RUN_DIR/4/x"
cat > "$RUN_DIR/4/forward.patch" << 'PATCH'
--- x
+++ x
@@ -1,3 +1,4 @@
 1
 2
+mid
 3
PATCH
cp "$RUN_DIR/4/x" "$RUN_DIR/4/x.orig"
(cd "$RUN_DIR/4" && "$PATCH_BIN" -i forward.patch -p0) 2>/dev/null
(cd "$RUN_DIR/4" && "$PATCH_BIN" -i forward.patch -p0 -R) 2>/dev/null
if diff -q "$RUN_DIR/4/x" "$RUN_DIR/4/x.orig" >/dev/null 2>&1; then
  echo "  PASS: reverse -R (apply then reverse == original)"
  ((PASS++)) || true
else
  echo "  FAIL: reverse -R"
  ((FAIL++)) || true
fi

echo ""
echo "=== 5. --dry-run 不修改文件 ==="
mkdir -p "$RUN_DIR/5"
echo "x" > "$RUN_DIR/5/f"
cat > "$RUN_DIR/5/p.patch" << 'PATCH'
--- f
+++ f
@@ -1 +1,2 @@
 x
+y
PATCH
cp "$RUN_DIR/5/f" "$RUN_DIR/5/f.bak"
(cd "$RUN_DIR/5" && "$PATCH_BIN" -i p.patch -p0 --dry-run) 2>/dev/null
if diff -q "$RUN_DIR/5/f" "$RUN_DIR/5/f.bak" >/dev/null 2>&1; then
  echo "  PASS: --dry-run (file unchanged)"
  ((PASS++)) || true
else
  echo "  FAIL: --dry-run"
  ((FAIL++)) || true
fi

echo ""
echo "=== 6. Context diff -c ==="
mkdir -p "$RUN_DIR/6"
echo -e "A\nB\nC" > "$RUN_DIR/6/ctx"
cat > "$RUN_DIR/6/c.patch" << 'PATCH'
*** ctx
--- ctx
***************
*** 1,3 ****
  A
  B
  C
--- 1,4 ----
  A
  B
  + line
  C
PATCH
(cd "$RUN_DIR/6" && "$PATCH_BIN" -c -i c.patch -p0) 2>/dev/null
if grep -q "line" "$RUN_DIR/6/ctx" 2>/dev/null; then
  echo "  PASS: context diff -c"
  ((PASS++)) || true
else
  echo "  FAIL: context diff -c"
  ((FAIL++)) || true
fi

echo ""
echo "=== 7. -o 输出到指定文件 ==="
mkdir -p "$RUN_DIR/7"
echo "in" > "$RUN_DIR/7/src"
cat > "$RUN_DIR/7/p.patch" << 'PATCH'
--- src
+++ src
@@ -1 +1,2 @@
 in
+out
PATCH
(cd "$RUN_DIR/7" && "$PATCH_BIN" -i p.patch -p0 -o out) 2>/dev/null
if [[ -f "$RUN_DIR/7/out" ]] && grep -q out "$RUN_DIR/7/out" && ! grep -q out "$RUN_DIR/7/src" 2>/dev/null; then
  echo "  PASS: -o output file"
  ((PASS++)) || true
else
  echo "  FAIL: -o output file"
  ((FAIL++)) || true
fi

echo ""
echo "=== 8. -d 切换目录 ==="
mkdir -p "$RUN_DIR/8/sub"
echo "data" > "$RUN_DIR/8/sub/f"
cat > "$RUN_DIR/8/sub/p.patch" << 'PATCH'
--- f
+++ f
@@ -1 +1,2 @@
 data
+more
PATCH
(cd "$RUN_DIR/8" && "$PATCH_BIN" -d sub -i sub/p.patch -p0) 2>/dev/null
if grep -q more "$RUN_DIR/8/sub/f" 2>/dev/null; then
  echo "  PASS: -d directory"
  ((PASS++)) || true
else
  echo "  FAIL: -d directory"
  ((FAIL++)) || true
fi

echo ""
echo "=== 9. 多文件 unified（Git 风格路径）==="
mkdir -p "$RUN_DIR/9"
echo "f1" > "$RUN_DIR/9/one"
echo "f2" > "$RUN_DIR/9/two"
cat > "$RUN_DIR/9/multi.patch" << 'PATCH'
--- a/one
+++ b/one
@@ -1 +1,2 @@
 f1
+one_add
--- a/two
+++ b/two
@@ -1 +1,2 @@
 f2
+two_add
PATCH
(cd "$RUN_DIR/9" && "$PATCH_BIN" -i multi.patch -p1) 2>/dev/null
if grep -q one_add "$RUN_DIR/9/one" && grep -q two_add "$RUN_DIR/9/two"; then
  echo "  PASS: multi-file unified -p1"
  ((PASS++)) || true
else
  echo "  FAIL: multi-file unified -p1"
  ((FAIL++)) || true
fi

echo ""
echo "=== 10. 与参考 patch 对比（若已设置 REFERENCE_PATCH）==="
if [[ -n "$REFERENCE_PATCH" ]]; then
  mkdir -p "$RUN_DIR/orig"
  echo -e "line1\nline2\nline3" > "$RUN_DIR/orig/bar"
  cat > "$RUN_DIR/compare.patch" << 'PATCH'
--- bar
+++ bar
@@ -1,3 +1,4 @@
 line1
 line2
+inserted
 line3
PATCH
  run_compare "unified single file" -p0
else
  echo "  SKIP: set REFERENCE_PATCH to enable (e.g. REFERENCE_PATCH=/usr/bin/patch)"
fi

echo ""
echo "=========================================="
echo "Total: $PASS passed, $FAIL failed"
if [[ $FAIL -gt 0 ]]; then
  exit 1
fi
exit 0
