#!/bin/bash
# =============================================================================
# workbuddy / Claude 推送前 4 件套预检脚本
# =============================================================================
# 用途: 推送前自检 4 件套,发现 P0 漏洞立即拦截
#   1. 鉴权: 新增/修改端点是否带 Depends(get_current_*) 鉴权
#   2. 依赖: 新增 import 是否同步 requirements.txt / package.json
#   3. alembic: model schema 变化是否生成迁移脚本
#   4. 配置: nginx / docker / conf 改动是否完整
#
# 用法:
#   bash scripts/pre-commit-check.sh           # 检查 staged 变更
#   bash scripts/pre-commit-check.sh --staged  # 同上(默认)
#   bash scripts/pre-commit-check.sh --branch  # 检查当前分支相对 main 的全部变更
#   bash scripts/pre-commit-check.sh --strict  # 严格模式(任何 warn 也失败)
#   bash scripts/pre-commit-check.sh --json     # 输出 JSON 格式(给 workbuddy 解析)
#
# 退出码:
#   0 = 全过 / 仅 INFO
#   1 = 有 WARN(--strict 下)
#   2 = 有 ERROR(必须修)
# =============================================================================

set -e

# 颜色
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

PASS_COUNT=0
WARN_COUNT=0
FAIL_COUNT=0
WARN_LIST=()
FAIL_LIST=()

pass() { PASS_COUNT=$((PASS_COUNT+1)); echo -e "${GREEN}[PASS]${NC}  $1"; }
warn() { WARN_COUNT=$((WARN_COUNT+1)); WARN_LIST+=("$1"); echo -e "${YELLOW}[WARN]${NC}  $1"; }
fail() { FAIL_COUNT=$((FAIL_COUNT+1)); FAIL_LIST+=("$1"); echo -e "${RED}[FAIL]${NC}  $1"; }
info() { echo -e "${BLUE}[INFO]${NC}  $1"; }

# 参数
MODE="staged"
STRICT=false
JSON_OUT=false
for arg in "$@"; do
    case $arg in
        --staged) MODE="staged" ;;
        --branch) MODE="branch" ;;
        --strict) STRICT=true ;;
        --json) JSON_OUT=true ;;
        *) ;;
    esac
done

PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$PROJECT_ROOT"

# 收集变更文件
if [ "$MODE" = "staged" ]; then
    info "检查 staged 变更..."
    CHANGED=$(git diff --cached --name-only)
    BASE="staged"
elif [ "$MODE" = "branch" ]; then
    info "检查当前分支 vs main 的变更..."
    BASE_BRANCH="main"
    git rev-parse --verify "$BASE_BRANCH" >/dev/null 2>&1 || BASE_BRANCH="origin/main"
    git rev-parse --verify "$BASE_BRANCH" >/dev/null 2>&1 || {
        fail "找不到 main 分支,请先 git fetch"
        exit 2
    }
    CHANGED=$(git diff --name-only "$BASE_BRANCH"...HEAD)
fi

if [ -z "$CHANGED" ]; then
    info "无变更文件,跳过"
    exit 0
fi

info "变更文件 ($([ "$MODE" = "staged" ] && echo "staged" || echo "branch")):"
echo "$CHANGED" | sed 's/^/  /'

# =============================================================================
# 检查 1: 鉴权
# =============================================================================
info ""
info "── 检查 1/4: 鉴权 (Depends(get_current_*))"

