package region_sync

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/glebarez/sqlite"
	"gorm.io/gorm"

	"github.com/QuantumNous/new-api/model"
	"github.com/QuantumNous/new-api/setting/system_setting"
)

// setupTestDB 设置测试数据库
func setupTestDB(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.PendingSyncRecord{})
	require.NoError(t, err)

	return db
}

func TestSyncManager_NewSyncManager(t *testing.T) {
	manager := NewSyncManager()
	assert.NotNil(t, manager.client)
}

func TestSyncManager_RunBatchSync_Empty(t *testing.T) {
	// 保存原始设置
	settings := system_setting.GetRegionSyncSettings()
	settings.Enabled = false

	manager := NewSyncManager()
	count := manager.RunBatchSync()
	assert.Equal(t, 0, count)
}

func TestSyncManager_RunBatchSync_WithRecords(t *testing.T) {
	origDB := model.DB
	defer func() {
		model.DB = origDB
	}()

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

	// 插入一些待同步记录
	record1 := &model.PendingSyncRecord{
		Id:           1,
		UserId:       100,
		RemoteUserId: 100,
		RequestId:    "req-001",
		Quota:        100,
		Status:       model.PendingSyncStatusSettled,
		CreatedAt:    time.Now().Unix(),
	}
	record2 := &model.PendingSyncRecord{
		Id:           2,
		UserId:       100,
		RemoteUserId: 100,
		RequestId:    "req-002",
		Quota:        200,
		Status:       model.PendingSyncStatusSettled,
		CreatedAt:    time.Now().Unix(),
	}

	require.NoError(t, db.Create(record1).Error)
	require.NoError(t, db.Create(record2).Error)

	manager := NewSyncManager()
	count := manager.RunBatchSync()

	// 因为没有真正的服务器，返回 0
	assert.Equal(t, 0, count)
}

func TestSyncManager_QueryMasterQuota(t *testing.T) {
	origDB := model.DB
	defer func() {
		model.DB = origDB
	}()

	manager := NewSyncManager()
	quota, err := manager.QueryMasterQuota(100)

	// 在测试环境中应该返回错误（因为没有启用配置）
	require.Error(t, err)
	assert.Equal(t, 0, quota)
}

func TestSyncManager_RunCleanup(t *testing.T) {
	origDB := model.DB
	defer func() {
		model.DB = origDB
	}()

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

	// 插入一些已同步的旧记录
	oldRecord := &model.PendingSyncRecord{
		Id:           1,
		UserId:       100,
		RemoteUserId: 100,
		RequestId:    "req-001",
		Quota:        100,
		Status:       model.PendingSyncStatusSynced,
		CreatedAt:    time.Now().Add(-40 * 24 * time.Hour).Unix(),
	}
	require.NoError(t, db.Create(oldRecord).Error)

	manager := NewSyncManager()
	deletedCount := manager.RunCleanup()
	assert.GreaterOrEqual(t, deletedCount, 0)
}

func TestSyncManager_RunBatchSync_MultipleRecords(t *testing.T) {
	origDB := model.DB
	defer func() {
		model.DB = origDB
	}()

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

	// 插入待同步记录
	for i := 1; i <= 3; i++ {
		record := &model.PendingSyncRecord{
			Id:           i,
			UserId:       100,
			RemoteUserId: 100,
			RequestId:    fmt.Sprintf("req-batch-%d", i),
			Quota:        i * 100,
			Status:       model.PendingSyncStatusSettled,
			CreatedAt:    time.Now().Unix(),
		}
		require.NoError(t, db.Create(record).Error)
	}

	manager := NewSyncManager()
	// 执行批量同步
	count := manager.RunBatchSync()

	// 因为没有真正的服务器，返回 0
	assert.Equal(t, 0, count)
}

func TestSyncManager_StartSyncWorkers(t *testing.T) {
	manager := NewSyncManager()

	// 这个测试只是验证不会 panic
	// 实际的协程测试需要更复杂的设置
	manager.StartSyncWorkers()

	// 等待一小段时间让协程启动
	time.Sleep(100 * time.Millisecond)

	// 停止管理器
	manager.Stop()
}

// ---------------------------------------------------------------------------
// 阶段 3 补充：SyncManager 端到端测试
// ---------------------------------------------------------------------------

