#!/bin/bash
#
# API 端到端冒烟测试
# 用途：部署后快速验证核心功能是否正常
# 支持：staging / production 多环境
#
# 用法：
#   BASE_URL=https://staging-api.xiaoxiajianji.com ./api_smoke_test.sh
#   BASE_URL=https://saas-api.xiaoxiajianji.com TEST_USER=prod_e2e TEST_PASSWORD=xxx ./api_smoke_test.sh
#   # 只跑指定模块
#   MODULES="auth,edit-plans" ./api_smoke_test.sh
#
# 环境变量：
#   BASE_URL          API 地址，必填
#   TEST_USER         测试用户名，默认自动生成
#   TEST_PASSWORD     测试密码，默认 Test123456!
#   TEST_EMAIL        测试邮箱，默认自动生成
#   EXISTING_TOKEN    使用已有 token（跳过注册/登录，生产环境推荐）
#   MODULES           指定模块，逗号分隔，默认全部
#   CLEANUP_ENABLED   是否清理测试数据，1=是 0=否，默认 1
#

set -euo pipefail

# ===== 环境预设 (SMOKE_ENV) =====
# 支持 SMOKE_ENV=production / staging 快捷预设
SMOKE_ENV="${SMOKE_ENV:-}"
if [ "$SMOKE_ENV" = "production" ]; then
    # 生产环境预设：安全优先，默认只读
    BASE_URL="${BASE_URL:-https://api.xiaoxiajianji.com}"
    WEB_URL="${WEB_URL:-https://saas.xiaoxiajianji.com}"
    # 如果没有提供 EXISTING_TOKEN，默认只跑不需要鉴权的模块（只读）
    if [ -z "${EXISTING_TOKEN:-}" ]; then
        MODULES="${MODULES:-health,nginx}"
    else
        # 有 token 时跑只读安全模块
        MODULES="${MODULES:-health,assets,generation,subscription,nginx}"
    fi
    CLEANUP_ENABLED="${CLEANUP_ENABLED:-0}"
    PRODUCTION_MODE=1
elif [ "$SMOKE_ENV" = "staging" ]; then
    BASE_URL="${BASE_URL:-https://staging-api.xiaoxiajianji.com}"
    WEB_URL="${WEB_URL:-https://staging.xiaoxiajianji.com}"
    CLEANUP_ENABLED="${CLEANUP_ENABLED:-1}"
    PRODUCTION_MODE=0
else
    PRODUCTION_MODE=0
fi

# ===== 配置 =====
BASE_URL="${BASE_URL:-}"
TEST_USER="${TEST_USER:-e2e_$(date +%s)}"
TEST_PASSWORD="${TEST_PASSWORD:-Test123456!}"
TEST_EMAIL="${TEST_EMAIL:-${TEST_USER}@test.com}"
EXISTING_TOKEN="${EXISTING_TOKEN:-}"
MODULES="${MODULES:-all}"
CLEANUP_ENABLED="${CLEANUP_ENABLED:-1}"
CURL_TIMEOUT=30
CURL_CONNECT_TIMEOUT=15
CURL_INSECURE="${CURL_INSECURE:-0}"
PERF_CHECK_ENABLED="${PERF_CHECK_ENABLED:-1}"  # 是否启用响应时间检查
PERF_WARN_THRESHOLD_MS="${PERF_WARN_THRESHOLD_MS:-3000}"  # 响应时间警告阈值（毫秒）
PERF_FAIL_THRESHOLD_MS="${PERF_FAIL_THRESHOLD_MS:-10000}"  # 响应时间失败阈值（毫秒）

# 证书不安全的环境（如staging）可设 CURL_INSECURE=1 跳过校验
if [ "$CURL_INSECURE" = "1" ]; then
    curl() { command curl -k "$@"; }
fi

if [ -z "$BASE_URL" ]; then
    echo "❌ 错误：BASE_URL 环境变量未设置"
    echo "   用法: BASE_URL=https://api.example.com $0"
    exit 1
fi

# 去掉末尾的斜杠
BASE_URL="${BASE_URL%/}"

# ===== 全局变量 =====
PASSED=0
FAILED=0
FAIL_LIST=""
TOKEN=""
AUTH_HEADER=""
TEST_USER_ID=""

# 测试数据追踪（用于清理）
CREATED_PLANS=()
CREATED_TEMPLATES=()
CREATED_PROJECTS=()

