package controller

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/QuantumNous/new-api/common"
	"github.com/QuantumNous/new-api/model"
	"github.com/QuantumNous/new-api/service/region_sync"
	"github.com/gin-gonic/gin"
	"github.com/glebarez/sqlite"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"gorm.io/gorm"
)

func TestReceiveSyncedUserCreate_Success(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	// 使用测试专用的认证中间件，绕过系统设置检查
	router.Use(testSyncAuthMiddleware())
	router.POST("/api/internal/sync/user/create", ReceiveSyncedUserCreate)

	origDB := model.DB
	defer func() {
		model.DB = origDB
	}()

	// 设置测试数据库
	db := setupSyncTestDB(t)
	model.DB = db

	req := region_sync.SyncUserRequest{
		Username:     "testuser",
		Email:        "test@example.com",
		PasswordHash: "hashedpassword",
		DisplayName:  "Test User",
		Quota:        10000,
		RemoteUserId: 10000001,
		Group:        "default",
	}

	reqBody, _ := json.Marshal(req)
	reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/user/create", bytes.NewBuffer(reqBody))
	reqHTTP.Header.Set("Content-Type", "application/json")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, reqHTTP)

	assert.Equal(t, http.StatusOK, w.Code)

	var resp map[string]interface{}
	json.Unmarshal(w.Body.Bytes(), &resp)
	assert.True(t, resp["success"].(bool))

	// 验证用户已创建
	var user model.User
	err := db.Where("username = ?", "testuser").First(&user).Error
	require.NoError(t, err)
	assert.Equal(t, "testuser", user.Username)
	assert.Equal(t, 10000001, user.RemoteUserId)
	assert.Equal(t, "synced", user.Source)
}

func TestReceiveQuotaUpdate_Success(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.Use(testSyncAuthMiddleware())
	router.POST("/api/internal/sync/quota/update", ReceiveQuotaUpdate)

	origDB := model.DB
	defer func() {
		model.DB = origDB
	}()

	// 设置测试数据库
	db := setupSyncTestDB(t)
	model.DB = db

	// 创建测试用户
	user := model.User{
		Username:     "testuser",
		RemoteUserId: 10000001,
		Source:       "synced",
		Quota:        5000,
		SyncedQuota:  5000,
		AffCode:      "aff-qu-1",
	}
	db.Create(&user)

	req := region_sync.UpdateQuotaRequest{
		RemoteUserId: 10000001,
		Quota:        10000,
	}

	reqBody, _ := json.Marshal(req)
	reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/quota/update", bytes.NewBuffer(reqBody))
	reqHTTP.Header.Set("Content-Type", "application/json")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, reqHTTP)

	assert.Equal(t, http.StatusOK, w.Code)

	var resp map[string]interface{}
	json.Unmarshal(w.Body.Bytes(), &resp)
	assert.True(t, resp["success"].(bool))

	// 验证余额已更新
	var updatedUser model.User
	err := db.Where("remote_user_id = ?", 10000001).First(&updatedUser).Error
	require.NoError(t, err)
	assert.Equal(t, 10000, updatedUser.SyncedQuota)
}

func TestQueryUserQuota_Success(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.Use(testSyncAuthMiddleware())
	router.POST("/api/internal/sync/quota/query", QueryUserQuota)

	origDB := model.DB
	defer func() {
		model.DB = origDB
	}()

	// 设置测试数据库
	db := setupSyncTestDB(t)
	model.DB = db

	// 创建测试用户
	user := model.User{
		Username: "testuser",
		Quota:    15000,
		AffCode:  "aff-qq-1",
	}
	db.Create(&user)

	req := region_sync.QueryQuotaRequest{
		UserId: user.Id,
	}

	reqBody, _ := json.Marshal(req)
	reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/quota/query", bytes.NewBuffer(reqBody))
	reqHTTP.Header.Set("Content-Type", "application/json")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, reqHTTP)

	assert.Equal(t, http.StatusOK, w.Code)

	var resp region_sync.QueryQuotaResponse
	json.Unmarshal(w.Body.Bytes(), &resp)
	assert.True(t, resp.Success)
	assert.Equal(t, 15000, resp.Quota)
}

