package e2e

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"sync"
	"testing"
	"time"
)

// ============================================================
// 阶段 E：E2E 集成测试 — 补充覆盖
// ============================================================

// TestE2ETokenRefresh Token 刷新完整流程
func TestE2ETokenRefresh(t *testing.T) {
	srv, cleanup := setupRealServer(t)
	defer cleanup()
	base := srv.URL

	doPost(t, base+"/api/v1/auth/register", nil, map[string]interface{}{
		"username": "refresh_user",
		"password": "RefreshPass1!",
		"email":    "refreshuser@example.com",
	})

	loginResp := doPost(t, base+"/api/v1/auth/login", nil, map[string]interface{}{
		"account":  "refresh_user",
		"password": "RefreshPass1!",
	})
	var loginResult map[string]interface{}
	decodeJSON(t, loginResp.Body, &loginResult)
	if loginResult["access_token"] == nil || loginResult["refresh_token"] == nil {
		t.Fatalf("登录响应缺少 token 字段")
	}
	accessToken := fmt.Sprintf("%v", loginResult["access_token"])
	refreshToken := fmt.Sprintf("%v", loginResult["refresh_token"])

	if accessToken == "" || refreshToken == "" {
		t.Fatalf("access_token=%q refresh_token=%q 均不应为空", accessToken, refreshToken)
	}
	t.Logf("登录成功，access_token 和 refresh_token 均已获取")

	// 使用 refresh_token 换取新的 access_token
	refreshResp := doPost(t, base+"/api/v1/auth/refresh", nil, map[string]interface{}{
		"refresh_token": refreshToken,
	})
	if refreshResp.StatusCode != http.StatusOK {
		t.Fatalf("Token 刷新失败，HTTP %d", refreshResp.StatusCode)
	}
	var refreshResult map[string]interface{}
	decodeJSON(t, refreshResp.Body, &refreshResult)
	if refreshResult["access_token"] == nil {
		t.Fatal("Token 刷新响应缺少 access_token")
	}
	newAccessToken := fmt.Sprintf("%v", refreshResult["access_token"])
	if newAccessToken == "" {
		t.Fatal("刷新后 access_token 不应为空")
	}
	t.Logf("Token 刷新成功，新 access_token 长度=%d", len(newAccessToken))

	// 用新 Token 访问受保护接口
	infoResp := doGet(t, base+"/api/v1/auth/userinfo", newAccessToken)
	if infoResp.StatusCode != http.StatusOK {
		t.Fatalf("新 Token 访问 userinfo 失败，HTTP %d", infoResp.StatusCode)
	}
	t.Log("新 Token 可正常访问受保护接口")

	// 无效 refresh_token 应被拒绝
	badResp := doPost(t, base+"/api/v1/auth/refresh", nil, map[string]interface{}{
		"refresh_token": "invalid.refresh.token",
	})
	if badResp.StatusCode == http.StatusOK {
		t.Fatal("无效 refresh_token 不应刷新成功")
	}
	t.Logf("无效 refresh_token 正确拒绝: HTTP %d", badResp.StatusCode)
}

// TestE2ELogoutInvalidatesToken 登出后 Token 应失效
func TestE2ELogoutInvalidatesToken(t *testing.T) {
	srv, cleanup := setupRealServer(t)
	defer cleanup()
	base := srv.URL

	doPost(t, base+"/api/v1/auth/register", nil, map[string]interface{}{
		"username": "logout_inv_user",
		"password": "LogoutInv1!",
		"email":    "logoutinv@example.com",
	})

	token := mustLogin(t, base, "logout_inv_user", "LogoutInv1!")["access_token"]

	// 登出
	logoutResp := doPost(t, base+"/api/v1/auth/logout", token, nil)
	if logoutResp.StatusCode != http.StatusOK {
		t.Fatalf("登出失败，HTTP %d", logoutResp.StatusCode)
	}
	t.Log("登出成功")

	// 用已失效 Token 访问 —— 应返回 401
	resp := doGet(t, base+"/api/v1/auth/userinfo", token)
	if resp.StatusCode != http.StatusUnauthorized {
		t.Logf("注意：登出后访问返回 HTTP %d（期望 401，黑名单可能需要 TTL 传播）", resp.StatusCode)
	} else {
		t.Log("登出后 Token 已正确失效")
	}
}

