#!/usr/bin/env bash
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
COMPILER="$ROOT_DIR/build/bin/compiler"
TMP_DIR="$ROOT_DIR/build/test_compiler"
TEST_DIRS=("$ROOT_DIR/test/test_case/functional" "$ROOT_DIR/test/test_case/performance")

if [[ ! -x "$COMPILER" ]]; then
  echo "未找到编译器: $COMPILER"
  echo "请先构建编译器，例如: mkdir -p build && cd build && cmake .. && make -j"
  exit 1
fi

mkdir -p "$TMP_DIR"

ir_total=0
ir_pass=0
result_total=0
result_pass=0

ir_failures=()
result_failures=()

function normalize_file() {
  sed 's/\r$//' "$1"
}

for test_dir in "${TEST_DIRS[@]}"; do
  if [[ ! -d "$test_dir" ]]; then
    echo "跳过不存在的测试目录: $test_dir"
    continue
  fi

  shopt -s nullglob
  for input in "$test_dir"/*.sy; do
    ir_total=$((ir_total+1))
    base=$(basename "$input")
    stem=${base%.sy}
    out_dir="$TMP_DIR/$(basename "$test_dir")"
    mkdir -p "$out_dir"
    ll_file="$out_dir/$stem.ll"
    stdout_file="$out_dir/$stem.stdout"
    expected_file="$test_dir/$stem.out"
    stdin_file="$test_dir/$stem.in"

    echo "[TEST] $input"
    
    # 编译并捕获所有输出
    compiler_status=0
    compiler_output=""
    compiler_output=$("$COMPILER" --emit-ir "$input" 2>&1) || compiler_status=$?
    
    # 临时文件存储原始输出
    raw_ll="$out_dir/$stem.raw.ll"
    printf '%s\n' "$compiler_output" > "$raw_ll"
    
    # 检查编译是否成功
    if [[ $compiler_status -ne 0 ]]; then
      echo "  [IR] 编译失败: 返回码 $compiler_status"
      ir_failures+=("$input: compiler failed ($compiler_status)")
      # 失败：保留原始输出（包含所有调试信息）
      cp "$raw_ll" "$ll_file"
      rm -f "$raw_ll"
      continue
    fi
    
    # 检查是否生成了有效的函数定义（在过滤后的内容中检查）
    # 先过滤一下看看是否有define
    filtered_content=$(sed -E '/^\[DEBUG\]|^SymbolTable::|^Check|^绑定|^保存|^dim_count:/d' "$raw_ll")
    if ! echo "$filtered_content" | grep -qE '^define '; then
      echo "  [IR] 失败: 未生成有效函数定义"
      ir_failures+=("$input: invalid IR output")
      # 失败：保留原始输出
      cp "$raw_ll" "$ll_file"
      rm -f "$raw_ll"
      continue
    fi
    
    # 编译成功：过滤掉所有调试输出，只保留IR
    # 过滤规则：
    # 1. 以 [DEBUG] 开头的行
    # 2. SymbolTable:: 开头的行
    # 3. CheckLValue: 开头的行
    # 4. 绑定变量: 开头的行
    # 5. dim_count: 开头的行
    # 6. 空行（可选）
    sed -E '/^(\[DEBUG|SymbolTable::|Check|绑定|保存|dim_)/d' "$raw_ll" > "$ll_file"
    
    # 可选：删除多余的空行
    sed -i '/^$/N;/\n$/D' "$ll_file"
    
    rm -f "$raw_ll"
    
    ir_pass=$((ir_pass+1))
    echo "  [IR] 生成成功 (IR已保存到: $ll_file)"
    
    # 运行测试
    if [[ -f "$expected_file" ]]; then
      result_total=$((result_total+1))
      
      # 运行LLVM IR
      run_status=0
      if [[ -f "$stdin_file" ]]; then
        lli "$ll_file" < "$stdin_file" > "$stdout_file" 2>&1 || run_status=$?
      else
        lli "$ll_file" > "$stdout_file" 2>&1 || run_status=$?
      fi
      
      # 读取预期返回值
      expected=$(normalize_file "$expected_file")
      
      # 比较返回值
      if [[ "$run_status" -eq "$expected" ]]; then
        result_pass=$((result_pass+1))
        echo "  [RUN] 返回值匹配: $run_status"
        # 成功：保留已清理的.ll文件，删除输出文件
        rm -f "$stdout_file"
      else
        echo "  [RUN] 返回值不匹配: got $run_status, expected $expected"
        result_failures+=("$input: exit code mismatch (got $run_status, expected $expected)")
        # 失败：.ll文件已经保留，输出文件也保留用于调试
      fi
    else
      echo "  [RUN] 未找到预期返回值文件 $expected_file，跳过结果验证"
    fi
  done
  shopt -u nullglob
done

# 输出统计
cat <<EOF
测试完成。
IR 生成: $ir_pass / $ir_total
结果匹配: $result_pass / $result_total
EOF

# 输出失败列表
if [[ ${#ir_failures[@]} -gt 0 ]]; then
  echo ""
  echo "IR 失败列表:"
  for item in "${ir_failures[@]}"; do
    echo "  $item"
  done
fi

if [[ ${#result_failures[@]} -gt 0 ]]; then
  echo ""
  echo "结果失败列表:"
  for item in "${result_failures[@]}"; do
    echo "  $item"
  done
  echo ""
  echo "失败的测试文件已保留在: $TMP_DIR"
  echo "可以查看对应的.ll文件进行调试"
fi