# ===== 工具函数 =====
# 记录并检查响应时间
perf_check() {
    local name="$1"
    local elapsed_ms="$2"
    
    if [ "$PERF_CHECK_ENABLED" != "1" ]; then
        return 0
    fi
    
    if [ "$elapsed_ms" -ge "$PERF_FAIL_THRESHOLD_MS" ]; then
        fail "$name 响应时间" "${elapsed_ms}ms > ${PERF_FAIL_THRESHOLD_MS}ms（严重超标）"
        return 1
    elif [ "$elapsed_ms" -ge "$PERF_WARN_THRESHOLD_MS" ]; then
        echo "⚠️  $name 响应时间: ${elapsed_ms}ms（超过警告阈值 ${PERF_WARN_THRESHOLD_MS}ms）"
        return 0
    fi
    return 0
}

# 带计时的 curl 请求
curl_timed() {
    local output_file=$(mktemp)
    local start_time=$(date +%s%N)
    curl -s -o "$output_file" -w "%{http_code}" "$@"
    local code=$?
    local end_time=$(date +%s%N)
    local elapsed_ms=$(( (end_time - start_time) / 1000000 ))
    cat "$output_file"
    rm -f "$output_file"
    # 通过 stderr 返回耗时（调用方需重定向）
    echo "$elapsed_ms" >&2
    return $code
}

pass() {
    echo "✅ $1"
    PASSED=$((PASSED + 1))
}

fail() {
    echo "❌ $1"
    [ -n "${2:-}" ] && echo "   $2"
    FAILED=$((FAILED + 1))
    FAIL_LIST="$FAIL_LIST
  ❌ $1${2:+" - $2"}"
}

info() {
    echo "ℹ️  $1"
}

section() {
    echo ""
    echo "============================================================"
    echo "$1"
    echo "============================================================"
}

# 检查模块是否应该运行
should_run() {
    local module="$1"
    if [ "$MODULES" = "all" ]; then
        return 0
    fi
    echo ",$MODULES," | grep -q ",$module,"
}

# 安全的 JSON 字段提取
json_get() {
    local json="$1"
    local key="$2"
    echo "$json" | python3 -c "
import sys, json
try:
    d = json.load(sys.stdin)
    # 支持嵌套 key，用 . 分隔
    keys = '$key'.split('.')
    val = d
    for k in keys:
        if isinstance(val, dict):
            val = val.get(k, '')
        elif isinstance(val, list) and k.isdigit():
            idx = int(k)
            val = val[idx] if idx < len(val) else ''
        else:
            val = ''
            break
    if val is None:
        print('')
    elif isinstance(val, (dict, list)):
        print(json.dumps(val, ensure_ascii=False))
    else:
        print(str(val))
except:
    print('')
" 2>/dev/null
}

# ===== 认证 =====
setup_auth() {
    section "0. 认证准备"

    if [ -n "$EXISTING_TOKEN" ]; then
        TOKEN="$EXISTING_TOKEN"
        AUTH_HEADER="Authorization: Bearer $TOKEN"
        info "使用已有 TOKEN"

        # 验证 token 有效
        local code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 \
            -H "$AUTH_HEADER" "$BASE_URL/api/v1/auth/me")
        if [ "$code" = "200" ]; then
            pass "Token 验证通过"
        else
            fail "Token 验证失败" "HTTP $code"
            return 1
        fi
        return 0
    fi

    # 注册新用户
    local resp=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/api/v1/auth/register" \
        -H "Content-Type: application/json" \
        -d "{\"username\":\"$TEST_USER\",\"email\":\"$TEST_EMAIL\",\"password\":\"$TEST_PASSWORD\",\"nickname\":\"E2E测试\"}" \
        --max-time $CURL_TIMEOUT)
    local code=$(echo "$resp" | tail -1)
    local body=$(echo "$resp" | sed '$d')

    if [ "$code" = "200" ] || [ "$code" = "201" ]; then
        pass "用户注册成功"
        TEST_USER_ID=$(json_get "$body" "user_id")
    elif [ "$code" = "400" ] || [ "$code" = "409" ] || [ "$code" = "422" ]; then
        info "用户已存在，尝试登录"
    else
        fail "用户注册" "HTTP $code, body: ${body:0:200}"
        return 1
    fi

    # 登录
    resp=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/api/v1/auth/login" \
        -H "Content-Type: application/json" \
        -d "{\"email\":\"$TEST_EMAIL\",\"password\":\"$TEST_PASSWORD\"}" \
        --max-time $CURL_TIMEOUT)
    code=$(echo "$resp" | tail -1)
    body=$(echo "$resp" | sed '$d')

    if [ "$code" = "200" ]; then
        pass "用户登录成功"
        TOKEN=$(json_get "$body" "access_token")
        AUTH_HEADER="Authorization: Bearer $TOKEN"
        TEST_USER_ID=$(json_get "$body" "user_id")
    else
        fail "用户登录" "HTTP $code, body: ${body:0:200}"
        return 1
    fi

    # 验证
    code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 \
        -H "$AUTH_HEADER" "$BASE_URL/api/v1/auth/me")
    if [ "$code" = "200" ]; then
        pass "获取当前用户信息"
    else
        fail "获取当前用户信息" "HTTP $code"
    fi

    return 0
}