check_auth() {
    local file="$1"
    # 只检查后端 api 路由文件
    case "$file" in
        backend/app/api/*.py|backend/app/api/**/*.py) ;;
        *) return 0 ;;
    esac
    # 跳过非路由文件
    case "$file" in
        *schemas*) return 0 ;;
        *_test*) return 0 ;;
    esac

    # 看 diff 是否新增/修改了路由(@router.*)
    local diff
    if [ "$MODE" = "staged" ]; then
        diff=$(git diff --cached "$file")
    else
        diff=$(git diff "$BASE_BRANCH"...HEAD -- "$file")
    fi
    # 有 router 装饰器改动?
    if ! echo "$diff" | grep -qE '^\+.*@router\.(get|post|put|delete|patch)'; then
        return 0  # 无路由变化,跳过
    fi

    # 看新增/修改的函数是否带 Depends
    local func_diff
    func_diff=$(echo "$diff" | grep -E '^\+.*(async )?def [a-z_]+\(')
    if echo "$func_diff" | grep -qE 'Depends\(.*get_current'; then
        pass "  $file: 新路由有 Depends 鉴权"
    elif echo "$func_diff" | grep -qE '@router\.(get|post|put|delete|patch)'; then
        # 新增路由但没 Depends → 极可能是 P0 漏洞
        local new_routes
        new_routes=$(echo "$func_diff" | grep -B 5 'def [a-z_]' | grep -E '^\+.*@router\.' | head -5)
        if [ -n "$new_routes" ]; then
            fail "  $file: 新增路由可能无鉴权: $(echo "$new_routes" | wc -l) 处"
        fi
    fi
}

for f in $CHANGED; do
    [ -f "$f" ] && check_auth "$f"
done

# =============================================================================
# 检查 2: 依赖
# =============================================================================
info ""
info "── 检查 2/4: 依赖 (requirements.txt / package.json)"

check_deps() {
    local file="$1"
    # 只检查 Python / JS 文件
    case "$file" in
        *.py)
            # 看 diff 是否新增 import
            local diff
            if [ "$MODE" = "staged" ]; then
                diff=$(git diff --cached "$file")
            else
                diff=$(git diff "$BASE_BRANCH"...HEAD -- "$file")
            fi
            local new_imports
            new_imports=$(echo "$diff" | grep -E '^\+.*^(from|import) ' | grep -vE '^\+\s*#' | head -10)
            if [ -z "$new_imports" ]; then return 0; fi

            # 提取第三方包(标准库除外)
            local third_party
            third_party=$(echo "$new_imports" | grep -E '^\+.*^(from|import) [a-z_]+' | \
                sed -E 's/^\+ *(from|import) ([a-z_][a-z0-9_]*).*/\2/' | \
                grep -vE '^(os|sys|re|json|time|datetime|typing|asyncio|pathlib|hashlib|hmac|secrets|base64|urllib|http|logging|functools|collections|itertools|contextlib|io|copy|enum|dataclasses|abc|math|random|string|subprocess|threading|multiprocessing|signal|socket|ssl|tempfile|shutil|glob|fnmatch|stat|argparse|getopt|unittest|traceback|warnings|pickle|csv|xml|html|email|zoneinfo|decimal|fractions|gcd)' | \
                sort -u)
            if [ -n "$third_party" ]; then
                # 检查 requirements.txt 是否已有
                local missing=""
                for pkg in $third_party; do
                    if ! grep -qiE "^${pkg}([=<>!~]|$)" backend/requirements.txt 2>/dev/null; then
                        missing+="$pkg "
                    fi
                done
                if [ -n "$missing" ]; then
                    fail "  $file: 新增第三方 import 但 requirements.txt 缺: $missing"
                else
                    pass "  $file: 新增 import 已在 requirements.txt"
                fi
            fi
            ;;
        *.ts|*.tsx|*.vue|*.js|*.jsx)
            # 看 diff 是否新增 import / require
            local diff
            if [ "$MODE" = "staged" ]; then
                diff=$(git diff --cached "$file")
            else
                diff=$(git diff "$BASE_BRANCH"...HEAD -- "$file")
            fi
            local new_imports
            new_imports=$(echo "$diff" | grep -E '^\+.*(import .* from |require\()' | head -10)
            if [ -z "$new_imports" ]; then return 0; fi

            # 找对应 package.json
            local pkg_json="package.json"
            case "$file" in
                frontend-admin/*) pkg_json="frontend-admin/package.json" ;;
                frontend-agent/*) pkg_json="frontend-agent/package.json" ;;
                frontend-h5/*) pkg_json="frontend-h5/package.json" ;;
                frontend-portal/*) pkg_json="frontend-portal/package.json" ;;
            esac
            if [ -f "$pkg_json" ]; then
                # 提取包名(简单粗暴,workbuddy 改的常规 npm 包)
                local new_pkgs
                new_pkgs=$(echo "$new_imports" | grep -oE "from ['\"](@?[a-z][a-z0-9_/.-]+)" | sed -E "s/from ['\"]//" | sort -u)
                if [ -n "$new_pkgs" ]; then
                    local missing=""
                    for pkg in $new_pkgs; do
                        if ! grep -qE "\"${pkg#@*/?}\"" "$pkg_json" 2>/dev/null; then
                            missing+="$pkg "
                        fi
                    done
                    if [ -n "$missing" ]; then
                        warn "  $file: 新增 import,需确认 $pkg_json 有: $missing"
                    fi
                fi
            fi
            ;;
    esac
}

