package model

import (
	"testing"

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

func setupWechatTopUpDB(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
	common.QuotaPerUnit = 500 * 1000.0

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

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

func TestRechargeWechat_Success(t *testing.T) {
	db := setupWechatTopUpDB(t)

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

	// 创建 pending 状态的充值订单
	expectedQuota := int(decimal.NewFromFloat(10.0).Mul(decimal.NewFromFloat(common.QuotaPerUnit)).IntPart())
	topUp := &TopUp{
		UserId:        200,
		Amount:        10,
		Money:         10.0, // 10 元
		TradeNo:       "wx_test_trade_001",
		PaymentMethod: "wechat_pay",
		CreateTime:    common.GetTimestamp(),
		Status:        common.TopUpStatusPending,
	}
	require.NoError(t, db.Create(topUp).Error)

	// 执行充值
	err := RechargeWechat("wx_test_trade_001")
	require.NoError(t, err)

	// 验证用户额度已增加
	var updatedUser User
	require.NoError(t, db.First(&updatedUser, 200).Error)
	assert.Equal(t, 100000+expectedQuota, updatedUser.Quota)

	// 验证订单状态已更新
	var updatedTopUp TopUp
	require.NoError(t, db.Where("trade_no = ?", "wx_test_trade_001").First(&updatedTopUp).Error)
	assert.Equal(t, common.TopUpStatusSuccess, updatedTopUp.Status)
	assert.NotZero(t, updatedTopUp.CompleteTime)
}

func TestRechargeWechat_OrderNotFound(t *testing.T) {
	setupWechatTopUpDB(t)

	err := RechargeWechat("non_existent_trade_no")
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "充值订单不存在")
}

func TestRechargeWechat_EmptyTradeNo(t *testing.T) {
	setupWechatTopUpDB(t)

	err := RechargeWechat("")
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "未提供支付单号")
}

func TestRechargeWechat_AlreadyCompleted(t *testing.T) {
	db := setupWechatTopUpDB(t)

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

	// 创建已完成的充值订单
	topUp := &TopUp{
		UserId:        201,
		Amount:        10,
		Money:         10.0,
		TradeNo:       "wx_test_trade_completed",
		PaymentMethod: "wechat_pay",
		CreateTime:    common.GetTimestamp(),
		CompleteTime:  common.GetTimestamp(),
		Status:        common.TopUpStatusSuccess,
	}
	require.NoError(t, db.Create(topUp).Error)

	// 再次充值应幂等返回成功
	err := RechargeWechat("wx_test_trade_completed")
	require.NoError(t, err)

	// 验证额度未变化
	var updatedUser User
	require.NoError(t, db.First(&updatedUser, 201).Error)
	assert.Equal(t, 100000, updatedUser.Quota) // 额度不变
}

func TestRechargeWechat_InvalidStatus(t *testing.T) {
	db := setupWechatTopUpDB(t)

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

	// 创建 expired 状态的订单
	topUp := &TopUp{
		UserId:        202,
		Amount:        10,
		Money:         10.0,
		TradeNo:       "wx_test_trade_expired",
		PaymentMethod: "wechat_pay",
		CreateTime:    common.GetTimestamp(),
		Status:        common.TopUpStatusExpired,
	}
	require.NoError(t, db.Create(topUp).Error)

	// 应返回状态错误
	err := RechargeWechat("wx_test_trade_expired")
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "充值订单状态错误")

	// 验证额度未变化
	var updatedUser User
	require.NoError(t, db.First(&updatedUser, 202).Error)
	assert.Equal(t, 100000, updatedUser.Quota)
}

func TestRechargeWechat_ConcurrentSafety(t *testing.T) {
	db := setupWechatTopUpDB(t)

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

	expectedQuota := int(decimal.NewFromFloat(10.0).Mul(decimal.NewFromFloat(common.QuotaPerUnit)).IntPart())

	// 创建 pending 订单
	topUp := &TopUp{
		UserId:        203,
		Amount:        10,
		Money:         10.0,
		TradeNo:       "wx_concurrent_trade",
		PaymentMethod: "wechat_pay",
		CreateTime:    common.GetTimestamp(),
		Status:        common.TopUpStatusPending,
	}
	require.NoError(t, db.Create(topUp).Error)

	// 并发调用 RechargeWechat，只有第一个应该成功充值
	done := make(chan error, 2)
	go func() { done <- RechargeWechat("wx_concurrent_trade") }()
	go func() { done <- RechargeWechat("wx_concurrent_trade") }()

	err1 := <-done
	err2 := <-done

	// 至少一个成功，不能两个都失败
	require.True(t, err1 == nil || err2 == nil, "至少一个并发请求应成功")

	// 验证额度只增加了一次（因为 SQLite 单连接，实际上是序列化的）
	var updatedUser User
	require.NoError(t, db.First(&updatedUser, 203).Error)
	assert.Equal(t, 100000+expectedQuota, updatedUser.Quota, "额度应只增加一次")
}

func TestRechargeWechat_QuotaCalculation(t *testing.T) {
	db := setupWechatTopUpDB(t)

	testCases := []struct {
		name         string
		money        float64
		initialQuota int
		expectedAdd  int
		userId       int
	}{
		{"整数金额", 7.0, 0, int(decimal.NewFromFloat(7.0).Mul(decimal.NewFromFloat(common.QuotaPerUnit)).IntPart()), 310},
		{"小数金额", 7.5, 0, int(decimal.NewFromFloat(7.5).Mul(decimal.NewFromFloat(common.QuotaPerUnit)).IntPart()), 311},
		{"最小金额", 0.01, 0, int(decimal.NewFromFloat(0.01).Mul(decimal.NewFromFloat(common.QuotaPerUnit)).IntPart()), 312},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			userId := tc.userId
			tradeNo := "wx_quota_test_" + tc.name

			// 创建用户
			user := User{
				Id:       userId,
				Username: "quota_test_" + tc.name,
				Password: "hashed",
				Quota:    tc.initialQuota,
				AffCode:  "aff_" + tc.name,
			}
			require.NoError(t, db.Create(&user).Error)

			// 创建订单
			topUp := &TopUp{
				UserId:        userId,
				Amount:        1,
				Money:         tc.money,
				TradeNo:       tradeNo,
				PaymentMethod: "wechat_pay",
				CreateTime:    common.GetTimestamp(),
				Status:        common.TopUpStatusPending,
			}
			require.NoError(t, db.Create(topUp).Error)

			err := RechargeWechat(tradeNo)
			require.NoError(t, err)

			var updatedUser User
			require.NoError(t, db.First(&updatedUser, userId).Error)
			assert.Equal(t, tc.initialQuota+tc.expectedAdd, updatedUser.Quota, tc.name)
		})
	}
}
