#!/bin/bash
# Nexus Pre-deploy Gate Check (v2 — 7 gates)
# 在部署前强制检查 7 道门控，任何一道不过则阻止部署
#
# 门控1: CHANGELOG  — docs/changelog/ 下必须有今天的 .md 文件（且行数≥10）
# 门控2: AUDIT     — docs/audit/ 下必须有今天的审计记录（且包含关键段落）
# 门控3: TEST      — tests/test_api.py 必须存在且全通过
# 门控4: LINT      — ruff check server/ 零错误
# 门控5: IMPORT    — python -c "import server.main" 成功
# 门控6: SECURITY  — bandit -r server/ 无 HIGH/MEDIUM
# 门控7: REVIEW    — 审计文件包含 Closure表+文件清单+DoD，且文件清单与 git diff 交叉验证
#
# 用法: bash deploy/pre_deploy_check.sh
# 退出码: 0=全部通过, 1=至少一道门未过
# 日志: deploy/gate_log.jsonl（每次检查自动追加记录）

set -euo pipefail

_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
_REPO_ROOT="$(cd "${_SCRIPT_DIR}/.." && pwd)"
DEPLOY_DIR="${NEXUS_DEPLOY_DIR:-${_REPO_ROOT}}"
TODAY=$(date +%Y-%m-%d)
TIMESTAMP=$(date -Iseconds)
GATES_PASSED=0
GATES_TOTAL=7
BLOCKED=0
GATE_RESULTS=""

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m' # No Color

# ── 日志函数 ──
gate_log() {
    local gate="$1" result="$2" detail="${3:-}"
    local entry="{\"ts\":\"${TIMESTAMP}\",\"date\":\"${TODAY}\",\"gate\":\"${gate}\",\"result\":\"${result}\",\"detail\":\"${detail}\"}"
    GATE_RESULTS="${GATE_RESULTS}${entry},"
    # 追加到 JSONL 日志文件
    echo "${entry}" >> "${DEPLOY_DIR}/deploy/gate_log.jsonl"
}

echo "========================================"
echo " Nexus Pre-deploy Gate Check (v2)"
echo " Date: ${TODAY}"
echo " Gates: ${GATES_TOTAL}"
echo "========================================"
echo ""

# ── Gate 1: Changelog (存在 + 行数≥10) ──
echo -n "Gate 1/7: Changelog ... "
CHANGELOG_DIR="${DEPLOY_DIR}/docs/changelog"
if ls "${CHANGELOG_DIR}/${TODAY}-"*.md 1>/dev/null 2>&1; then
    FILE=$(ls -t "${CHANGELOG_DIR}/${TODAY}-"*.md 2>/dev/null | head -1)
    LINES=$(wc -l < "${FILE}")
    if [ "${LINES}" -ge 10 ]; then
        echo -e "${GREEN}PASS${NC}"
        echo "  └─ Found: $(basename "${FILE}") (${LINES} lines)"
        GATES_PASSED=$((GATES_PASSED + 1))
        gate_log "changelog" "PASS" "$(basename "${FILE}") ${LINES}lines"
    else
        echo -e "${RED}BLOCK${NC}"
        echo "  └─ File exists but only ${LINES} lines (need ≥10)"
        echo "  └─ Changelog must have substance, not just a placeholder"
        BLOCKED=1
        gate_log "changelog" "BLOCK" "only ${LINES} lines"
    fi
else
    echo -e "${RED}BLOCK${NC}"
    echo "  └─ No changelog file found: ${CHANGELOG_DIR}/${TODAY}-*.md"
    echo "  └─ Create changelog before deploying"
    BLOCKED=1
    gate_log "changelog" "BLOCK" "file not found"
fi

# ── Gate 2: Audit (存在 + 关键段落) ──
echo -n "Gate 2/7: Audit ... "
AUDIT_DIR="${DEPLOY_DIR}/docs/audit"
if ls "${AUDIT_DIR}/${TODAY}-"*.md 1>/dev/null 2>&1; then
    FILE=$(ls -t "${AUDIT_DIR}/${TODAY}-"*.md 2>/dev/null | head -1)
    # 检查关键段落是否存在
    HAS_STEP3=$(grep -c "Step 3" "${FILE}" 2>/dev/null || echo 0)
    HAS_CLOSURE=$(grep -c "Closure" "${FILE}" 2>/dev/null || echo 0)
    HAS_DOD=$(grep -c "DoD" "${FILE}" 2>/dev/null || echo 0)
    if [ "${HAS_STEP3}" -ge 1 ] && [ "${HAS_CLOSURE}" -ge 1 ] && [ "${HAS_DOD}" -ge 1 ]; then
        echo -e "${GREEN}PASS${NC}"
        echo "  └─ Found: $(basename "${FILE}") (Step3✓ Closure✓ DoD✓)"
        GATES_PASSED=$((GATES_PASSED + 1))
        gate_log "audit" "PASS" "$(basename "${FILE}")"
    else
        echo -e "${RED}BLOCK${NC}"
        echo "  └─ Audit file exists but missing required sections:"
        [ "${HAS_STEP3}" -eq 0 ] && echo "  └─ Missing: Step 3 (规则扫描)"
        [ "${HAS_CLOSURE}" -eq 0 ] && echo "  └─ Missing: Closure表"
        [ "${HAS_DOD}" -eq 0 ] && echo "  └─ Missing: DoD (Definition of Done)"
        BLOCKED=1
        gate_log "audit" "BLOCK" "missing sections"
    fi