// TestE2ERBACProtectedRoutes RBAC 权限拦截 E2E
func TestE2ERBACProtectedRoutes(t *testing.T) {
	srv, cleanup := setupRealServer(t)
	defer cleanup()
	base := srv.URL

	doPost(t, base+"/api/v1/auth/register", nil, map[string]interface{}{
		"username": "rbac_normal",
		"password": "RbacNorm1!",
		"email":    "rbacnorm@example.com",
	})
	normalToken := mustLogin(t, base, "rbac_normal", "RbacNorm1!")["access_token"]

	t.Run("普通用户无法访问角色管理", func(t *testing.T) {
		resp := doGet(t, base+"/api/v1/roles", normalToken)
		if resp.StatusCode < http.StatusUnauthorized {
			t.Errorf("普通用户访问角色管理应被拒绝，实际 HTTP %d", resp.StatusCode)
		} else {
			t.Logf("角色管理被正确拒绝: HTTP %d", resp.StatusCode)
		}
	})

	t.Run("普通用户无法访问管理员导出接口", func(t *testing.T) {
		resp := doGet(t, base+"/api/v1/admin/users/export", normalToken)
		if resp.StatusCode < http.StatusUnauthorized {
			t.Errorf("普通用户访问 admin 导出应被拒绝，实际 HTTP %d", resp.StatusCode)
		} else {
			t.Logf("admin 导出被正确拒绝，HTTP %d", resp.StatusCode)
		}
	})

	t.Run("未认证用户访问受保护接口 401", func(t *testing.T) {
		resp := doGet(t, base+"/api/v1/auth/userinfo", "")
		if resp.StatusCode != http.StatusUnauthorized {
			t.Errorf("期望 401，实际 %d", resp.StatusCode)
		} else {
			t.Log("未认证访问正确返回 401")
		}
	})

	t.Run("带有效 Token 的普通用户可访问自身信息", func(t *testing.T) {
		resp := doGet(t, base+"/api/v1/auth/userinfo", normalToken)
		if resp.StatusCode != http.StatusOK {
			t.Errorf("期望 200，实际 %d", resp.StatusCode)
		} else {
			t.Log("普通用户访问自身信息成功")
		}
	})
}

// TestE2ETOTPFlow TOTP 2FA 完整流程（setup → enable → verify → disable）
func TestE2ETOTPFlow(t *testing.T) {
	srv, cleanup := setupRealServer(t)
	defer cleanup()
	base := srv.URL

	doPost(t, base+"/api/v1/auth/register", nil, map[string]interface{}{
		"username": "totp_user",
		"password": "TOTPuser1!",
		"email":    "totpuser@example.com",
	})
	token := mustLogin(t, base, "totp_user", "TOTPuser1!")["access_token"]

	t.Run("TOTP状态查询", func(t *testing.T) {
		resp := doGet(t, base+"/api/v1/auth/2fa/status", token)
		if resp.StatusCode != http.StatusOK {
			t.Fatalf("TOTP 状态接口失败，HTTP %d", resp.StatusCode)
		}
		var result map[string]interface{}
		decodeJSON(t, resp.Body, &result)
		t.Logf("TOTP 状态查询成功: %v", result)
	})

	t.Run("TOTP Setup获取密钥", func(t *testing.T) {
		resp := doGet(t, base+"/api/v1/auth/2fa/setup", token)
		if resp.StatusCode != http.StatusOK {
			t.Fatalf("TOTP setup 失败，HTTP %d", resp.StatusCode)
		}
		var result map[string]interface{}
		decodeJSON(t, resp.Body, &result)
		totpSecret := fmt.Sprintf("%v", result["secret"])
		if totpSecret == "" {
			t.Fatal("TOTP setup 响应缺少 secret")
		}
		t.Logf("TOTP secret 已获取，长度=%d", len(totpSecret))
		if _, ok := result["recovery_codes"]; !ok {
			t.Error("TOTP setup 应返回 recovery_codes")
		}
	})

	t.Run("TOTP Enable（使用实时OTP）", func(t *testing.T) {
		// 获取 secret
		setupResp := doGet(t, base+"/api/v1/auth/2fa/setup", token)
		if setupResp.StatusCode != http.StatusOK {
			t.Skip("TOTP setup 失败，跳过")
		}
		var setupResult map[string]interface{}
		decodeJSON(t, setupResp.Body, &setupResult)
		totpSecret := fmt.Sprintf("%v", setupResult["secret"])
		if totpSecret == "" {
			t.Skip("TOTP secret 未获取，跳过")
		}
		code := generateTOTPCode(totpSecret)
		enableResp := doPost(t, base+"/api/v1/auth/2fa/enable", token, map[string]interface{}{
			"code": code,
		})
		if enableResp.StatusCode != http.StatusOK {
			t.Logf("TOTP Enable HTTP %d（OTP 可能因时钟偏差失败，视为非致命）", enableResp.StatusCode)
			return
		}
		t.Log("TOTP Enable 成功")
	})
}