# ===== 模块：基础健康检查 =====
test_health() {
    should_run "health" || return 0
    section "1. 基础健康检查"
    
    if [ "$PERF_CHECK_ENABLED" = "1" ]; then
        info "响应时间检查已启用: 警告=${PERF_WARN_THRESHOLD_MS}ms, 失败=${PERF_FAIL_THRESHOLD_MS}ms"
    fi

    local code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$BASE_URL/health")
    [ "$code" = "200" ] && pass "健康检查 /health" || fail "健康检查" "HTTP $code"

    code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$BASE_URL/docs")
    if [ "$code" = "200" ]; then
        pass "API Docs 可访问"
    elif [ "$code" = "404" ] || [ "$code" = "403" ] || [ "$code" = "401" ]; then
        pass "API Docs（生产环境已禁用，HTTP $code，安全策略正常）"
    else
        fail "API Docs" "HTTP $code"
    fi

    code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$BASE_URL/api/v1/assets")
    if [ "$code" = "401" ] || [ "$code" = "403" ]; then
        pass "未授权访问返回 401/403（鉴权正常）"
    else
        fail "未授权鉴权" "HTTP $code（应为401/403）"
    fi
}

# ===== 模块：剪辑计划 =====
test_edit_plans() {
    should_run "edit-plans" || return 0
    [ -z "$TOKEN" ] && return 0

    section "2. 剪辑计划 (edit-plans)"

    local template_id=""
    local plan_id=""

    # 列表
    local resp=$(curl -s -w "\n%{http_code}" -H "$AUTH_HEADER" \
        "$BASE_URL/api/v1/edit-plans" --max-time $CURL_TIMEOUT)
    local code=$(echo "$resp" | tail -1)
    [ "$code" = "200" ] && pass "剪辑计划列表" || fail "剪辑计划列表" "HTTP $code"

    # 获取一个模板ID
    resp=$(curl -s -w "\n%{http_code}" -H "$AUTH_HEADER" \
        "$BASE_URL/api/v1/templates" --max-time $CURL_TIMEOUT)
    local tcode=$(echo "$resp" | tail -1)
    local tbody=$(echo "$resp" | sed '$d')

    if [ "$tcode" = "200" ]; then
        template_id=$(echo "$tbody" | python3 -c "
import sys, json
d = json.load(sys.stdin)
items = d.get('items', []) or d.get('data', []) or []
print(items[0].get('id', '') if items else '')
" 2>/dev/null)
    fi

    if [ -z "$template_id" ]; then
        # 创建一个模板
        resp=$(curl -s -w "\n%{http_code}" -X POST \
            -H "$AUTH_HEADER" -H "Content-Type: application/json" \
            -d '{"name":"E2E测试模板","mode":"voice_over","category":"vlog","config":{}}' \
            "$BASE_URL/api/v1/templates" --max-time $CURL_TIMEOUT)
        tcode=$(echo "$resp" | tail -1)
        tbody=$(echo "$resp" | sed '$d')
        if [ "$tcode" = "200" ] || [ "$tcode" = "201" ]; then
            template_id=$(json_get "$tbody" "id")
            [ -z "$template_id" ] && template_id=$(json_get "$tbody" "data.id")
            pass "创建测试模板（用于计划依赖）"
            CREATED_TEMPLATES+=("$template_id")
        else
            fail "创建测试模板" "HTTP $tcode"
            return
        fi
    else
        pass "获取已有模板ID"
    fi

    # 创建 - 完整参数
    resp=$(curl -s -w "\n%{http_code}" -X POST \
        -H "$AUTH_HEADER" -H "Content-Type: application/json" \
        -d "{\"name\":\"E2E完整测试\",\"template_id\":\"$template_id\",\"description\":\"自动化测试\",\"config\":{\"cover\":{\"type\":\"ai\",\"image_url\":\"\",\"position\":0},\"title\":{\"text\":\"测试标题\",\"font_size\":24,\"color\":\"#ffffff\",\"position\":\"top\",\"ai_enabled\":true},\"subtitle\":{\"enabled\":true,\"font_size\":14,\"color\":\"#ffffff\",\"position\":\"bottom\",\"style\":\"default\"},\"bgm\":{\"enabled\":true,\"type\":\"auto\",\"volume\":0.5}}}" \
        "$BASE_URL/api/v1/edit-plans" --max-time $CURL_TIMEOUT)
    code=$(echo "$resp" | tail -1)
    local body=$(echo "$resp" | sed '$d')

    if [ "$code" = "200" ] || [ "$code" = "201" ]; then
        pass "创建剪辑计划（完整config）"
        plan_id=$(json_get "$body" "id")
        [ -z "$plan_id" ] && plan_id=$(json_get "$body" "data.id")
        CREATED_PLANS+=("$plan_id")

        # 验证 config 结构
        local has_cover=$(echo "$body" | grep -c '"cover"' || true)
        local has_title=$(echo "$body" | grep -c '"title"' || true)
        local has_subtitle=$(echo "$body" | grep -c '"subtitle"' || true)
        local has_bgm=$(echo "$body" | grep -c '"bgm"' || true)
        if [ "$has_cover" -gt 0 ] && [ "$has_title" -gt 0 ] && [ "$has_subtitle" -gt 0 ] && [ "$has_bgm" -gt 0 ]; then
            pass "config 结构标准化（cover/title/subtitle/bgm 齐全）"
        else
            fail "config 结构标准化" "cover=$has_cover title=$has_title subtitle=$has_subtitle bgm=$has_bgm"
        fi
    else
        fail "创建剪辑计划（完整config）" "HTTP $code, body: ${body:0:200}"
    fi

    # 创建 - 最小参数
    resp=$(curl -s -w "\n%{http_code}" -X POST \
        -H "$AUTH_HEADER" -H "Content-Type: application/json" \
        -d "{\"name\":\"E2E最小测试\",\"template_id\":\"$template_id\"}" \
        "$BASE_URL/api/v1/edit-plans" --max-time $CURL_TIMEOUT)
    code=$(echo "$resp" | tail -1)
    body=$(echo "$resp" | sed '$d')
    if [ "$code" = "200" ] || [ "$code" = "201" ]; then
        pass "创建剪辑计划（最小参数，自动补默认config）"
        local has_default=$(echo "$body" | python3 -c "
import sys, json
d = json.load(sys.stdin)
data = d.get('data', d)
config = data.get('config', {})
if isinstance(config, dict) and 'cover' in config and 'title' in config:
    print('yes')
else:
    print('no')
" 2>/dev/null)
        [ "$has_default" = "yes" ] && pass "最小参数创建时自动填充默认config" || fail "自动填充默认config" "未填充"
    else
        fail "创建剪辑计划（最小参数）" "HTTP $code"
    fi

    # 详情
    if [ -n "$plan_id" ]; then
        code=$(curl -s -o /dev/null -w "%{http_code}" \
            -H "$AUTH_HEADER" "$BASE_URL/api/v1/edit-plans/$plan_id" --max-time $CURL_TIMEOUT)
        [ "$code" = "200" ] && pass "获取剪辑计划详情" || fail "获取剪辑计划详情" "HTTP $code"
    fi

    # 更新
    if [ -n "$plan_id" ]; then
        code=$(curl -s -o /dev/null -w "%{http_code}" -X PUT \
            -H "$AUTH_HEADER" -H "Content-Type: application/json" \
            -d '{"name":"E2E测试-已更新","config":{"title":{"text":"新标题","ai_enabled":false}}}' \
            "$BASE_URL/api/v1/edit-plans/$plan_id" --max-time $CURL_TIMEOUT)
        [ "$code" = "200" ] && pass "更新剪辑计划" || fail "更新剪辑计划" "HTTP $code"
    fi

    # AI 推荐（可能因无素材/服务未配置返回500/503，接口存在即可）
    if [ -n "$plan_id" ]; then
        code=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
            -H "$AUTH_HEADER" -H "Content-Type: application/json" \
            -d '{"style":"vlog"}' \
            "$BASE_URL/api/v1/edit-plans/$plan_id/ai-recommend" --max-time 60)
        if [ "$code" = "200" ] || [ "$code" = "202" ]; then
            pass "AI 推荐片段"
        elif [ "$code" = "500" ] || [ "$code" = "503" ]; then
            pass "AI 推荐片段（HTTP $code，环境限制，接口存在）"
        else
            fail "AI 推荐片段" "HTTP $code"
        fi
    fi

    # AI 封面生成
    if [ -n "$plan_id" ]; then
        code=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
            -H "$AUTH_HEADER" -H "Content-Type: application/json" \
            -d '{"mode":"ai_select"}' \
            "$BASE_URL/api/v1/edit-plans/$plan_id/generate-cover" --max-time 60)
        if [ "$code" = "200" ] || [ "$code" = "202" ]; then
            pass "AI 封面生成"
        elif [ "$code" = "500" ] || [ "$code" = "503" ]; then
            pass "AI 封面生成（HTTP $code，环境限制，接口存在）"
        else
            fail "AI 封面生成" "HTTP $code"
        fi
    fi

    # 关联 generations
    if [ -n "$plan_id" ]; then
        code=$(curl -s -o /dev/null -w "%{http_code}" \
            -H "$AUTH_HEADER" "$BASE_URL/api/v1/edit-plans/$plan_id/generations" --max-time $CURL_TIMEOUT)
        [ "$code" = "200" ] && pass "剪辑计划关联生成历史" || fail "剪辑计划关联生成历史" "HTTP $code"
    fi

    # 时间线
    if [ -n "$plan_id" ]; then
        code=$(curl -s -o /dev/null -w "%{http_code}" \
            -H "$AUTH_HEADER" "$BASE_URL/api/v1/edit-plans/$plan_id/timeline" --max-time $CURL_TIMEOUT)
        [ "$code" = "200" ] && pass "剪辑计划时间线" || fail "剪辑计划时间线" "HTTP $code"
    fi
}