func TestBatchDeductQuota_Success(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.Use(testSyncAuthMiddleware())
	router.POST("/api/internal/sync/quota/batch-deduct", BatchDeductQuota)

	origDB := model.DB
	defer func() {
		model.DB = origDB
	}()

	// 设置测试数据库
	db := setupSyncTestDB(t)
	model.DB = db

	// 创建测试用户
	user1 := model.User{Username: "user1", Quota: 1000, AffCode: "aff-bd-1"}
	user2 := model.User{Username: "user2", Quota: 500, AffCode: "aff-bd-2"}
	db.Create(&user1)
	db.Create(&user2)

	req := region_sync.BatchDeductRequest{
		Records: []region_sync.BatchDeductRecord{
			{UserId: user1.Id, RequestId: "req-1", Quota: 100},
			{UserId: user2.Id, RequestId: "req-2", Quota: 50},
		},
	}

	reqBody, _ := json.Marshal(req)
	reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/quota/batch-deduct", bytes.NewBuffer(reqBody))
	reqHTTP.Header.Set("Content-Type", "application/json")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, reqHTTP)

	assert.Equal(t, http.StatusOK, w.Code)

	var resp region_sync.BatchDeductResponse
	json.Unmarshal(w.Body.Bytes(), &resp)
	assert.True(t, resp.Success)
	assert.Len(t, resp.Results, 2)

	// 验证用户余额已更新
	var updatedUser1 model.User
	db.First(&updatedUser1, user1.Id)
	assert.Equal(t, 900, updatedUser1.Quota)

	var updatedUser2 model.User
	db.First(&updatedUser2, user2.Id)
	assert.Equal(t, 450, updatedUser2.Quota)
}

func TestBatchDeductQuota_InsufficientQuota(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.Use(testSyncAuthMiddleware())
	router.POST("/api/internal/sync/quota/batch-deduct", BatchDeductQuota)

	origDB := model.DB
	defer func() {
		model.DB = origDB
	}()

	// 设置测试数据库
	db := setupSyncTestDB(t)
	model.DB = db

	// 创建余额不足的用户
	user := model.User{Username: "pooruser", Quota: 10, AffCode: "aff-poor-1"}
	db.Create(&user)

	req := region_sync.BatchDeductRequest{
		Records: []region_sync.BatchDeductRecord{
			{UserId: user.Id, RequestId: "req-1", Quota: 100},
		},
	}

	reqBody, _ := json.Marshal(req)
	reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/quota/batch-deduct", bytes.NewBuffer(reqBody))
	reqHTTP.Header.Set("Content-Type", "application/json")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, reqHTTP)

	assert.Equal(t, http.StatusOK, w.Code)

	var resp region_sync.BatchDeductResponse
	json.Unmarshal(w.Body.Bytes(), &resp)
	assert.True(t, resp.Success)
	assert.Len(t, resp.Results, 1)
	assert.False(t, resp.Results[0].Success)
	assert.Contains(t, resp.Results[0].Error, "insufficient quota")
}

// testSyncAuthMiddleware 测试专用的认证中间件，绕过系统设置
func testSyncAuthMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Set("sync_node", "test-node")
		c.Next()
	}
}

// setupSyncTestDB 设置同步测试数据库
func setupSyncTestDB(t *testing.T) *gorm.DB {
	t.Helper()
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	require.NoError(t, err)
	sqlDB, _ := db.DB()
	sqlDB.SetMaxOpenConns(1)

	err = db.AutoMigrate(&model.User{}, &model.QuotaSyncLog{})
	require.NoError(t, err)

	return db
}

// ---------------------------------------------------------------------------
// 阶段 4：Controller 边界条件测试
// ---------------------------------------------------------------------------

func TestReceiveSyncedUserCreate_DuplicateUser(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.Use(testSyncAuthMiddleware())
	router.POST("/api/internal/sync/user/create", ReceiveSyncedUserCreate)

	origDB := model.DB
	defer func() { model.DB = origDB }()

	db := setupSyncTestDB(t)
	model.DB = db

	// 先创建一个用户
	db.Create(&model.User{
		Username: "dupuser", RemoteUserId: 10000001,
		Source: common.UserSourceSynced, Password: "hash", AffCode: "aff-dup-1",
	})

	req := region_sync.SyncUserRequest{
		Username:     "dupuser",
		RemoteUserId: 10000001,
		PasswordHash: "newhash",
	}
	reqBody, _ := json.Marshal(req)
	reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/user/create", bytes.NewBuffer(reqBody))
	reqHTTP.Header.Set("Content-Type", "application/json")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, reqHTTP)

	assert.Equal(t, http.StatusOK, w.Code)
	var resp map[string]interface{}
	json.Unmarshal(w.Body.Bytes(), &resp)
	assert.True(t, resp["success"].(bool))
	assert.Contains(t, resp["message"], "already exists")
}