else
    echo -e "${RED}BLOCK${NC}"
    echo "  └─ No audit file found: ${AUDIT_DIR}/${TODAY}-*.md"
    echo "  └─ Run line-walk audit (8 steps) and save report before deploying"
    BLOCKED=1
    gate_log "audit" "BLOCK" "file not found"
fi

# ── Gate 3: Test (必须存在 + 必须通过) ──
echo -n "Gate 3/7: Test ... "
TEST_SCRIPT="${DEPLOY_DIR}/tests/test_api.py"
if [ ! -f "${TEST_SCRIPT}" ]; then
    echo -e "${RED}BLOCK${NC}"
    echo "  └─ Test script not found: ${TEST_SCRIPT}"
    echo "  └─ Tests are MANDATORY — cannot deploy without test coverage"
    BLOCKED=1
    gate_log "test" "BLOCK" "script not found"
else
    TEST_OUTPUT=$(cd "${DEPLOY_DIR}" && python3 "${TEST_SCRIPT}" 2>&1) || true
    FAIL_COUNT=$(echo "${TEST_OUTPUT}" | grep -cE "^\s+\[FAIL\]" 2>/dev/null || true)
    FAIL_COUNT=${FAIL_COUNT:-0}
    if [ "${FAIL_COUNT}" -gt 0 ] || echo "${TEST_OUTPUT}" | grep -qE "[0-9]+ test(s) failed"; then
        echo -e "${RED}BLOCK${NC}"
        echo "  └─ Tests failed. Output:"
        echo "${TEST_OUTPUT}" | head -20 | sed 's/^/  │ /'
        BLOCKED=1
        gate_log "test" "BLOCK" "tests failed"
    else
        echo -e "${GREEN}PASS${NC}"
        echo "  └─ All tests passed"
        GATES_PASSED=$((GATES_PASSED + 1))
        gate_log "test" "PASS" "all passed"
    fi
fi

# ── Gate 4: Lint (ruff — F only: undefined names, unused imports) ──
echo -n "Gate 4/7: Lint ... "
if command -v ruff &>/dev/null; then
    # 只检查 F (pyflakes) — 真正的BUG：未定义名称、未使用导入
    # S/B 问题由 Security 门扫描但不阻断部署
    LINT_OUTPUT=$(cd "${DEPLOY_DIR}" && ruff check server/ --select F 2>&1) || true
    LINT_COUNT=$(echo "${LINT_OUTPUT}" | grep -cE "^server/" 2>/dev/null || true)
    LINT_COUNT=$(echo "${LINT_COUNT}" | head -1 | tr -d '[:space:]')
    LINT_COUNT=${LINT_COUNT:-0}
    if [ "${LINT_COUNT}" -eq 0 ]; then
        echo -e "${GREEN}PASS${NC}"
        echo "  └─ ruff check: 0 violations"
        GATES_PASSED=$((GATES_PASSED + 1))
        gate_log "lint" "PASS" "0 violations"
    else
        echo -e "${RED}BLOCK${NC}"
        echo "  └─ ruff found ${LINT_COUNT} violations:"
        echo "${LINT_OUTPUT}" | head -15 | sed 's/^/  │ /'
        BLOCKED=1
        gate_log "lint" "BLOCK" "${LINT_COUNT} violations"
    fi
else
    echo -e "${YELLOW}SKIP${NC}"
    echo "  └─ ruff not installed (pip install ruff)"
    GATES_PASSED=$((GATES_PASSED + 1))
    gate_log "lint" "SKIP" "ruff not installed"
fi

# ── Gate 5: Import (python -c "import server.main") ──
echo -n "Gate 5/7: Import ... "
# 优先使用 venv python，回退到系统 python
VENV_PYTHON="${DEPLOY_DIR}/venv/bin/python3"
if [ -x "${VENV_PYTHON}" ]; then
    IMPORT_PYTHON="${VENV_PYTHON}"
else
    IMPORT_PYTHON="python3"
fi
IMPORT_OUTPUT=$(cd "${DEPLOY_DIR}" && "${IMPORT_PYTHON}" -c "import server.main" 2>&1) && IMPORT_OK=1 || IMPORT_OK=0
if [ "${IMPORT_OK}" -eq 1 ]; then
    echo -e "${GREEN}PASS${NC}"
    echo "  └─ server.main imports successfully"
    GATES_PASSED=$((GATES_PASSED + 1))
    gate_log "import" "PASS" "ok"