# ===== 模块：剪辑模板 =====
test_templates() {
    should_run "templates" || return 0
    [ -z "$TOKEN" ] && return 0

    section "3. 剪辑模板 (templates)"

    local tpl_id=""

    # 列表
    local code=$(curl -s -o /dev/null -w "%{http_code}" -H "$AUTH_HEADER" \
        "$BASE_URL/api/v1/templates" --max-time $CURL_TIMEOUT)
    [ "$code" = "200" ] && pass "模板列表" || fail "模板列表" "HTTP $code"

    # 分类
    code=$(curl -s -o /dev/null -w "%{http_code}" -H "$AUTH_HEADER" \
        "$BASE_URL/api/v1/templates/categories/list" --max-time $CURL_TIMEOUT)
    [ "$code" = "200" ] && pass "模板分类列表" || fail "模板分类列表" "HTTP $code"

    # 创建
    local resp=$(curl -s -w "\n%{http_code}" -X POST \
        -H "$AUTH_HEADER" -H "Content-Type: application/json" \
        -d '{"name":"E2E测试模板-完整","mode":"voice_over","description":"测试模板","category":"vlog","config":{"cover":{"type":"ai"},"title":{"ai_enabled":true},"subtitle":{"enabled":true},"bgm":{"enabled":true}}}' \
        "$BASE_URL/api/v1/templates" --max-time $CURL_TIMEOUT)
    code=$(echo "$resp" | tail -1)
    local body=$(echo "$resp" | sed '$d')

    if [ "$code" = "200" ] || [ "$code" = "201" ]; then
        pass "创建模板"
        tpl_id=$(json_get "$body" "id")
        [ -z "$tpl_id" ] && tpl_id=$(json_get "$body" "data.id")
        CREATED_TEMPLATES+=("$tpl_id")
    else
        fail "创建模板" "HTTP $code, body: ${body:0:200}"
    fi

    # 详情
    if [ -n "$tpl_id" ]; then
        code=$(curl -s -o /dev/null -w "%{http_code}" \
            -H "$AUTH_HEADER" "$BASE_URL/api/v1/templates/$tpl_id" --max-time $CURL_TIMEOUT)
        [ "$code" = "200" ] && pass "模板详情" || fail "模板详情" "HTTP $code"
    fi

    # toggle-favorite
    if [ -n "$tpl_id" ]; then
        code=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
            -H "$AUTH_HEADER" \
            "$BASE_URL/api/v1/templates/$tpl_id/toggle-favorite" --max-time $CURL_TIMEOUT)
        [ "$code" = "200" ] && pass "模板收藏切换 (toggle-favorite)" || fail "模板收藏切换" "HTTP $code"
    fi

    # validate
    if [ -n "$tpl_id" ]; then
        code=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
            -H "$AUTH_HEADER" -H "Content-Type: application/json" \
            -d '{}' \
            "$BASE_URL/api/v1/templates/$tpl_id/validate" --max-time $CURL_TIMEOUT)
        if [ "$code" = "200" ] || [ "$code" = "204" ]; then
            pass "模板配置验证 (validate)"
        else
            fail "模板配置验证" "HTTP $code"
        fi
    fi
}

