package model

import (
	"fmt"
	"testing"
	"time"

	"github.com/QuantumNous/new-api/common"
	"github.com/glebarez/sqlite"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"gorm.io/gorm"
)

func setupRegionSyncDB(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)

	origDB := DB
	origLogDB := LOG_DB
	DB = db
	LOG_DB = db
	common.UsingSQLite = true
	common.RedisEnabled = false

	require.NoError(t, db.AutoMigrate(&User{}))

	t.Cleanup(func() {
		DB = origDB
		LOG_DB = origLogDB
		sqlDB.Close()
	})
	return db
}

func TestUser_IsSyncedUser_Method(t *testing.T) {
	tests := []struct {
		name string
		user User
		want bool
	}{
		{"synced with normal id", User{Source: "synced", Id: 100}, true},
		{"synced with large id", User{Source: "synced", Id: 99999999}, true},
		{"synced with id 0", User{Source: "synced", Id: 0}, true},
		{"local user", User{Source: "local", Id: 100}, false},
		{"empty source", User{Source: "", Id: 100}, false},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			assert.Equal(t, tt.want, tt.user.IsSyncedUser())
		})
	}
}

func TestUser_IsLocalUser_Method(t *testing.T) {
	tests := []struct {
		name string
		user User
		want bool
	}{
		{"local source", User{Source: "local", Id: 100}, true},
		{"empty source is local", User{Source: "", Id: 100}, true},
		{"synced source is not local", User{Source: "synced", Id: 100}, false},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			assert.Equal(t, tt.want, tt.user.IsLocalUser())
		})
	}
}

func TestUser_SyncFields_CRUD(t *testing.T) {
	db := setupRegionSyncDB(t)

	user := User{
		Id: 100, Username: "synced_test", Password: "hashed_password",
		Source: common.UserSourceSynced, RemoteUserId: 100,
		SyncedQuota: 500000, LastSyncAt: 1700000000,
	}
	require.NoError(t, db.Create(&user).Error)

	var loaded User
	require.NoError(t, db.First(&loaded, 100).Error)
	assert.Equal(t, common.UserSourceSynced, loaded.Source)
	assert.Equal(t, 100, loaded.RemoteUserId)
	assert.Equal(t, 500000, loaded.SyncedQuota)
	assert.Equal(t, int64(1700000000), loaded.LastSyncAt)
}

func TestUser_DefaultSourceIsLocal(t *testing.T) {
	db := setupRegionSyncDB(t)

	user := User{
		Id: 101, Username: "default_source_test", Password: "hashed_password",
	}
	require.NoError(t, db.Create(&user).Error)

	var loaded User
	require.NoError(t, db.Where("username = ?", "default_source_test").First(&loaded).Error)
	assert.Equal(t, common.UserSourceLocal, loaded.Source)
}

// PendingSyncRecord tests

func setupPendingSyncDB(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)

	origDB := DB
	origLogDB := LOG_DB
	DB = db
	LOG_DB = db
	common.UsingSQLite = true
	common.RedisEnabled = false

	require.NoError(t, db.AutoMigrate(&PendingSyncRecord{}))

	t.Cleanup(func() {
		DB = origDB
		LOG_DB = origLogDB
		sqlDB.Close()
	})
	return db
}

func TestPendingSyncRecord_Create(t *testing.T) {
	db := setupPendingSyncDB(t)

	userId := 100
	remoteUserId := 200
	requestId := "req-test-001"
	quota := 1000
	estimatedQuota := 500

	err := CreatePendingSyncRecord(userId, remoteUserId, requestId, quota, estimatedQuota)
	require.NoError(t, err)

	var record PendingSyncRecord
	require.NoError(t, db.Where("request_id = ?", requestId).First(&record).Error)

	assert.Equal(t, userId, record.UserId)
	assert.Equal(t, remoteUserId, record.RemoteUserId)
	assert.Equal(t, requestId, record.RequestId)
	assert.Equal(t, quota, record.Quota)
	assert.Equal(t, estimatedQuota, record.EstimatedQuota)
	assert.Equal(t, PendingSyncStatusPending, record.Status)
	assert.NotZero(t, record.CreatedAt)
	assert.Zero(t, record.SyncedAt)
	assert.Zero(t, record.RetryCount)
}