else
    # 检查是否有真正的导入错误（不是缺少运行时依赖如数据库）
    if echo "${IMPORT_OUTPUT}" | grep -qiE "ImportError|ModuleNotFoundError|SyntaxError"; then
        echo -e "${RED}BLOCK${NC}"
        echo "  └─ Import failed:"
        echo "${IMPORT_OUTPUT}" | head -5 | sed 's/^/  │ /'
        BLOCKED=1
        gate_log "import" "BLOCK" "import/syntax error"
    else
        # 运行时依赖缺失（如数据库连接）不算BLOCK，但警告
        echo -e "${YELLOW}WARN${NC}"
        echo "  └─ Import has runtime dependency issues (not code errors)"
        GATES_PASSED=$((GATES_PASSED + 1))
        gate_log "import" "WARN" "runtime deps only"
    fi
fi

# ── Gate 6: Security (bandit — HIGH only) ──
echo -n "Gate 6/7: Security ... "
if command -v bandit &>/dev/null; then
    # 只阻断 HIGH severity，MEDIUM/LOW 由 ruff S 规则覆盖
    SEC_OUTPUT=$(cd "${DEPLOY_DIR}" && bandit -r server/ -f txt -ll 2>&1) || true
    HIGH_COUNT=$(echo "${SEC_OUTPUT}" | grep -cE "Severity: High" 2>/dev/null || true)
    HIGH_COUNT=$(echo "${HIGH_COUNT}" | head -1 | tr -d '[:space:]')
    HIGH_COUNT=${HIGH_COUNT:-0}
    if [ "${HIGH_COUNT}" -eq 0 ]; then
        echo -e "${GREEN}PASS${NC}"
        echo "  └─ bandit: 0 HIGH findings"
        GATES_PASSED=$((GATES_PASSED + 1))
        gate_log "security" "PASS" "0 HIGH"
    else
        echo -e "${RED}BLOCK${NC}"
        echo "  └─ bandit found ${HIGH_COUNT} HIGH severity issues:"
        echo "${SEC_OUTPUT}" | grep -A3 "Severity: High" | head -15 | sed 's/^/  │ /'
        BLOCKED=1
        gate_log "security" "BLOCK" "${HIGH_COUNT} HIGH findings"
    fi
else
    echo -e "${YELLOW}SKIP${NC}"
    echo "  └─ bandit not installed (pip install bandit)"
    GATES_PASSED=$((GATES_PASSED + 1))
    gate_log "security" "SKIP" "bandit not installed"
fi

# ── Gate 7: Review (审计文件交叉验证) ──
echo -n "Gate 7/7: Review ... "
if ls "${AUDIT_DIR}/${TODAY}-"*.md 1>/dev/null 2>&1; then
    FILE=$(ls -t "${AUDIT_DIR}/${TODAY}-"*.md 2>/dev/null | head -1)
    # 检查审计文件中是否列出了实际改动的文件
    REVIEW_OK=1
    # 如果是git仓库，交叉验证改动文件
    if command -v git &>/dev/null && [ -d "${DEPLOY_DIR}/.git" ]; then
        CHANGED_FILES=$(cd "${DEPLOY_DIR}" && git diff --name-only HEAD~1 2>/dev/null || true)
        for CF in ${CHANGED_FILES}; do
            # 跳过非代码文件
            case "${CF}" in
                docs/*|CLAUDE.md|*.md) continue ;;
            esac
            # 检查审计文件是否提到了这个文件
            if ! grep -q "$(basename "${CF}")" "${FILE}" 2>/dev/null; then
                if [ "${REVIEW_OK}" -eq 1 ]; then
                    echo -e "${RED}BLOCK${NC}"
                    REVIEW_OK=0
                fi
                echo "  └─ Changed file not in audit: ${CF}"
            fi
        done
    fi
    if [ "${REVIEW_OK}" -eq 1 ]; then
        echo -e "${GREEN}PASS${NC}"
        echo "  └─ Audit covers all changed files"
        GATES_PASSED=$((GATES_PASSED + 1))
        gate_log "review" "PASS" "audit covers changes"
    else
        BLOCKED=1
        gate_log "review" "BLOCK" "audit missing changed files"
    fi
else
    echo -e "${RED}BLOCK${NC}"
    echo "  └─ No audit file to validate against"
    BLOCKED=1
    gate_log "review" "BLOCK" "no audit file"
fi

# ── Summary ──
echo ""
echo "========================================"
if [ "${BLOCKED}" -eq 0 ]; then
    echo -e " Result: ${GREEN}${GATES_PASSED}/${GATES_TOTAL} gates passed${NC} — Deploy allowed"
    echo "========================================"
    gate_log "summary" "PASS" "${GATES_PASSED}/${GATES_TOTAL}"
    exit 0
else
    echo -e " Result: ${RED}${GATES_PASSED}/${GATES_TOTAL} gates passed${NC} — Deploy BLOCKED"
    echo "========================================"
    gate_log "summary" "BLOCK" "${GATES_PASSED}/${GATES_TOTAL}"
    exit 1
fi