# ===== 模块：素材库 =====
test_assets() {
    should_run "assets" || return 0
    [ -z "$TOKEN" ] && return 0

    section "4. 素材库 (assets & asset-libraries)"

    # 素材列表
    local resp=$(curl -s -w "\n%{http_code}" \
        -H "$AUTH_HEADER" "$BASE_URL/api/v1/assets" --max-time $CURL_TIMEOUT)
    local code=$(echo "$resp" | tail -1)
    local body=$(echo "$resp" | sed '$d')

    if [ "$code" = "200" ]; then
        pass "素材列表"
        local count=$(echo "$body" | python3 -c "
import sys, json
d = json.load(sys.stdin)
items = d.get('items', []) or d.get('data', []) or []
print(len(items))
" 2>/dev/null)

        if [ "${count:-0}" -gt 0 ]; then
            local has_file=$(echo "$body" | grep -c '"file_url"' || true)
            local has_thumb=$(echo "$body" | grep -c '"thumbnail_url"' || true)
            [ "$has_file" -gt 0 ] && pass "素材包含 file_url 字段" || fail "素材 file_url 字段" "未找到"
            [ "$has_thumb" -gt 0 ] && pass "素材包含 thumbnail_url 字段" || fail "素材 thumbnail_url 字段" "未找到"
        else
            pass "新用户素材列表为空（正常）"
            pass "跳过 file_url 检查（无素材）"
            pass "跳过 thumbnail_url 检查（无素材）"
        fi
    else
        fail "素材列表" "HTTP $code"
    fi

    # 素材库列表
    resp=$(curl -s -w "\n%{http_code}" \
        -H "$AUTH_HEADER" "$BASE_URL/api/v1/asset-libraries" --max-time $CURL_TIMEOUT)
    code=$(echo "$resp" | tail -1)
    body=$(echo "$resp" | sed '$d')

    if [ "$code" = "200" ]; then
        pass "素材库列表 (asset-libraries)"
    else
        fail "素材库列表" "HTTP $code"
    fi
}

# ===== 模块：诊断 =====
test_diagnosis() {
    should_run "diagnosis" || return 0
    [ -z "$TOKEN" ] && return 0

    section "5. 素材诊断"

    local project_id=""

    # 创建项目
    local resp=$(curl -s -w "\n%{http_code}" -X POST \
        -H "$AUTH_HEADER" -H "Content-Type: application/json" \
        -d '{"name":"E2E诊断测试项目"}' \
        "$BASE_URL/api/v1/projects" --max-time $CURL_TIMEOUT)
    local pcode=$(echo "$resp" | tail -1)
    local pbody=$(echo "$resp" | sed '$d')

    if [ "$pcode" = "200" ] || [ "$pcode" = "201" ]; then
        project_id=$(json_get "$pbody" "id")
        [ -z "$project_id" ] && project_id=$(json_get "$pbody" "data.id")
        pass "创建测试项目"
        CREATED_PROJECTS+=("$project_id")
    else
        # 尝试获取已有项目
        resp=$(curl -s -w "\n%{http_code}" -H "$AUTH_HEADER" \
            "$BASE_URL/api/v1/projects" --max-time $CURL_TIMEOUT)
        local lcode=$(echo "$resp" | tail -1)
        local lbody=$(echo "$resp" | sed '$d')
        if [ "$lcode" = "200" ]; then
            project_id=$(echo "$lbody" | python3 -c "
import sys, json
d = json.load(sys.stdin)
items = d.get('items', []) or d.get('data', []) or []
print(items[0].get('id', '') if items else '')
" 2>/dev/null)
            pass "获取已有项目"
        else
            fail "获取项目" "HTTP $lcode"
            return
        fi
    fi

    # 项目级素材诊断
    if [ -n "$project_id" ]; then
        resp=$(curl -s -w "\n%{http_code}" \
            -H "$AUTH_HEADER" "$BASE_URL/api/v1/projects/$project_id/asset-diagnosis" --max-time $CURL_TIMEOUT)
        local code=$(echo "$resp" | tail -1)
        body=$(echo "$resp" | sed '$d')

        if [ "$code" = "200" ]; then
            pass "项目级素材诊断"

            # 检查新格式
            local has_score=$(echo "$body" | grep -c '"readiness_score"' || true)
            local has_label=$(echo "$body" | grep -c '"readiness_label"' || true)
            local has_total=$(echo "$body" | grep -c '"total_assets"' || true)

            if [ "$has_score" -gt 0 ] || [ "$has_label" -gt 0 ] || [ "$has_total" -gt 0 ]; then
                pass "诊断返回新格式（readiness_score/readiness_label/total_assets）"
            else
                local keys=$(echo "$body" | python3 -c "
import sys, json
d = json.load(sys.stdin)
data = d.get('data', d)
if isinstance(data, dict):
    print(','.join(list(data.keys())[:8]))
else:
    print('')
" 2>/dev/null)
                fail "诊断返回结构" "keys: $keys"
            fi
        else
            fail "项目级素材诊断" "HTTP $code"
        fi
    fi
}

# ===== 模块：生成任务 =====
test_generation() {
    should_run "generation" || return 0
    [ -z "$TOKEN" ] && return 0

    section "6. 生成任务"

    local code=$(curl -s -o /dev/null -w "%{http_code}" \
        -H "$AUTH_HEADER" "$BASE_URL/api/v1/generation/tasks" --max-time $CURL_TIMEOUT)
    [ "$code" = "200" ] && pass "生成任务列表" || fail "生成任务列表" "HTTP $code"
}

# ===== 模块：订阅 =====
test_subscription() {
    should_run "subscription" || return 0
    [ -z "$TOKEN" ] && return 0

    section "7. 订阅与配额"

    local code=$(curl -s -o /dev/null -w "%{http_code}" \
        -H "$AUTH_HEADER" "$BASE_URL/api/v1/subscription/current" --max-time $CURL_TIMEOUT)
    [ "$code" = "200" ] && pass "当前订阅信息" || fail "当前订阅信息" "HTTP $code"

    code=$(curl -s -o /dev/null -w "%{http_code}" \
        -H "$AUTH_HEADER" "$BASE_URL/api/v1/subscription/billing-records" --max-time $CURL_TIMEOUT)
    [ "$code" = "200" ] && pass "订阅账单记录" || fail "订阅账单记录" "HTTP $code"

    code=$(curl -s -o /dev/null -w "%{http_code}" \
        -H "$AUTH_HEADER" "$BASE_URL/api/v1/dashboard/overview" --max-time $CURL_TIMEOUT)
    [ "$code" = "200" ] && pass "仪表盘概览" || fail "仪表盘概览" "HTTP $code"
}

# ===== 模块：其他核心接口 =====
test_misc() {
    should_run "misc" || return 0
    [ -z "$TOKEN" ] && return 0

    section "8. 其他核心接口"

    local code=$(curl -s -o /dev/null -w "%{http_code}" \
        -H "$AUTH_HEADER" "$BASE_URL/api/v1/voices" --max-time $CURL_TIMEOUT)
    [ "$code" = "200" ] && pass "音色列表" || fail "音色列表" "HTTP $code"

    # 标题生成
    code=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
        -H "$AUTH_HEADER" -H "Content-Type: application/json" \
        -d '{"name":"E2E测试标题","text":"今天天气真好我们出去玩"}' \
        "$BASE_URL/api/v1/titles" --max-time $CURL_TIMEOUT)
    if [ "$code" = "200" ] || [ "$code" = "201" ]; then
        pass "AI 标题生成"
    elif [ "$code" = "500" ] || [ "$code" = "503" ]; then
        pass "AI 标题生成（HTTP $code，环境限制，接口存在）"
    else
        fail "AI 标题生成" "HTTP $code"
    fi
}

# ===== 模块：Nginx 路由 =====
test_nginx() {
    should_run "nginx" || return 0

    section "9. 前端 SPA 路由验证（Nginx）"

    # 需要 WEB_URL 环境变量
    local web_url="${WEB_URL:-}"
    if [ -z "$web_url" ]; then
        info "跳过（WEB_URL 未设置）"
        return 0
    fi
    web_url="${web_url%/}"

    check_spa_route() {
        local path="$1"
        local name="$2"
        local code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$web_url$path")
        local content_type=$(curl -s -I --max-time 10 "$web_url$path" 2>/dev/null | grep -i "content-type" | tr -d '\r')

        if [ "$code" = "200" ] && echo "$content_type" | grep -q "text/html"; then
            pass "$name（200 + text/html）"
        elif [ "$code" = "200" ]; then
            fail "$name" "HTTP 200 但 content-type 不是 text/html"
        else
            fail "$name" "HTTP $code"
        fi
    }

    check_spa_route "/" "首页"
    check_spa_route "/app/dashboard" "/app/dashboard"
    check_spa_route "/app/editing-planner" "/app/editing-planner"
    check_spa_route "/app/assets" "/app/assets"
    check_spa_route "/app/generate" "/app/generate"
    check_spa_route "/app/templates" "/app/templates"
    check_spa_route "/app/settings/profile" "深层路由 /app/settings/profile"

    # /assets 特殊检查：不能返回 403（Nginx 目录列表问题）
    local code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$web_url/assets")
    if [ "$code" = "403" ]; then
        fail "/assets 路由" "返回 403（Nginx 目录列表问题）"
    else
        pass "/assets 路由正常（非 403）"
    fi
}

# ===== 清理 =====
cleanup() {
    [ "$CLEANUP_ENABLED" != "1" ] && return 0
    [ -z "$TOKEN" ] && return 0

    section "清理测试数据"

    for plan_id in "${CREATED_PLANS[@]}"; do
        [ -z "$plan_id" ] && continue
        curl -s -o /dev/null -X DELETE -H "$AUTH_HEADER" \
            "$BASE_URL/api/v1/edit-plans/$plan_id" --max-time 10 || true
    done
    [ ${#CREATED_PLANS[@]} -gt 0 ] && info "清理了 ${#CREATED_PLANS[@]} 个剪辑计划"

    for tpl_id in "${CREATED_TEMPLATES[@]}"; do
        [ -z "$tpl_id" ] && continue
        curl -s -o /dev/null -X DELETE -H "$AUTH_HEADER" \
            "$BASE_URL/api/v1/templates/$tpl_id" --max-time 10 || true
    done
    [ ${#CREATED_TEMPLATES[@]} -gt 0 ] && info "清理了 ${#CREATED_TEMPLATES[@]} 个模板"

    for proj_id in "${CREATED_PROJECTS[@]}"; do
        [ -z "$proj_id" ] && continue
        curl -s -o /dev/null -X DELETE -H "$AUTH_HEADER" \
            "$BASE_URL/api/v1/projects/$proj_id" --max-time 10 || true
    done
    [ ${#CREATED_PROJECTS[@]} -gt 0 ] && info "清理了 ${#CREATED_PROJECTS[@]} 个项目"

    echo "✅ 清理完成"
}

# ===== 主流程 =====
main() {
    echo ""
    echo "╔══════════════════════════════════════════════════╗"
    echo "║         API E2E 冒烟测试                         ║"
    echo "╚══════════════════════════════════════════════════╝"
    echo ""
    if [ "$PRODUCTION_MODE" = "1" ]; then
        echo "⚠️  生产环境模式 - 安全只读"
        echo "   - 不注册新用户"
        echo "   - 不创建测试数据"
        echo "   - CLEANUP_ENABLED=0"
        echo ""
    fi
    echo "环境: $BASE_URL"
    echo "模块: $MODULES"
    echo "清理: $CLEANUP_ENABLED"
    echo "开始时间: $(date '+%Y-%m-%d %H:%M:%S')"

    # 健康检查（不需要鉴权）
    test_health

    # 鉴权
    if ! setup_auth; then
        echo ""
        echo "❌ 认证失败，终止测试"
        exit 1
    fi

    # 业务模块
    test_edit_plans
    test_templates
    test_assets
    test_diagnosis
    test_generation
    test_subscription
    test_misc
    test_nginx

    # 清理
    cleanup

    # 总结
    echo ""
    echo "============================================================"
    echo "测试完成: $PASSED 通过, $FAILED 失败"
    echo "============================================================"

    if [ "$FAILED" -gt 0 ]; then
        echo ""
        echo "失败用例:"
        echo -e "$FAIL_LIST"
        echo ""
        exit 1
    else
        echo ""
        echo "🎉 全部通过！"
        echo ""
        exit 0
    fi
}

main "$@"