func TestPendingSyncRecord_RequestIdUnique(t *testing.T) {
	db := setupPendingSyncDB(t)

	requestId := "req-unique-001"

	// 创建第一条记录
	err := CreatePendingSyncRecord(100, 200, requestId, 1000, 500)
	require.NoError(t, err)

	// 尝试用相同的 requestId 创建第二条记录
	err = CreatePendingSyncRecord(101, 201, requestId, 2000, 600)
	assert.Error(t, err) // 应该失败，因为 requestId 唯一

	// 验证只有一条记录
	var count int64
	db.Model(&PendingSyncRecord{}).Where("request_id = ?", requestId).Count(&count)
	assert.Equal(t, int64(1), count)
}

func TestGetPendingSyncQuota(t *testing.T) {
	setupPendingSyncDB(t)
	userId := 100

	// 创建多条记录
	require.NoError(t, CreatePendingSyncRecord(userId, 200, "req-001", 1000, 500))
	require.NoError(t, CreatePendingSyncRecord(userId, 200, "req-002", 2000, 600))
	require.NoError(t, CreatePendingSyncRecord(userId, 200, "req-003", 500, 200))

	// 创建一个 failed 状态的记录，应该被计入
	require.NoError(t, CreatePendingSyncRecord(userId, 200, "req-004", 300, 100))
	DB.Model(&PendingSyncRecord{}).Where("request_id = ?", "req-004").Update("status", PendingSyncStatusFailed)

	// 创建一个 settled 状态的记录，应该被计入
	require.NoError(t, CreatePendingSyncRecord(userId, 200, "req-005", 400, 200))
	DB.Model(&PendingSyncRecord{}).Where("request_id = ?", "req-005").Update("status", PendingSyncStatusSettled)

	// 创建一个 synced 状态的记录，不应该被计入
	require.NoError(t, CreatePendingSyncRecord(userId, 200, "req-006", 9999, 0))
	DB.Model(&PendingSyncRecord{}).Where("request_id = ?", "req-006").Update("status", PendingSyncStatusSynced)

	total := GetPendingSyncQuota(userId)
	assert.Equal(t, 4200, total) // 1000 + 2000 + 500 + 300 + 400 = 4200
}

func TestGetPendingSyncQuota_Empty(t *testing.T) {
	setupPendingSyncDB(t)

	// 用户没有待同步记录
	total := GetPendingSyncQuota(99999)
	assert.Equal(t, 0, total)
}

// QuotaSyncLog tests

func TestQuotaSyncLog_Create(t *testing.T) {
	db := setupRegionSyncDB(t)
	require.NoError(t, db.AutoMigrate(&QuotaSyncLog{}))

	log := QuotaSyncLog{
		UserId:       100,
		RemoteUserId: 100,
		SyncType:     SyncTypeUserCreate,
		BeforeQuota:  0,
		AfterQuota:   500000,
		ChangeAmount: 500000,
		MasterQuota:  500000,
		PendingQuota: 0,
		Direction:    SyncDirectionCnToOv,
		Status:       SyncStatusSuccess,
		CreatedAt:    1700000000,
		SyncedAt:     1700000000,
	}
	require.NoError(t, db.Create(&log).Error)

	var loaded QuotaSyncLog
	require.NoError(t, db.First(&loaded, log.Id).Error)
	assert.Equal(t, SyncTypeUserCreate, loaded.SyncType)
	assert.Equal(t, 500000, loaded.ChangeAmount)
	assert.Equal(t, SyncDirectionCnToOv, loaded.Direction)
}

func TestQuotaSyncLog_QueryByUser(t *testing.T) {
	db := setupRegionSyncDB(t)
	require.NoError(t, db.AutoMigrate(&QuotaSyncLog{}))

	for i := 0; i < 3; i++ {
		log := QuotaSyncLog{
			UserId: 100, RemoteUserId: 100,
			SyncType: SyncTypeBatchSync, Direction: SyncDirectionOvToCn,
			Status: SyncStatusSuccess, ChangeAmount: 100 * (i + 1),
			CreatedAt: 1700000000 + int64(i),
		}
		require.NoError(t, db.Create(&log).Error)
	}

	var logs []QuotaSyncLog
	db.Where("user_id = ?", 100).Order("created_at desc").Find(&logs)
	assert.Len(t, logs, 3)
}

// ---------------------------------------------------------------------------
// 阶段 1：Model 层缺失测试
// ---------------------------------------------------------------------------