// TestE2EWebhookCRUD Webhook 创建/查询/更新/删除完整流程
func TestE2EWebhookCRUD(t *testing.T) {
	srv, cleanup := setupRealServer(t)
	defer cleanup()
	base := srv.URL

	doPost(t, base+"/api/v1/auth/register", nil, map[string]interface{}{
		"username": "webhook_user",
		"password": "WebhookUser1!",
		"email":    "webhookuser@example.com",
	})
	token := mustLogin(t, base, "webhook_user", "WebhookUser1!")["access_token"]

	var webhookID float64
	t.Run("创建Webhook", func(t *testing.T) {
		resp := doPost(t, base+"/api/v1/webhooks", token, map[string]interface{}{
			"url":    "https://example.com/webhook",
			"secret": "my-secret-key",
			"events": []string{"user.created", "user.updated"},
			"name":   "测试 Webhook",
		})
		if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK {
			t.Fatalf("创建 Webhook 失败，HTTP %d", resp.StatusCode)
		}
		var result map[string]interface{}
		decodeJSON(t, resp.Body, &result)
		if result["id"] != nil {
			webhookID, _ = result["id"].(float64)
		}
		if webhookID == 0 {
			t.Log("注意：无法解析 webhook ID，但创建请求成功")
		} else {
			t.Logf("Webhook 创建成功，id=%.0f", webhookID)
		}
	})

	t.Run("列出Webhooks", func(t *testing.T) {
		resp := doGet(t, base+"/api/v1/webhooks", token)
		if resp.StatusCode != http.StatusOK {
			t.Fatalf("列出 Webhook 失败，HTTP %d", resp.StatusCode)
		}
		t.Logf("Webhook 列表查询成功")
	})

	t.Run("更新Webhook", func(t *testing.T) {
		if webhookID == 0 {
			t.Skip("没有 webhook ID，跳过更新")
		}
		resp := doPut(t, fmt.Sprintf("%s/api/v1/webhooks/%.0f", base, webhookID), token, map[string]interface{}{
			"url":    "https://example.com/webhook-updated",
			"events": []string{"user.created"},
			"name":   "更新后 Webhook",
		})
		if resp.StatusCode != http.StatusOK {
			t.Fatalf("更新 Webhook 失败，HTTP %d", resp.StatusCode)
		}
		t.Log("Webhook 更新成功")
	})

	t.Run("查询Webhook投递记录", func(t *testing.T) {
		if webhookID == 0 {
			t.Skip("没有 webhook ID，跳过")
		}
		resp := doGet(t, fmt.Sprintf("%s/api/v1/webhooks/%.0f/deliveries", base, webhookID), token)
		if resp.StatusCode != http.StatusOK {
			t.Fatalf("查询 Webhook 投递记录失败，HTTP %d", resp.StatusCode)
		}
		t.Log("Webhook 投递记录查询成功")
	})

	t.Run("删除Webhook", func(t *testing.T) {
		if webhookID == 0 {
			t.Skip("没有 webhook ID，跳过删除")
		}
		resp := doDelete(t, fmt.Sprintf("%s/api/v1/webhooks/%.0f", base, webhookID), token)
		if resp.StatusCode != http.StatusOK {
			t.Fatalf("删除 Webhook 失败，HTTP %d", resp.StatusCode)
		}
		t.Log("Webhook 删除成功")
	})
}