for f in $CHANGED; do
    [ -f "$f" ] && check_deps "$f"
done

# =============================================================================
# 检查 3: alembic
# =============================================================================
info ""
info "── 检查 3/4: alembic 迁移"

check_alembic() {
    local file="$1"
    # model 改了 → 必须有 alembic 迁移
    case "$file" in
        backend/app/models/*.py)
            # 看 diff 是否改 schema(Column / type / nullable / default)
            local diff
            if [ "$MODE" = "staged" ]; then
                diff=$(git diff --cached "$file")
            else
                diff=$(git diff "$BASE_BRANCH"...HEAD -- "$file")
            fi
            if echo "$diff" | grep -qE '^\+.*Column\(|^\+.*Mapped\['; then
                # 找本次 commit/branch 是否新增 alembic 迁移
                local migrations
                if [ "$MODE" = "staged" ]; then
                    migrations=$(git diff --cached --name-only | grep "alembic/versions/.*\.py" || true)
                else
                    migrations=$(git diff --name-only "$BASE_BRANCH"...HEAD | grep "alembic/versions/.*\.py" || true)
                fi
                if [ -z "$migrations" ]; then
                    fail "  $file: model schema 变化但无 alembic 迁移"
                else
                    pass "  $file: model 改了,有 alembic 迁移: $migrations"
                fi
            fi
            ;;
        backend/alembic/versions/*.py)
            info "  $file: alembic 迁移新增"
            ;;
    esac
}

for f in $CHANGED; do
    [ -f "$f" ] && check_alembic "$f"
done

# =============================================================================
# 检查 4: 配置
# =============================================================================
info ""
info "── 检查 4/4: 配置 (nginx / docker / conf)"

check_config() {
    local file="$1"
    case "$file" in
        nginx.conf|deploy-server/nginx.conf|docker-compose.yml|docker-compose*.yml|.env.example)
            warn "  $file: 配置文件改动,确认 deploy.sh / docs/DEPLOY_NAS.md 同步更新"
            ;;
        backend/app/config.py)
            # 配置改了 → 看 .env.example 是否同步
            local diff
            if [ "$MODE" = "staged" ]; then
                diff=$(git diff --cached "$file")
            else
                diff=$(git diff "$BASE_BRANCH"...HEAD -- "$file")
            fi
            local new_settings
            new_settings=$(echo "$diff" | grep -E '^\+.*(os\.getenv|Field\(.*env=)' | head -5)
            if [ -n "$new_settings" ]; then
                warn "  $file: 新增配置项,确认 .env.example 同步"
            fi
            ;;
    esac
}

for f in $CHANGED; do
    [ -f "$f" ] && check_config "$f"
done

# =============================================================================
# 总结
# =============================================================================
info ""
info "── 总结"
echo "  PASS: $PASS_COUNT / WARN: $WARN_COUNT / FAIL: $FAIL_COUNT"

if [ $FAIL_COUNT -gt 0 ]; then
    echo ""
    echo "🛑 [FAIL] 列表:"
    for msg in "${FAIL_LIST[@]}"; do echo "  - $msg"; done
    echo ""
    echo "🛑 预检失败,请修 FAIL 项后再推送"
    exit 2
fi

if [ $WARN_COUNT -gt 0 ]; then
    echo ""
    echo "⚠️  [WARN] 列表:"
    for msg in "${WARN_LIST[@]}"; do echo "  - $msg"; done
    if [ "$STRICT" = true ]; then
        echo ""
        echo "🛑 严格模式下 WARN 也算失败"
        exit 1
    fi
    echo ""
    echo "⚠️  有 WARN 项,但允许推送(评审员关注)"
fi

echo ""
echo "✅ 预检通过,可以推送"
exit 0