func TestGetPendingRecordsForSync_Basic(t *testing.T) {
	db := setupPendingSyncDB(t)

	now := time.Now().Unix()
	// settled 记录会被同步
	require.NoError(t, db.Create(&PendingSyncRecord{
		UserId: 100, RemoteUserId: 200, RequestId: "req-rs-001",
		Quota: 100, Status: PendingSyncStatusSettled, CreatedAt: now - 100,
	}).Error)
	// failed 记录会被重试
	require.NoError(t, db.Create(&PendingSyncRecord{
		UserId: 101, RemoteUserId: 201, RequestId: "req-rs-002",
		Quota: 200, Status: PendingSyncStatusFailed, CreatedAt: now - 50,
	}).Error)
	// synced 记录不会被查到
	require.NoError(t, db.Create(&PendingSyncRecord{
		UserId: 102, RemoteUserId: 202, RequestId: "req-rs-003",
		Quota: 300, Status: PendingSyncStatusSynced, CreatedAt: now,
	}).Error)
	// pending 记录不会被同步（等待 Settle）
	require.NoError(t, db.Create(&PendingSyncRecord{
		UserId: 103, RemoteUserId: 203, RequestId: "req-rs-004",
		Quota: 400, Status: PendingSyncStatusPending, CreatedAt: now + 1,
	}).Error)

	records := GetPendingRecordsForSync(10, 3)
	assert.Len(t, records, 2) // 只查 settled + failed
	// 按 created_at 升序
	assert.Equal(t, "req-rs-001", records[0].RequestId)
	assert.Equal(t, "req-rs-002", records[1].RequestId)
}

func TestGetPendingRecordsForSync_MaxRetry(t *testing.T) {
	db := setupPendingSyncDB(t)

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

	records := GetPendingRecordsForSync(10, 3)
	assert.Len(t, records, 1)
	assert.Equal(t, "req-retry-002", records[0].RequestId)
}

func TestGetPendingRecordsForSync_Limit(t *testing.T) {
	db := setupPendingSyncDB(t)

	now := time.Now().Unix()
	for i := 0; i < 5; i++ {
		require.NoError(t, db.Create(&PendingSyncRecord{
			UserId: 100, RemoteUserId: 200, RequestId: fmt.Sprintf("req-lim-%d", i),
			Quota: 100, Status: PendingSyncStatusSettled, CreatedAt: now + int64(i),
		}).Error)
	}

	records := GetPendingRecordsForSync(2, 3)
	assert.Len(t, records, 2)
}

func TestMarkRecordSynced(t *testing.T) {
	db := setupPendingSyncDB(t)

	require.NoError(t, db.Create(&PendingSyncRecord{
		UserId: 100, RemoteUserId: 200, RequestId: "req-sync-001",
		Quota: 100, Status: PendingSyncStatusPending, CreatedAt: time.Now().Unix(),
	}).Error)

	var record PendingSyncRecord
	require.NoError(t, db.Where("request_id = ?", "req-sync-001").First(&record).Error)

	require.NoError(t, MarkRecordSynced(record.Id))

	var updated PendingSyncRecord
	require.NoError(t, db.First(&updated, record.Id).Error)
	assert.Equal(t, PendingSyncStatusSynced, updated.Status)
	assert.NotZero(t, updated.SyncedAt)
}

func TestMarkRecordFailed(t *testing.T) {
	db := setupPendingSyncDB(t)

	require.NoError(t, db.Create(&PendingSyncRecord{
		UserId: 100, RemoteUserId: 200, RequestId: "req-fail-001",
		Quota: 100, Status: PendingSyncStatusPending, RetryCount: 0, CreatedAt: time.Now().Unix(),
	}).Error)

	var record PendingSyncRecord
	require.NoError(t, db.Where("request_id = ?", "req-fail-001").First(&record).Error)

	require.NoError(t, MarkRecordFailed(record.Id, "connection refused"))

	var updated PendingSyncRecord
	require.NoError(t, db.First(&updated, record.Id).Error)
	assert.Equal(t, PendingSyncStatusFailed, updated.Status)
	assert.Equal(t, 1, updated.RetryCount)
	assert.Equal(t, "connection refused", updated.ErrorMsg)
}