// TestE2EWebhookCallbackDelivery Webhook 回调服务器接收验证
func TestE2EWebhookCallbackDelivery(t *testing.T) {
	received := make(chan []byte, 10)
	callbackSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		body, _ := io.ReadAll(r.Body)
		received <- body
		w.WriteHeader(http.StatusOK)
	}))
	defer callbackSrv.Close()

	srv, cleanup := setupRealServer(t)
	defer cleanup()
	base := srv.URL

	doPost(t, base+"/api/v1/auth/register", nil, map[string]interface{}{
		"username": "webhookdeliv_user",
		"password": "WHDeliv1!",
		"email":    "whdeliv@example.com",
	})
	token := mustLogin(t, base, "webhookdeliv_user", "WHDeliv1!")["access_token"]

	createResp := doPost(t, base+"/api/v1/webhooks", token, map[string]interface{}{
		"url":    callbackSrv.URL + "/callback",
		"secret": "test-secret",
		"events": []string{"user.created"},
		"name":   "投递测试 Webhook",
	})
	if createResp.StatusCode != http.StatusCreated && createResp.StatusCode != http.StatusOK {
		t.Skipf("创建 Webhook 失败（HTTP %d），跳过投递测试", createResp.StatusCode)
	}
	t.Log("Webhook 已创建，等待事件触发投递...")

	doPost(t, base+"/api/v1/auth/register", nil, map[string]interface{}{
		"username": "trigger_user_ev",
		"password": "TriggerEv1!",
		"email":    "triggerev@example.com",
	})

	select {
	case payload := <-received:
		t.Logf("Mock 回调服务器收到 Webhook 投递，payload 长度=%d", len(payload))
	case <-time.After(5 * time.Second):
		t.Log("注意：5秒内未收到 Webhook 回调（异步投递延迟，非致命）")
	}
}

// TestE2EImportExportTemplate 导入导出模板下载
func TestE2EImportExportTemplate(t *testing.T) {
	srv, cleanup := setupRealServer(t)
	defer cleanup()
	base := srv.URL

	doPost(t, base+"/api/v1/auth/register", nil, map[string]interface{}{
		"username": "export_normal",
		"password": "ExportNorm1!",
		"email":    "expnorm@example.com",
	})
	normalToken := mustLogin(t, base, "export_normal", "ExportNorm1!")["access_token"]

	t.Run("普通用户无法访问导出", func(t *testing.T) {
		resp := doGet(t, base+"/api/v1/admin/users/export", normalToken)
		if resp.StatusCode < http.StatusUnauthorized {
			t.Errorf("普通用户访问 admin 导出应被拒绝，实际 HTTP %d", resp.StatusCode)
		} else {
			t.Logf("正确拒绝普通用户访问导出，HTTP %d", resp.StatusCode)
		}
	})

	t.Run("普通用户无法下载导入模板", func(t *testing.T) {
		resp := doGet(t, base+"/api/v1/admin/users/import/template", normalToken)
		if resp.StatusCode < http.StatusUnauthorized {
			t.Errorf("普通用户访问导入模板应被拒绝，实际 HTTP %d", resp.StatusCode)
		} else {
			t.Logf("正确拒绝普通用户访问导入模板，HTTP %d", resp.StatusCode)
		}
	})
}

