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

# CI 警示：本脚本在 Gitea Actions 多个 job 中调用（quality-gates::backend-integration、
# deploy-uat::post-deploy-regression）。它们共享全局测试容器 ffoa-test-postgres /
# ffoa-test-redis，并发执行会互相 docker rm 撞库。任何**新增 CI 调用方**必须先持有
# /tmp/ffoa-test-containers.lock 互斥锁（exec 200>...; flock -x -w 1800 200）。
# 详见 .gitea/workflows/quality-gates.yml::backend-integration 的实现样例
# 关联：.learnings/2026-05-09-ci-flock-shared-test-containers.md / 工单 #264
#
# 关于 OOM 与 batch 模式（issue #260）：
# - 旧路径 jest --runInBand 把全量 36 个 suite 串行塞 1 个 Node 进程，heap 累积到
#   ~1879 MB 撞 V8 默认 ~2 GB old-space 上限 OOM（PR #261 临时止血加 NODE_OPTIONS=
#   --max-old-space-size=4096，但 trend 不解决）。
# - 现在按模块 batch：12 个 module 目录各起一次 jest，进程退出 heap 自动回收。
#   模块内仍 --runInBand 串行（共享 test DB，cleanup 不能 race）。
# - 不传任何路径 → 自动 batch by module；传具体路径 → 单次 jest（向后兼容）。

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"

# shellcheck disable=SC1091
source "${ROOT_DIR}/testing/scripts/lib-test-db.sh"

# 确保测试数据库容器运行中
ensure_test_db

# 确保测试 Redis 容器运行中（IAM 对 Redis 是硬依赖，详见 backend/src/core/cache/redis/redis.service.ts）
ensure_test_redis

# 仅在首次（schema 不存在）时重建，避免每次清空模块种子数据
# 用 --force-reset 参数可以强制重建
if [[ "${1:-}" == "--force-reset" ]]; then
  shift
  echo "test-db: 强制重建 schema"
  reset_test_db_schema
elif ! docker exec ffoa-test-postgres psql -U ffws_test -d ffws_integration_test -c "SELECT 1 FROM platform_iam.users LIMIT 1" >/dev/null 2>&1; then
  echo "test-db: schema 未就绪，执行初始化"
  reset_test_db_schema
else
  echo "test-db: schema 已就绪，跳过重建（使用 --force-reset 可强制重建）"
fi

cd "${ROOT_DIR}/testing"

JEST_BIN="../backend/node_modules/.bin/jest"
JEST_CONFIG="config/jest-backend-integration.json"
INTEGRATION_ROOT="backend/integration"

run_jest_once() {
  node "${JEST_BIN}" --config "${JEST_CONFIG}" --forceExit --runInBand "$@"
}

# 单次 jest 透传仅在传"路径"时触发（参数不以 - 开头）。
# 之前的实现 [[ $# -gt 0 ]] 会把任何 jest 选项（如 --runInBand）当成"用户指定路径"，
# 绕过 batch by module → 单进程跑 36 suite 撞 V8 ~2GB 上限 OOM。
# 详见 .learnings/2026-05-11-deploy-uat-l1-oom-batch-bypass.md
HAS_PATH=false
for arg in "$@"; do
  [[ "$arg" != -* ]] && HAS_PATH=true && break
done
if $HAS_PATH; then
  run_jest_once "$@"
  exit $?
fi
# 走到这里：参数全是 jest 选项（无路径）→ 仍走 batch by module，选项透传给每个 module 的 jest。
# batch 内部强制 --runInBand（共享 test DB 不能并发），外层重复加 --runInBand 也只是冗余无害。

# 无参数 → batch by module
mapfile -t MODULES < <(find "${INTEGRATION_ROOT}" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | sort)

if [[ ${#MODULES[@]} -eq 0 ]]; then
  echo "❌ ${INTEGRATION_ROOT}/ 下没有发现任何模块目录" >&2
  exit 1
fi

echo ""
echo "=== batch by module（${#MODULES[@]} 个模块）==="
for m in "${MODULES[@]}"; do echo "  - ${m}"; done
echo ""

FAILED_MODULES=()
TOTAL_START=$SECONDS

for module in "${MODULES[@]}"; do
  echo ""
  echo "▶ [$(date +%H:%M:%S)] module: ${module}"
  MOD_START=$SECONDS
  # 故意不 set -e fail-fast：跑完所有模块再统一报失败，方便一次看到全部红项
  if run_jest_once "${INTEGRATION_ROOT}/${module}"; then
    echo "✓ ${module} 通过（$((SECONDS - MOD_START))s）"
  else
    EC=$?
    echo "✗ ${module} 失败（exit ${EC}，$((SECONDS - MOD_START))s）" >&2
    FAILED_MODULES+=("${module}")
  fi
done

echo ""
echo "=== batch 完成：总耗时 $((SECONDS - TOTAL_START))s ==="

if [[ ${#FAILED_MODULES[@]} -gt 0 ]]; then
  echo "❌ 失败模块（${#FAILED_MODULES[@]}/${#MODULES[@]}）：" >&2
  for m in "${FAILED_MODULES[@]}"; do echo "  - ${m}" >&2; done
  exit 1
fi

echo "✅ 全部 ${#MODULES[@]} 个模块通过"