func TestRunBatchSync_WithMockServer(t *testing.T) {
	origDB := model.DB
	defer func() { model.DB = origDB }()

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

	now := time.Now().Unix()

	settings := system_setting.GetRegionSyncSettings()
	origEnabled := settings.Enabled
	origBatchSize := settings.SyncBatchSize
	origMaxRetry := settings.MaxRetryCount
	defer func() {
		settings.Enabled = origEnabled
		settings.SyncBatchSize = origBatchSize
		settings.MaxRetryCount = origMaxRetry
	}()

	settings.Enabled = true
		settings.IsMaster = true // 跳过 FetchConfig
	settings.SyncBatchSize = 100
	settings.MaxRetryCount = 3

	t.Run("AllSuccess", func(t *testing.T) {
		require.NoError(t, db.Exec("DELETE FROM pending_sync_records").Error)
		require.NoError(t, db.Create(&model.PendingSyncRecord{
			UserId: 100, RemoteUserId: 200, RequestId: "req-batch-ok-1",
			Quota: 100, Status: model.PendingSyncStatusSettled, CreatedAt: now,
		}).Error)
		require.NoError(t, db.Create(&model.PendingSyncRecord{
			UserId: 101, RemoteUserId: 201, RequestId: "req-batch-ok-2",
			Quota: 200, Status: model.PendingSyncStatusSettled, CreatedAt: now + 1,
		}).Error)

		server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			json.NewEncoder(w).Encode(BatchDeductResponse{
				Success: true,
				Results: []DeductResult{
					{UserId: 200, Success: true, DeductedQuota: 100, RemainingQuota: 999},
					{UserId: 201, Success: true, DeductedQuota: 200, RemainingQuota: 888},
				},
			})
		}))
		defer server.Close()

		manager := &SyncManager{client: NewSyncClient(server.URL, "test-key")}
		count := manager.RunBatchSync()
		assert.Equal(t, 2, count)

		var syncedCount int64
		db.Model(&model.PendingSyncRecord{}).Where("status = ?", model.PendingSyncStatusSynced).Count(&syncedCount)
		assert.Equal(t, int64(2), syncedCount)
	})

	t.Run("PartialSuccess", func(t *testing.T) {
		require.NoError(t, db.Exec("DELETE FROM pending_sync_records").Error)
		require.NoError(t, db.Create(&model.PendingSyncRecord{
			UserId: 100, RemoteUserId: 200, RequestId: "req-part-1",
			Quota: 100, Status: model.PendingSyncStatusSettled, CreatedAt: now,
		}).Error)
		require.NoError(t, db.Create(&model.PendingSyncRecord{
			UserId: 101, RemoteUserId: 201, RequestId: "req-part-2",
			Quota: 200, Status: model.PendingSyncStatusSettled, CreatedAt: now + 1,
		}).Error)

		server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			json.NewEncoder(w).Encode(BatchDeductResponse{
				Success: true,
				Results: []DeductResult{
					{UserId: 200, Success: true, DeductedQuota: 100},
					{UserId: 201, Success: false, Error: "insufficient quota"},
				},
			})
		}))
		defer server.Close()

		manager := &SyncManager{client: NewSyncClient(server.URL, "test-key")}
		count := manager.RunBatchSync()
		assert.Equal(t, 1, count)

		var syncedCount, pendingCount int64
		db.Model(&model.PendingSyncRecord{}).Where("status = ?", model.PendingSyncStatusSynced).Count(&syncedCount)
		db.Model(&model.PendingSyncRecord{}).Where("status = ?", model.PendingSyncStatusSettled).Count(&pendingCount)
		assert.Equal(t, int64(1), syncedCount)
		assert.Equal(t, int64(1), pendingCount)
	})

	t.Run("ServerError", func(t *testing.T) {
		require.NoError(t, db.Exec("DELETE FROM pending_sync_records").Error)
		require.NoError(t, db.Create(&model.PendingSyncRecord{
			UserId: 100, RemoteUserId: 200, RequestId: "req-srv-err",
			Quota: 100, Status: model.PendingSyncStatusSettled, CreatedAt: now,
		}).Error)

		server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			w.WriteHeader(500)
			json.NewEncoder(w).Encode(BatchDeductResponse{Success: false, Error: "internal error"})
		}))
		defer server.Close()

		manager := &SyncManager{client: NewSyncClient(server.URL, "test-key")}
		count := manager.RunBatchSync()
		assert.Equal(t, 0, count)
	})
}

func TestRunBatchSync_RetryExhaustion(t *testing.T) {
	origDB := model.DB
	defer func() { model.DB = origDB }()

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

	now := time.Now().Unix()
	require.NoError(t, db.Create(&model.PendingSyncRecord{
		UserId: 100, RemoteUserId: 200, RequestId: "req-retry-exh",
		Quota: 100, Status: model.PendingSyncStatusFailed, RetryCount: 2, CreatedAt: now,
	}).Error)

	settings := system_setting.GetRegionSyncSettings()
	origEnabled := settings.Enabled
	origBatchSize := settings.SyncBatchSize
	origMaxRetry := settings.MaxRetryCount
	defer func() {
		settings.Enabled = origEnabled
		settings.SyncBatchSize = origBatchSize
		settings.MaxRetryCount = origMaxRetry
	}()
	settings.Enabled = true
		settings.IsMaster = true
	settings.SyncBatchSize = 100
	settings.MaxRetryCount = 3

	manager := &SyncManager{client: NewSyncClient("http://127.0.0.1:0", "key")}
	count := manager.RunBatchSync()
	assert.Equal(t, 0, count)

	var record model.PendingSyncRecord
	require.NoError(t, db.Where("request_id = ?", "req-retry-exh").First(&record).Error)
	assert.Equal(t, model.PendingSyncStatusFailed, record.Status)
	assert.Equal(t, 3, record.RetryCount)
}