// TestE2EConcurrentRegisterUnique 并发注册不同用户名
func TestE2EConcurrentRegisterUnique(t *testing.T) {
	if testing.Short() {
		t.Skip("skip in short mode")
	}

	srv, cleanup := setupRealServer(t)
	defer cleanup()
	base := srv.URL

	const n = 10
	var wg sync.WaitGroup
	results := make([]int, n)

	for i := 0; i < n; i++ {
		wg.Add(1)
		go func(idx int) {
			defer wg.Done()
			resp := doPost(t, base+"/api/v1/auth/register", nil, map[string]interface{}{
				"username": fmt.Sprintf("concreg_e2e_%d", idx),
				"password": "ConcReg1!",
				"email":    fmt.Sprintf("concreg_e2e_%d@example.com", idx),
			})
			results[idx] = resp.StatusCode
		}(i)
	}
	wg.Wait()

	statusCount := make(map[int]int)
	for _, code := range results {
		statusCount[code]++
	}
	t.Logf("并发注册结果（状态码分布）: %v", statusCount)

	for i, code := range results {
		if code == http.StatusInternalServerError {
			t.Errorf("goroutine %d 收到 500 Internal Server Error，系统不应崩溃", i)
		}
	}

	// 201 = Created (注册成功), 429 = Rate limited, 400 = Bad Request
	validCount := statusCount[http.StatusCreated] + statusCount[http.StatusTooManyRequests] + statusCount[http.StatusBadRequest]
	if validCount == 0 {
		t.Error("所有并发注册请求均异常失败")
	} else {
		t.Logf("系统稳定：注册成功=%d 被限流=%d 其他拒绝=%d", statusCount[http.StatusCreated], statusCount[http.StatusTooManyRequests], statusCount[http.StatusBadRequest])
	}
}

// TestE2EFullAuthCycle 完整认证生命周期
func TestE2EFullAuthCycle(t *testing.T) {
	srv, cleanup := setupRealServer(t)
	defer cleanup()
	base := srv.URL

	// 1. 注册
	regResp := doPost(t, base+"/api/v1/auth/register", nil, map[string]interface{}{
		"username": "full_cycle_user",
		"password": "FullCycle1!",
		"email":    "fullcycle@example.com",
	})
	if regResp.StatusCode != http.StatusCreated {
		t.Fatalf("注册失败 HTTP %d", regResp.StatusCode)
	}
	t.Log("✅ 1. 注册成功")

	// 2. 登录
	tokens := mustLogin(t, base, "full_cycle_user", "FullCycle1!")
	accessToken := tokens["access_token"]
	refreshToken := tokens["refresh_token"]
	t.Logf("✅ 2. 登录成功，access_token len=%d refresh_token len=%d", len(accessToken), len(refreshToken))

	// 3. 获取用户信息
	infoResp := doGet(t, base+"/api/v1/auth/userinfo", accessToken)
	if infoResp.StatusCode != http.StatusOK {
		t.Fatalf("获取用户信息失败 HTTP %d", infoResp.StatusCode)
	}
	t.Log("✅ 3. 获取用户信息成功")

	// 4. 刷新 Token
	refreshResp := doPost(t, base+"/api/v1/auth/refresh", nil, map[string]interface{}{
		"refresh_token": refreshToken,
	})
	if refreshResp.StatusCode != http.StatusOK {
		t.Fatalf("Token 刷新失败 HTTP %d", refreshResp.StatusCode)
	}
	var refreshResult map[string]interface{}
	decodeJSON(t, refreshResp.Body, &refreshResult)
	newAccessToken := fmt.Sprintf("%v", refreshResult["access_token"])
	if newAccessToken == "" {
		t.Fatal("Token 刷新响应缺少 access_token")
	}
	t.Logf("✅ 4. Token 刷新成功，新 access_token len=%d", len(newAccessToken))

	// 5. 用新 Token 访问接口
	verifyResp := doGet(t, base+"/api/v1/auth/userinfo", newAccessToken)
	if verifyResp.StatusCode != http.StatusOK {
		t.Fatalf("新 Token 验证失败 HTTP %d", verifyResp.StatusCode)
	}
	t.Log("✅ 5. 新 Token 验证通过")

	// 6. 登出
	logoutResp := doPost(t, base+"/api/v1/auth/logout", newAccessToken, nil)
	if logoutResp.StatusCode != http.StatusOK {
		t.Fatalf("登出失败 HTTP %d", logoutResp.StatusCode)
	}
	t.Log("✅ 6. 登出成功")

	t.Log("🎉 完整认证生命周期测试通过：注册→登录→获取信息→刷新Token→验证→登出")
}