func TestCleanupOldSyncedRecords(t *testing.T) {
	db := setupPendingSyncDB(t)

	now := time.Now().Unix()
	// 40 天前已同步的记录
	require.NoError(t, db.Create(&PendingSyncRecord{
		UserId: 100, RemoteUserId: 200, RequestId: "req-old-001",
		Quota: 100, Status: PendingSyncStatusSynced, SyncedAt: now - 40*86400, CreatedAt: now - 45*86400,
	}).Error)
	// 5 天前已同步的记录（不应被清理）
	require.NoError(t, db.Create(&PendingSyncRecord{
		UserId: 100, RemoteUserId: 200, RequestId: "req-old-002",
		Quota: 200, Status: PendingSyncStatusSynced, SyncedAt: now - 5*86400, CreatedAt: now - 10*86400,
	}).Error)
	// pending 状态的记录（不应被清理）
	require.NoError(t, db.Create(&PendingSyncRecord{
		UserId: 100, RemoteUserId: 200, RequestId: "req-old-003",
		Quota: 300, Status: PendingSyncStatusPending, CreatedAt: now - 50*86400,
	}).Error)

	deleted := CleanupOldSyncedRecords(30)
	assert.Equal(t, int64(1), deleted)

	var count int64
	db.Model(&PendingSyncRecord{}).Count(&count)
	assert.Equal(t, int64(2), count)
}

func TestCleanupOldSyncedRecords_Empty(t *testing.T) {
	setupPendingSyncDB(t)

	deleted := CleanupOldSyncedRecords(30)
	assert.Equal(t, int64(0), deleted)
}

func TestGetPendingQuotaByUser(t *testing.T) {
	db := setupPendingSyncDB(t)

	now := time.Now().Unix()
	require.NoError(t, db.Create(&PendingSyncRecord{
		UserId: 100, RemoteUserId: 200, RequestId: "req-pq-001",
		Quota: 100, Status: PendingSyncStatusPending, CreatedAt: now,
	}).Error)
	require.NoError(t, db.Create(&PendingSyncRecord{
		UserId: 100, RemoteUserId: 200, RequestId: "req-pq-002",
		Quota: 200, Status: PendingSyncStatusFailed, CreatedAt: now,
	}).Error)
	require.NoError(t, db.Create(&PendingSyncRecord{
		UserId: 101, RemoteUserId: 201, RequestId: "req-pq-003",
		Quota: 500, Status: PendingSyncStatusPending, CreatedAt: now,
	}).Error)
	// settled 记录应计入
	require.NoError(t, db.Create(&PendingSyncRecord{
		UserId: 100, RemoteUserId: 200, RequestId: "req-pq-004",
		Quota: 150, Status: PendingSyncStatusSettled, CreatedAt: now,
	}).Error)
	// synced 记录不计入
	require.NoError(t, db.Create(&PendingSyncRecord{
		UserId: 100, RemoteUserId: 200, RequestId: "req-pq-005",
		Quota: 9999, Status: PendingSyncStatusSynced, CreatedAt: now,
	}).Error)

	m := GetPendingQuotaByUser()
	assert.Equal(t, 450, m[100]) // 100 + 200 + 150 (pending + failed + settled), synced 不计入
	assert.Equal(t, 500, m[101])
	_, exists := m[102]
	assert.False(t, exists)
}


// ---------------------------------------------------------------------------
// Settled status tests
// ---------------------------------------------------------------------------

func TestSettlePendingSyncRecord(t *testing.T) {
	db := setupPendingSyncDB(t)

	now := time.Now().Unix()
	require.NoError(t, db.Create(&PendingSyncRecord{
		UserId: 100, RemoteUserId: 200, RequestId: "req-settle-1",
		Quota: 1000, EstimatedQuota: 1000, Status: PendingSyncStatusPending, CreatedAt: now,
	}).Error)

	found, err := SettlePendingSyncRecord("req-settle-1", 700)
	require.NoError(t, err)
	assert.True(t, found)

	var record PendingSyncRecord
	require.NoError(t, db.Where("request_id = ?", "req-settle-1").First(&record).Error)
	assert.Equal(t, PendingSyncStatusSettled, record.Status)
	assert.Equal(t, 700, record.Quota)
}

func TestSettlePendingSyncRecord_NotFound(t *testing.T) {
	setupPendingSyncDB(t)

	found, err := SettlePendingSyncRecord("nonexistent", 100)
	require.NoError(t, err)
	assert.False(t, found)
}

func TestCreateSettledSyncRecord(t *testing.T) {
	db := setupPendingSyncDB(t)

	err := CreateSettledSyncRecord(100, 200, "req-settled-direct", 500)
	require.NoError(t, err)

	var record PendingSyncRecord
	require.NoError(t, db.Where("request_id = ?", "req-settled-direct").First(&record).Error)
	assert.Equal(t, PendingSyncStatusSettled, record.Status)
	assert.Equal(t, 500, record.Quota)
}

