package task

import (
	"context"
	"strconv"
	"testing"
	"time"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/store/postgres"
	"github.com/break/junhong_cmp_fiber/internal/testutil"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

// testIDMask 用于将纳秒时间戳截断为合法的 uint 主键范围，仅限测试用。
const testIDMask = 0x7fffffff

// TestAutoPurchasePersistsTermsSnapshotsAndRealnameDecision 验证自动购包复用快照并遵守实名起算规则。
func TestAutoPurchasePersistsTermsSnapshotsAndRealnameDecision(t *testing.T) {
	testCases := []struct {
		name            string
		defaultBase     string
		override        *string
		realnameStatus  int
		expectedBase    string
		expectedStatus  int
		expectedPending bool
	}{
		{name: "跟随默认等待实名", defaultBase: constants.PackageExpiryBaseFromActivation, realnameStatus: constants.RealNameStatusNotVerified, expectedBase: constants.PackageExpiryBaseFromActivation, expectedStatus: constants.PackageUsageStatusPending, expectedPending: true},
		{name: "覆盖购买即生效", defaultBase: constants.PackageExpiryBaseFromActivation, override: testutil.StringPointer(constants.PackageExpiryBaseFromPurchase), realnameStatus: constants.RealNameStatusNotVerified, expectedBase: constants.PackageExpiryBaseFromPurchase, expectedStatus: constants.PackageUsageStatusActive},
		{name: "覆盖实名但已实名", defaultBase: constants.PackageExpiryBaseFromPurchase, override: testutil.StringPointer(constants.PackageExpiryBaseFromActivation), realnameStatus: constants.RealNameStatusVerified, expectedBase: constants.PackageExpiryBaseFromActivation, expectedStatus: constants.PackageUsageStatusActive},
	}
	for _, testCase := range testCases {
		t.Run(testCase.name, func(t *testing.T) {
			tx := testutil.NewPostgresTransaction(t)
			card := createAutoPurchaseCard(t, tx, testCase.realnameStatus)
			pkg := createAutoPurchasePackage(t, tx, constants.PackageTypeFormal, testCase.defaultBase, 31)
			shopID := uint(time.Now().UnixNano() & testIDMask)
			if testCase.override != nil {
				allocation := &model.ShopPackageAllocation{ShopID: shopID, PackageID: pkg.ID, CostPrice: 1, RetailPrice: 2, Status: constants.StatusEnabled, ShelfStatus: 1, ExpiryBaseOverride: testCase.override}
				if err := tx.Create(allocation).Error; err != nil {
					t.Fatalf("创建套餐覆盖配置失败：%v", err)
				}
			}
			handler := &AutoPurchaseHandler{db: tx, shopPackageAllocationStore: postgres.NewShopPackageAllocationStore(tx), logger: zap.NewNop()}
			order := newAutoPurchaseOrder(card.ID, &shopID)
			if err := handler.activateMainPackage(context.Background(), tx, order, pkg, constants.AssetWalletResourceTypeIotCard, card.ID, time.Now()); err != nil {
				t.Fatalf("自动购包创建主套餐失败：%v", err)
			}
			var usage model.PackageUsage
			if err := tx.Where("order_id = ? AND package_id = ?", order.ID, pkg.ID).First(&usage).Error; err != nil {
				t.Fatalf("查询自动购包记录失败：%v", err)
			}
			if usage.ExpiryBaseSnapshot != testCase.expectedBase || usage.CalendarTypeSnapshot != constants.PackageCalendarTypeByDay || usage.DurationDaysSnapshot != 31 || usage.Status != testCase.expectedStatus || usage.PendingRealnameActivation != testCase.expectedPending {
				t.Fatalf("自动购包快照或实名决策错误：%+v", usage)
			}
		})
	}
}

// TestAutoPurchaseDefaultFromPurchaseActivatesImmediatelyWithoutRealname 验证套餐默认为 from_purchase 时自动购包不需要实名即激活。
func TestAutoPurchaseDefaultFromPurchaseActivatesImmediatelyWithoutRealname(t *testing.T) {
	tx := testutil.NewPostgresTransaction(t)
	// 卡未实名，但套餐默认 from_purchase，应立即激活
	card := createAutoPurchaseCard(t, tx, constants.RealNameStatusNotVerified)
	pkg := createAutoPurchasePackage(t, tx, constants.PackageTypeFormal, constants.PackageExpiryBaseFromPurchase, 14)
	handler := &AutoPurchaseHandler{db: tx, shopPackageAllocationStore: postgres.NewShopPackageAllocationStore(tx), logger: zap.NewNop()}
	order := newAutoPurchaseOrder(card.ID, nil)
	if err := handler.activateMainPackage(context.Background(), tx, order, pkg, constants.AssetWalletResourceTypeIotCard, card.ID, time.Now()); err != nil {
		t.Fatalf("from_purchase 默认值自动购包失败：%v", err)
	}
	var usage model.PackageUsage
	if err := tx.Where("order_id = ? AND package_id = ?", order.ID, pkg.ID).First(&usage).Error; err != nil {
		t.Fatalf("查询自动购包记录失败：%v", err)
	}
	if usage.ExpiryBaseSnapshot != constants.PackageExpiryBaseFromPurchase || usage.DurationDaysSnapshot != 14 {
		t.Fatalf("from_purchase 默认值快照错误：%+v", usage)
	}
	if usage.Status != constants.PackageUsageStatusActive || usage.PendingRealnameActivation {
		t.Fatalf("from_purchase 默认值未实名仍应立即激活：status=%d pendingRealname=%v", usage.Status, usage.PendingRealnameActivation)
	}
}

// TestAutoPurchaseMainAndAddonAreIdempotentByBusinessFact 验证主套餐、加油包和重复消费依赖数据库事实去重。
func TestAutoPurchaseMainAndAddonAreIdempotentByBusinessFact(t *testing.T) {
	tx := testutil.NewPostgresTransaction(t)
	card := createAutoPurchaseCard(t, tx, constants.RealNameStatusVerified)
	formal := createAutoPurchasePackage(t, tx, constants.PackageTypeFormal, constants.PackageExpiryBaseFromPurchase, 30)
	addon := createAutoPurchasePackage(t, tx, constants.PackageTypeAddon, constants.PackageExpiryBaseFromActivation, 7)
	handler := &AutoPurchaseHandler{db: tx, shopPackageAllocationStore: postgres.NewShopPackageAllocationStore(tx), logger: zap.NewNop()}
	formalOrder := newAutoPurchaseOrder(card.ID, nil)
	addonOrder := newAutoPurchaseOrder(card.ID, nil)
	now := time.Now()
	if err := handler.activatePackages(context.Background(), tx, formalOrder, []*model.Package{formal}, now); err != nil {
		t.Fatalf("首次自动创建主套餐失败：%v", err)
	}
	if err := handler.activatePackages(context.Background(), tx, formalOrder, []*model.Package{formal}, now); err != nil {
		t.Fatalf("重复消费应按业务事实幂等：%v", err)
	}
	if err := handler.activatePackages(context.Background(), tx, addonOrder, []*model.Package{addon}, now.Add(time.Minute)); err != nil {
		t.Fatalf("自动创建加油包失败：%v", err)
	}
	var usages []model.PackageUsage
	if err := tx.Where("order_id IN ?", []uint{formalOrder.ID, addonOrder.ID}).Order("order_id").Find(&usages).Error; err != nil {
		t.Fatalf("查询自动购包记录失败：%v", err)
	}
	if len(usages) != 2 {
		t.Fatalf("重复消费只应形成一组记录，实际 %d", len(usages))
	}
	for _, usage := range usages {
		if usage.ExpiryBaseSnapshot == "" || usage.CalendarTypeSnapshot == "" {
			t.Fatalf("自动购包不得产生空快照：%+v", usage)
		}
	}
	if usages[1].MasterUsageID == nil {
		t.Fatalf("加油包必须关联主套餐：%+v", usages[1])
	}
}

// TestAutoPurchaseTransactionRollbackOnInvalidAddonTerms 验证同一事务中后续套餐失败时不留下部分使用记录。
func TestAutoPurchaseTransactionRollbackOnInvalidAddonTerms(t *testing.T) {
	tx := testutil.NewPostgresTransaction(t)
	card := createAutoPurchaseCard(t, tx, constants.RealNameStatusVerified)
	formal := createAutoPurchasePackage(t, tx, constants.PackageTypeFormal, constants.PackageExpiryBaseFromPurchase, 30)
	invalidAddon := createAutoPurchasePackage(t, tx, constants.PackageTypeAddon, "invalid", 7)
	handler := &AutoPurchaseHandler{db: tx, shopPackageAllocationStore: postgres.NewShopPackageAllocationStore(tx), logger: zap.NewNop()}
	order := newAutoPurchaseOrder(card.ID, nil)
	err := tx.Transaction(func(inner *gorm.DB) error {
		return handler.activatePackages(context.Background(), inner, order, []*model.Package{formal, invalidAddon}, time.Now())
	})
	if err == nil {
		t.Fatal("非法加油包计时条款必须使业务事务失败")
	}
	var count int64
	if err := tx.Model(&model.PackageUsage{}).Where("order_id = ?", order.ID).Count(&count).Error; err != nil || count != 0 {
		t.Fatalf("事务失败不应留下部分主套餐：count=%d err=%v", count, err)
	}
}

// TestAutoPurchaseConfigChangeDoesNotModifySnapshot 验证自动购包后修改套餐配置不改变已有使用记录的计时快照。
func TestAutoPurchaseConfigChangeDoesNotModifySnapshot(t *testing.T) {
	tx := testutil.NewPostgresTransaction(t)
	card := createAutoPurchaseCard(t, tx, constants.RealNameStatusVerified)
	pkg := createAutoPurchasePackage(t, tx, constants.PackageTypeFormal, constants.PackageExpiryBaseFromPurchase, 30)
	handler := &AutoPurchaseHandler{db: tx, shopPackageAllocationStore: postgres.NewShopPackageAllocationStore(tx), logger: zap.NewNop()}
	order := newAutoPurchaseOrder(card.ID, nil)
	if err := handler.activateMainPackage(context.Background(), tx, order, pkg, constants.AssetWalletResourceTypeIotCard, card.ID, time.Now()); err != nil {
		t.Fatalf("自动购包创建主套餐失败：%v", err)
	}
	// 购买后修改套餐的生效条件和时长
	if err := tx.Model(pkg).Updates(map[string]any{"expiry_base": constants.PackageExpiryBaseFromActivation, "duration_days": 99}).Error; err != nil {
		t.Fatalf("修改套餐配置失败：%v", err)
	}
	var usage model.PackageUsage
	if err := tx.Where("order_id = ? AND package_id = ?", order.ID, pkg.ID).First(&usage).Error; err != nil {
		t.Fatalf("查询自动购包记录失败：%v", err)
	}
	// 快照应保留购买时的值，不随套餐修改而变化
	if usage.ExpiryBaseSnapshot != constants.PackageExpiryBaseFromPurchase || usage.DurationDaysSnapshot != 30 {
		t.Fatalf("套餐配置变更不应影响已购买快照：expiryBase=%s days=%d", usage.ExpiryBaseSnapshot, usage.DurationDaysSnapshot)
	}
}

func createAutoPurchaseCard(t *testing.T, tx *gorm.DB, realnameStatus int) *model.IotCard {
	t.Helper()
	suffix := strconv.FormatInt(time.Now().UnixNano()%100000000000000000, 10)
	iccid := "88" + suffix
	if len(iccid) < 19 {
		iccid += "0000000000000000000"[:19-len(iccid)]
	}
	iccid = iccid[:19]
	card := &model.IotCard{ICCID: iccid, ICCID19: iccid, CarrierID: 1, RealNameStatus: realnameStatus, AssetStatus: constants.AssetStatusInStock, Generation: 1}
	if err := tx.Create(card).Error; err != nil {
		t.Fatalf("创建自动购包测试卡失败：%v", err)
	}
	return card
}

func createAutoPurchasePackage(t *testing.T, tx *gorm.DB, packageType, expiryBase string, durationDays int) *model.Package {
	t.Helper()
	suffix := strconv.FormatInt(time.Now().UnixNano(), 10)
	pkg := &model.Package{PackageCode: "UR55-AUTO-" + suffix, PackageName: "UR55自动购包测试套餐", PackageType: packageType, DurationMonths: 1, DurationDays: durationDays, CalendarType: constants.PackageCalendarTypeByDay, ExpiryBase: expiryBase, Status: constants.StatusEnabled, ShelfStatus: 1, DataResetCycle: "monthly"}
	if err := tx.Create(pkg).Error; err != nil {
		t.Fatalf("创建自动购包测试套餐失败：%v", err)
	}
	return pkg
}

func newAutoPurchaseOrder(cardID uint, sellerShopID *uint) *model.Order {
	orderID := uint(time.Now().UnixNano() & testIDMask)
	return &model.Order{Model: gorm.Model{ID: orderID}, OrderNo: "UR55-AUTO-" + strconv.FormatUint(uint64(orderID), 10), OrderType: model.OrderTypeSingleCard, IotCardID: &cardID, SellerShopID: sellerShopID, TotalAmount: 100, Generation: 1}
}