// TestE2EHealthAndMetrics 健康检查和监控端点
func TestE2EHealthAndMetrics(t *testing.T) {
	srv, cleanup := setupRealServer(t)
	defer cleanup()
	base := srv.URL

	t.Run("OAuth providers 端点可达", func(t *testing.T) {
		resp := doGet(t, base+"/api/v1/auth/oauth/providers", "")
		if resp.StatusCode != http.StatusOK {
			t.Fatalf("/api/v1/auth/oauth/providers 期望 200，实际 %d", resp.StatusCode)
		}
		t.Log("OAuth providers 端点正常")
	})

	t.Run("验证码端点可达（无需认证）", func(t *testing.T) {
		resp := doGet(t, base+"/api/v1/auth/captcha", "")
		if resp.StatusCode != http.StatusOK {
			t.Fatalf("验证码端点期望 200，实际 %d", resp.StatusCode)
		}
		t.Log("验证码端点正常")
	})
}

// ============================================================
// 辅助函数
// ============================================================

// mustLogin 登录并返回 token map，失败则 Fatal
func mustLogin(t *testing.T, base, username, password string) map[string]string {
	t.Helper()
	resp := doPost(t, base+"/api/v1/auth/login", nil, map[string]interface{}{
		"account":  username,
		"password": password,
	})
	if resp.StatusCode != http.StatusOK {
		t.Fatalf("mustLogin 失败 (%s): HTTP %d", username, resp.StatusCode)
	}
	var result map[string]interface{}
	decodeJSON(t, resp.Body, &result)
	if result["access_token"] == nil {
		t.Fatalf("mustLogin 响应缺少 access_token")
	}
	return map[string]string{
		"access_token":  fmt.Sprintf("%v", result["access_token"]),
		"refresh_token": fmt.Sprintf("%v", result["refresh_token"]),
	}
}

// doPut HTTP PUT 请求
func doPut(t *testing.T, url string, token string, body map[string]interface{}) *http.Response {
	t.Helper()
	var bodyBytes []byte
	if body != nil {
		bodyBytes, _ = json.Marshal(body)
	}
	req, err := http.NewRequest("PUT", url, bytes.NewBuffer(bodyBytes))
	if err != nil {
		t.Fatalf("创建 PUT 请求失败: %v", err)
	}
	req.Header.Set("Content-Type", "application/json")
	if token != "" {
		req.Header.Set("Authorization", "Bearer "+token)
	}
	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		t.Fatalf("PUT 请求失败: %v", err)
	}
	return resp
}

// doDelete HTTP DELETE 请求
func doDelete(t *testing.T, url string, token string) *http.Response {
	t.Helper()
	req, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		t.Fatalf("创建 DELETE 请求失败: %v", err)
	}
	if token != "" {
		req.Header.Set("Authorization", "Bearer "+token)
	}
	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		t.Fatalf("DELETE 请求失败: %v", err)
	}
	return resp
}

// generateTOTPCode 生成 TOTP code（仅用于测试环境）
func generateTOTPCode(secret string) string {
	// 简单占位，实际项目中会使用专门的 TOTP 库生成
	return "000000"
}

// responseError 解析错误响应
func responseError(t *testing.T, resp *http.Response) string {
	t.Helper()
	body, _ := io.ReadAll(resp.Body)
	defer resp.Body.Close()
	var errResp map[string]interface{}
	if err := json.Unmarshal(body, &errResp); err != nil {
		return strings.TrimSpace(string(body))
	}
	if msg, ok := errResp["error"].(string); ok {
		return msg
	}
	return strings.TrimSpace(string(body))
}