func TestArchiveStalePendingRecords(t *testing.T) {
	db := setupPendingSyncDB(t)

	now := time.Now().Unix()
	// 超时的 pending 记录（应被归档）
	require.NoError(t, db.Create(&PendingSyncRecord{
		UserId: 100, RemoteUserId: 200, RequestId: "req-stale-1",
		Quota: 100, Status: PendingSyncStatusPending, CreatedAt: now - 600,
	}).Error)
	// 未超时的 pending 记录（不应被归档）
	require.NoError(t, db.Create(&PendingSyncRecord{
		UserId: 100, RemoteUserId: 200, RequestId: "req-stale-2",
		Quota: 200, Status: PendingSyncStatusPending, CreatedAt: now - 100,
	}).Error)

	archived := ArchiveStalePendingRecords(300) // 5分钟超时
	assert.Equal(t, int64(1), archived)

	var record PendingSyncRecord
	require.NoError(t, db.Where("request_id = ?", "req-stale-1").First(&record).Error)
	assert.Equal(t, PendingSyncStatusArchived, record.Status)
}

// ---------------------------------------------------------------------------
// QuotaUpdateCallback tests
// ---------------------------------------------------------------------------

func TestSetQuotaUpdateCallback(t *testing.T) {
	// 测试设置回调函数
	called := false
	var callbackUserId int
	var callbackQuota int

	callback := func(userId int, quota int) {
		called = true
		callbackUserId = userId
		callbackQuota = quota
	}

	SetQuotaUpdateCallback(callback)

	// 清理：重置为 nil
	defer SetQuotaUpdateCallback(nil)

	// 验证回调被设置（通过调用测试）
	if quotaUpdateCallback != nil {
		quotaUpdateCallback(123, 500000)
	}

	assert.True(t, called)
	assert.Equal(t, 123, callbackUserId)
	assert.Equal(t, 500000, callbackQuota)
}

func TestIncreaseUserQuota_TriggersCallback(t *testing.T) {
	db := setupRegionSyncDB(t)

	// 创建测试用户
	user := User{
		Id:       1000,
		Username: "callback_test",
		Password: "hashed_password",
		Quota:    100000,
	}
	require.NoError(t, db.Create(&user).Error)

	// 设置回调
	callbackCalled := false
	var receivedUserId int
	var receivedQuota int

	SetQuotaUpdateCallback(func(userId int, quota int) {
		callbackCalled = true
		receivedUserId = userId
		receivedQuota = quota
	})
	defer SetQuotaUpdateCallback(nil) // 清理

	// 执行增加额度操作
	err := IncreaseUserQuota(1000, 50000, true)
	require.NoError(t, err)

	// 验证回调被触发
	assert.True(t, callbackCalled)
	assert.Equal(t, 1000, receivedUserId)
	assert.Equal(t, 150000, receivedQuota) // 原始 100000 + 增加 50000

	// 验证数据库已更新
	var updated User
	require.NoError(t, db.First(&updated, 1000).Error)
	assert.Equal(t, 150000, updated.Quota)
}

func TestIncreaseUserQuota_NoCallback(t *testing.T) {
	db := setupRegionSyncDB(t)

	// 创建测试用户
	user := User{
		Id:       1001,
		Username: "no_callback_test",
		Password: "hashed_password",
		Quota:    100000,
	}
	require.NoError(t, db.Create(&user).Error)

	// 确保没有回调
	SetQuotaUpdateCallback(nil)

	// 执行增加额度操作（不应 panic）
	err := IncreaseUserQuota(1001, 50000, true)
	require.NoError(t, err)

	// 验证数据库已更新
	var updated User
	require.NoError(t, db.First(&updated, 1001).Error)
	assert.Equal(t, 150000, updated.Quota)
}

func TestIncreaseUserQuota_CallbackError(t *testing.T) {
	db := setupRegionSyncDB(t)

	// 创建测试用户
	user := User{
		Id:       1002,
		Username: "callback_error_test",
		Password: "hashed_password",
		Quota:    100000,
	}
	require.NoError(t, db.Create(&user).Error)

	// 设置一个会 panic 的回调，验证不影响主流程
	SetQuotaUpdateCallback(func(userId int, quota int) {
		panic("callback error")
	})
	defer SetQuotaUpdateCallback(nil)

	// 执行增加额度操作（回调 panic 不应影响数据库更新）
	err := IncreaseUserQuota(1002, 50000, true)
	require.NoError(t, err)

	// 验证数据库已更新（即使回调失败）
	var updated User
	require.NoError(t, db.First(&updated, 1002).Error)
	assert.Equal(t, 150000, updated.Quota)
}
