package order

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

// TestSynchronousPurchasePersistsImmutableTermsSnapshots 验证同步主套餐和加油包固化购买时计时条款。
func TestSynchronousPurchasePersistsImmutableTermsSnapshots(t *testing.T) {
	tx := testutil.NewPostgresTransaction(t)
	card := createOrderTermsCard(t, tx, constants.RealNameStatusNotVerified)
	formal := createOrderTermsPackage(t, tx, constants.PackageTypeFormal, constants.PackageExpiryBaseFromActivation, 45)
	addon := createOrderTermsPackage(t, tx, constants.PackageTypeAddon, constants.PackageExpiryBaseFromActivation, 10)
	shopID := uint(time.Now().UnixNano() & testIDMask)
	override := constants.PackageExpiryBaseFromPurchase
	for _, packageID := range []uint{formal.ID, addon.ID} {
		allocation := &model.ShopPackageAllocation{ShopID: shopID, PackageID: packageID, CostPrice: 1, RetailPrice: 2, Status: constants.StatusEnabled, ShelfStatus: 1, ExpiryBaseOverride: &override}
		if err := tx.Create(allocation).Error; err != nil {
			t.Fatalf("创建套餐覆盖配置失败：%v", err)
		}
	}
	service := &Service{shopPackageAllocationStore: postgres.NewShopPackageAllocationStore(tx), logger: zap.NewNop()}
	now := time.Date(2026, 7, 22, 10, 0, 0, 0, time.Local)
	formalOrder := newOrderTermsOrder(formal.ID, card.ID, &shopID)
	if err := service.activateMainPackage(context.Background(), tx, formalOrder, formal, constants.AssetWalletResourceTypeIotCard, card.ID, now); err != nil {
		t.Fatalf("同步创建主套餐失败：%v", err)
	}
	addonOrder := newOrderTermsOrder(addon.ID, card.ID, &shopID)
	if err := service.activateAddonPackage(context.Background(), tx, addonOrder, addon, constants.AssetWalletResourceTypeIotCard, card.ID, now.Add(time.Minute)); err != nil {
		t.Fatalf("同步创建加油包失败：%v", err)
	}

	for _, item := range []struct {
		orderID     uint
		packageID   uint
		durationDay int
	}{
		{formalOrder.ID, formal.ID, 45},
		{addonOrder.ID, addon.ID, 10},
	} {
		var usage model.PackageUsage
		if err := tx.Where("order_id = ? AND package_id = ?", item.orderID, item.packageID).First(&usage).Error; err != nil {
			t.Fatalf("查询套餐使用记录失败：%v", err)
		}
		if usage.ExpiryBaseSnapshot != constants.PackageExpiryBaseFromPurchase || usage.CalendarTypeSnapshot != constants.PackageCalendarTypeByDay || usage.DurationDaysSnapshot != item.durationDay {
			t.Fatalf("同步购买快照错误：%+v", usage)
		}
	}

	if err := tx.Model(&model.Package{}).Where("id IN ?", []uint{formal.ID, addon.ID}).Updates(map[string]any{"expiry_base": constants.PackageExpiryBaseFromActivation, "duration_days": 99}).Error; err != nil {
		t.Fatalf("修改套餐当前配置失败：%v", err)
	}
	if err := tx.Model(&model.ShopPackageAllocation{}).Where("shop_id = ?", shopID).Update("expiry_base_override", nil).Error; 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 || usages[0].ExpiryBaseSnapshot != constants.PackageExpiryBaseFromPurchase || usages[1].ExpiryBaseSnapshot != constants.PackageExpiryBaseFromPurchase || usages[0].DurationDaysSnapshot == 99 || usages[1].DurationDaysSnapshot == 99 {
		t.Fatalf("购买后配置变化不应修改快照：%+v", usages)
	}
}

// TestSynchronousPurchaseRejectsInvalidTermsWithoutUsage 验证非法配置不会留下空快照使用记录。
func TestSynchronousPurchaseRejectsInvalidTermsWithoutUsage(t *testing.T) {
	tx := testutil.NewPostgresTransaction(t)
	card := createOrderTermsCard(t, tx, constants.RealNameStatusVerified)
	pkg := createOrderTermsPackage(t, tx, constants.PackageTypeFormal, "invalid", 30)
	order := newOrderTermsOrder(pkg.ID, card.ID, nil)
	service := &Service{shopPackageAllocationStore: postgres.NewShopPackageAllocationStore(tx), logger: zap.NewNop()}
	if err := service.activateMainPackage(context.Background(), tx, order, pkg, constants.AssetWalletResourceTypeIotCard, card.ID, time.Now()); 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)
	}
}