func TestReceiveSyncedUserCreate_InvalidBody(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.Use(testSyncAuthMiddleware())
	router.POST("/api/internal/sync/user/create", ReceiveSyncedUserCreate)

	reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/user/create", strings.NewReader("not json"))
	reqHTTP.Header.Set("Content-Type", "application/json")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, reqHTTP)
	assert.Equal(t, http.StatusBadRequest, w.Code)
}

func TestReceiveQuotaUpdate_UserNotFound(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.Use(testSyncAuthMiddleware())
	router.POST("/api/internal/sync/quota/update", ReceiveQuotaUpdate)

	origDB := model.DB
	defer func() { model.DB = origDB }()

	db := setupSyncTestDB(t)
	model.DB = db

	req := region_sync.UpdateQuotaRequest{
		RemoteUserId: 99999999,
		Quota:        5000,
	}
	reqBody, _ := json.Marshal(req)
	reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/quota/update", bytes.NewBuffer(reqBody))
	reqHTTP.Header.Set("Content-Type", "application/json")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, reqHTTP)
	assert.Equal(t, http.StatusNotFound, w.Code)
}

func TestReceiveQuotaUpdate_InvalidBody(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.Use(testSyncAuthMiddleware())
	router.POST("/api/internal/sync/quota/update", ReceiveQuotaUpdate)

	reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/quota/update", strings.NewReader("invalid"))
	reqHTTP.Header.Set("Content-Type", "application/json")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, reqHTTP)
	assert.Equal(t, http.StatusBadRequest, w.Code)
}

func TestQueryUserQuota_UserNotFound(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.Use(testSyncAuthMiddleware())
	router.POST("/api/internal/sync/quota/query", QueryUserQuota)

	origDB := model.DB
	defer func() { model.DB = origDB }()

	db := setupSyncTestDB(t)
	model.DB = db

	req := region_sync.QueryQuotaRequest{UserId: 99999}
	reqBody, _ := json.Marshal(req)
	reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/quota/query", bytes.NewBuffer(reqBody))
	reqHTTP.Header.Set("Content-Type", "application/json")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, reqHTTP)
	assert.Equal(t, http.StatusNotFound, w.Code)
}

func TestBatchDeductQuota_EmptyRecords(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.Use(testSyncAuthMiddleware())
	router.POST("/api/internal/sync/quota/batch-deduct", BatchDeductQuota)

	origDB := model.DB
	defer func() { model.DB = origDB }()

	db := setupSyncTestDB(t)
	model.DB = db

	req := region_sync.BatchDeductRequest{Records: []region_sync.BatchDeductRecord{}}
	reqBody, _ := json.Marshal(req)
	reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/quota/batch-deduct", bytes.NewBuffer(reqBody))
	reqHTTP.Header.Set("Content-Type", "application/json")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, reqHTTP)
	assert.Equal(t, http.StatusOK, w.Code)

	var resp region_sync.BatchDeductResponse
	json.Unmarshal(w.Body.Bytes(), &resp)
	assert.True(t, resp.Success)
	assert.Len(t, resp.Results, 0)
}

func TestBatchDeductQuota_UserNotFound(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.Use(testSyncAuthMiddleware())
	router.POST("/api/internal/sync/quota/batch-deduct", BatchDeductQuota)

	origDB := model.DB
	defer func() { model.DB = origDB }()

	db := setupSyncTestDB(t)
	model.DB = db

	req := region_sync.BatchDeductRequest{
		Records: []region_sync.BatchDeductRecord{
			{UserId: 99999, RequestId: "req-nf", Quota: 100},
		},
	}
	reqBody, _ := json.Marshal(req)
	reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/quota/batch-deduct", bytes.NewBuffer(reqBody))
	reqHTTP.Header.Set("Content-Type", "application/json")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, reqHTTP)
	assert.Equal(t, http.StatusOK, w.Code)

	var resp region_sync.BatchDeductResponse
	json.Unmarshal(w.Body.Bytes(), &resp)
	assert.True(t, resp.Success)
	assert.Len(t, resp.Results, 1)
	assert.False(t, resp.Results[0].Success)
	assert.Contains(t, resp.Results[0].Error, "user not found")
}

func TestBatchDeductQuota_InvalidBody(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.Use(testSyncAuthMiddleware())
	router.POST("/api/internal/sync/quota/batch-deduct", BatchDeductQuota)

	reqHTTP, _ := http.NewRequest("POST", "/api/internal/sync/quota/batch-deduct", strings.NewReader("bad"))
	reqHTTP.Header.Set("Content-Type", "application/json")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, reqHTTP)
	assert.Equal(t, http.StatusBadRequest, w.Code)
}