// TestCEndPurchasePersistsSnapshotAndActivatesImmediately 验证 C 端购买无论 expiry_base 如何均写入快照并立即激活。
func TestCEndPurchasePersistsSnapshotAndActivatesImmediately(t *testing.T) {
	tx := testutil.NewPostgresTransaction(t)
	// C 端购买前已做实名前置检查；测试用未实名卡以区分"C 端即时激活"与"实名才激活"
	card := createOrderTermsCard(t, tx, constants.RealNameStatusNotVerified)
	pkg := createOrderTermsPackage(t, tx, constants.PackageTypeFormal, constants.PackageExpiryBaseFromActivation, 20)
	service := &Service{shopPackageAllocationStore: postgres.NewShopPackageAllocationStore(tx), logger: zap.NewNop()}
	now := time.Now()
	order := &model.Order{
		Model:       gorm.Model{ID: uint(time.Now().UnixNano() & testIDMask)},
		OrderNo:     "UR55-CEND-" + strconv.FormatInt(time.Now().UnixNano(), 10),
		OrderType:   model.OrderTypeSingleCard,
		BuyerType:   model.BuyerTypePersonal,
		IotCardID:   &card.ID,
		TotalAmount: 100,
		Generation:  1,
	}
	if err := service.activateMainPackage(context.Background(), tx, order, pkg, constants.AssetWalletResourceTypeIotCard, card.ID, now); err != nil {
		t.Fatalf("C 端购买创建主套餐失败：%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("查询 C 端购买使用记录失败：%v", err)
	}
	// 快照必须完整
	if usage.ExpiryBaseSnapshot != constants.PackageExpiryBaseFromActivation || usage.CalendarTypeSnapshot != constants.PackageCalendarTypeByDay || usage.DurationDaysSnapshot != 20 {
		t.Fatalf("C 端购买快照错误：%+v", usage)
	}
	// C 端购买不等实名，必须立即激活
	if usage.Status != constants.PackageUsageStatusActive || usage.PendingRealnameActivation {
		t.Fatalf("C 端购买必须立即激活：status=%d pendingRealname=%v", usage.Status, usage.PendingRealnameActivation)
	}
}

// TestPlatformPurchaseWithoutAllocationPersistsPackageDefaultSnapshot 验证无分配配置时使用套餐默认值写入快照。
func TestPlatformPurchaseWithoutAllocationPersistsPackageDefaultSnapshot(t *testing.T) {
	tx := testutil.NewPostgresTransaction(t)
	card := createOrderTermsCard(t, tx, constants.RealNameStatusVerified)
	pkg := createOrderTermsPackage(t, tx, constants.PackageTypeFormal, constants.PackageExpiryBaseFromPurchase, 15)
	service := &Service{shopPackageAllocationStore: postgres.NewShopPackageAllocationStore(tx), logger: zap.NewNop()}
	// sellerShopID=nil 模拟平台后台代购（无代理分配）
	order := newOrderTermsOrder(pkg.ID, card.ID, nil)
	if err := service.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)
	}
	// 无分配时快照应取套餐默认值 from_purchase
	if usage.ExpiryBaseSnapshot != constants.PackageExpiryBaseFromPurchase || usage.DurationDaysSnapshot != 15 {
		t.Fatalf("平台代购快照应使用套餐默认值：%+v", usage)
	}
}

func createOrderTermsCard(t *testing.T, tx *gorm.DB, realnameStatus int) *model.IotCard {
	t.Helper()
	suffix := strconv.FormatInt(time.Now().UnixNano()%100000000000000000, 10)
	iccid := "89" + 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 createOrderTermsPackage(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-ORDER-" + 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 newOrderTermsOrder(packageID, cardID uint, sellerShopID *uint) *model.Order {
	orderID := uint(time.Now().UnixNano() & testIDMask)
	return &model.Order{Model: gorm.Model{ID: orderID}, OrderNo: "UR55-ORDER-" + strconv.FormatUint(uint64(orderID), 10), OrderType: model.OrderTypeSingleCard, BuyerType: model.BuyerTypeAgent, IotCardID: &cardID, SellerShopID: sellerShopID, TotalAmount: 100, Generation: 1}
}
