package customer_binding

import (
	"context"
	"fmt"
	"testing"

	"gorm.io/gorm"

	"github.com/break/junhong_cmp_fiber/internal/model"
)

// ---- 测试工具 ----

// bindKey 构建 mock 中使用的查找键
func bindKey(customerID uint, key string) string {
	return fmt.Sprintf("%d:%s", customerID, key)
}

// ---- mock 实现 ----

type mockCardReader struct {
	cards map[uint]*model.IotCard
}

func (m *mockCardReader) GetByID(_ context.Context, id uint) (*model.IotCard, error) {
	if c, ok := m.cards[id]; ok {
		return c, nil
	}
	return nil, gorm.ErrRecordNotFound
}

type mockDeviceReader struct {
	devices map[uint]*model.Device
}

func (m *mockDeviceReader) GetByID(_ context.Context, id uint) (*model.Device, error) {
	if d, ok := m.devices[id]; ok {
		return d, nil
	}
	return nil, gorm.ErrRecordNotFound
}

// mockPCDOps 模拟 tb_personal_customer_device 操作
type mockPCDOps struct {
	// bindings["customerID:virtualNo"] = true 表示有效（status=1）绑定
	bindings   map[string]bool
	created    []*model.PersonalCustomerDevice
	counts     map[string]int64              // virtualNo → count（用于首绑判断）
	allRecords []*model.PersonalCustomerDevice // GetByDeviceNo 和 UpdateStatus/UpdateVirtualNo 使用
}

func (m *mockPCDOps) ExistsByCustomerAndDevice(_ context.Context, customerID uint, deviceNo string) (bool, error) {
	return m.bindings[bindKey(customerID, deviceNo)], nil
}

func (m *mockPCDOps) Create(_ context.Context, record *model.PersonalCustomerDevice) error {
	m.created = append(m.created, record)
	return nil
}

func (m *mockPCDOps) CountByVirtualNo(_ context.Context, virtualNo string) (int64, error) {
	if m.counts != nil {
		return m.counts[virtualNo], nil
	}
	return 0, nil
}

func (m *mockPCDOps) GetByDeviceNo(_ context.Context, deviceNo string) ([]*model.PersonalCustomerDevice, error) {
	var result []*model.PersonalCustomerDevice
	for _, r := range m.allRecords {
		if r.VirtualNo == deviceNo {
			result = append(result, r)
		}
	}
	return result, nil
}

func (m *mockPCDOps) UpdateStatus(_ context.Context, id uint, status int) error {
	for _, r := range m.allRecords {
		if r.ID == id {
			r.Status = status
			return nil
		}
	}
	return nil
}

func (m *mockPCDOps) UpdateVirtualNo(_ context.Context, id uint, newVirtualNo string) error {
	for _, r := range m.allRecords {
		if r.ID == id {
			r.VirtualNo = newVirtualNo
			return nil
		}
	}
	return nil
}

// mockPCIOps 模拟 tb_personal_customer_iccid 操作
type mockPCIOps struct {
	bindings   map[string]bool
	created    []*model.PersonalCustomerICCID
	counts     map[string]int64              // iccid → count
	allRecords []*model.PersonalCustomerICCID // GetByICCID 和 UpdateStatus 使用
}

func (m *mockPCIOps) ExistsByCustomerAndICCID(_ context.Context, customerID uint, iccid string) (bool, error) {
	return m.bindings[bindKey(customerID, iccid)], nil
}

func (m *mockPCIOps) Create(_ context.Context, record *model.PersonalCustomerICCID) error {
	m.created = append(m.created, record)
	return nil
}

func (m *mockPCIOps) CountByICCID(_ context.Context, iccid string) (int64, error) {
	if m.counts != nil {
		return m.counts[iccid], nil
	}
	return 0, nil
}

func (m *mockPCIOps) GetByICCID(_ context.Context, iccid string) ([]*model.PersonalCustomerICCID, error) {
	var result []*model.PersonalCustomerICCID
	for _, r := range m.allRecords {
		if r.ICCID == iccid {
			result = append(result, r)
		}
	}
	return result, nil
}

func (m *mockPCIOps) UpdateStatus(_ context.Context, id uint, status int) error {
	for _, r := range m.allRecords {
		if r.ID == id {
			r.Status = status
			return nil
		}
	}
	return nil
}

// ---- 测试 Service 构造器 ----

// soldCalls 记录 markAsSold 调用
type soldCalls struct {
	items []string
}

func (s *soldCalls) mark(_ context.Context, _ *gorm.DB, assetType string, _ uint) error {
	s.items = append(s.items, assetType)
	return nil
}

func newTestService(cards cardReader, devices deviceReader, pcd pcdOps, pci pciOps) (*Service, *soldCalls) {
	sold := &soldCalls{}
	return &Service{
		cards:   cards,
		devices: devices,
		readCard: func(ctx context.Context, _ *gorm.DB, id uint) (*model.IotCard, error) {
			return cards.GetByID(ctx, id)
		},
		readDevice: func(ctx context.Context, _ *gorm.DB, id uint) (*model.Device, error) {
			return devices.GetByID(ctx, id)
		},
		makePCD:    func(_ *gorm.DB) pcdOps { return pcd },
		makePCI:    func(_ *gorm.DB) pciOps { return pci },
		markAsSold: sold.mark,
	}, sold
}

// ---- OwnsAsset 测试 ----

// 验证：有虚拟号卡 + 客户有有效绑定 → true（tracer bullet）
func TestOwnsAsset_有虚拟号卡_有效绑定_返回true(t *testing.T) {
	card := &model.IotCard{VirtualNo: "VN001"}
	card.ID = 1

	pcd := &mockPCDOps{
		bindings: map[string]bool{bindKey(10, "VN001"): true},
	}
	svc, _ := newTestService(
		&mockCardReader{cards: map[uint]*model.IotCard{1: card}},
		&mockDeviceReader{},
		pcd,
		&mockPCIOps{},
	)

	owned, err := svc.OwnsAsset(context.Background(), 10, "iot_card", 1)

	if err != nil {
		t.Fatalf("期望无错误，实际: %v", err)
	}
	if !owned {
		t.Fatal("期望返回 true，实际返回 false")
	}
}

// 验证：有虚拟号卡 + status=0 的绑定 → false（修复安全缺口）
func TestOwnsAsset_有虚拟号卡_禁用绑定_返回false(t *testing.T) {
	card := &model.IotCard{VirtualNo: "VN002"}
	card.ID = 2

	pcd := &mockPCDOps{
		// status=0 的绑定：在 ExistsByCustomerAndDevice 中会过滤掉（bindings 中不存在）
		bindings: map[string]bool{},
	}
	svc, _ := newTestService(
		&mockCardReader{cards: map[uint]*model.IotCard{2: card}},
		&mockDeviceReader{},
		pcd,
		&mockPCIOps{},
	)

	owned, err := svc.OwnsAsset(context.Background(), 10, "iot_card", 2)

	if err != nil {
		t.Fatalf("期望无错误，实际: %v", err)
	}
	if owned {
		t.Fatal("期望返回 false（status=0），实际返回 true")
	}
}

// 验证：有虚拟号卡 + 无绑定 → false
func TestOwnsAsset_有虚拟号卡_无绑定_返回false(t *testing.T) {
	card := &model.IotCard{VirtualNo: "VN003"}
	card.ID = 3

	svc, _ := newTestService(
		&mockCardReader{cards: map[uint]*model.IotCard{3: card}},
		&mockDeviceReader{},
		&mockPCDOps{bindings: map[string]bool{}},
		&mockPCIOps{},
	)

	owned, err := svc.OwnsAsset(context.Background(), 10, "iot_card", 3)

	if err != nil {
		t.Fatalf("期望无错误，实际: %v", err)
	}
	if owned {
		t.Fatal("期望返回 false（无绑定），实际返回 true")
	}
}

// 验证：assetType="card"（来自 assetService.Resolve）等价于 "iot_card"
func TestOwnsAsset_assetType_card_等价iot_card(t *testing.T) {
	card := &model.IotCard{VirtualNo: "VN_CARD"}
	card.ID = 9

	pcd := &mockPCDOps{
		bindings: map[string]bool{bindKey(10, "VN_CARD"): true},
	}
	svc, _ := newTestService(
		&mockCardReader{cards: map[uint]*model.IotCard{9: card}},
		&mockDeviceReader{},
		pcd,
		&mockPCIOps{},
	)

	owned, err := svc.OwnsAsset(context.Background(), 10, "card", 9)

	if err != nil {
		t.Fatalf("期望无错误，实际: %v", err)
	}
	if !owned {
		t.Fatal("期望 card 类型等价 iot_card 返回 true，实际 false")
	}
}

// 验证：设备资产 + 有效绑定 → true
func TestOwnsAsset_设备_有效绑定_返回true(t *testing.T) {
	device := &model.Device{VirtualNo: "DEV001"}
	device.ID = 5

	pcd := &mockPCDOps{
		bindings: map[string]bool{bindKey(10, "DEV001"): true},
	}
	svc, _ := newTestService(
		&mockCardReader{},
		&mockDeviceReader{devices: map[uint]*model.Device{5: device}},
		pcd,
		&mockPCIOps{},
	)

	owned, err := svc.OwnsAsset(context.Background(), 10, "device", 5)

	if err != nil {
		t.Fatalf("期望无错误，实际: %v", err)
	}
	if !owned {
		t.Fatal("期望返回 true，实际返回 false")
	}
}

// ---- Bind 测试 ----

// 验证：首次绑定有虚拟号卡 → 写入 pcd 记录 + 触发 markAssetAsSold
func TestBind_有虚拟号卡_首次绑定_创建记录并标记已售(t *testing.T) {
	card := &model.IotCard{VirtualNo: "VN010"}
	card.ID = 10

	pcd := &mockPCDOps{
		bindings: map[string]bool{},
		counts:   map[string]int64{"VN010": 0}, // 首次绑定
	}
	svc, sold := newTestService(
		&mockCardReader{cards: map[uint]*model.IotCard{10: card}},
		&mockDeviceReader{},
		pcd,
		&mockPCIOps{},
	)

	err := svc.Bind(context.Background(), nil, 20, "iot_card", 10)

	if err != nil {
		t.Fatalf("期望无错误，实际: %v", err)
	}
	if len(pcd.created) != 1 {
		t.Fatalf("期望创建 1 条 pcd 记录，实际: %d", len(pcd.created))
	}
	if pcd.created[0].VirtualNo != "VN010" {
		t.Errorf("期望 VirtualNo=VN010，实际: %s", pcd.created[0].VirtualNo)
	}
	if pcd.created[0].CustomerID != 20 {
		t.Errorf("期望 CustomerID=20，实际: %d", pcd.created[0].CustomerID)
	}
	if len(sold.items) != 1 || sold.items[0] != "iot_card" {
		t.Errorf("期望触发 markAssetAsSold(iot_card)，实际: %v", sold.items)
	}
}

// 验证：重复绑定 → 不创建新记录
func TestBind_有虚拟号卡_已有绑定_不重复创建(t *testing.T) {
	card := &model.IotCard{VirtualNo: "VN011"}
	card.ID = 11

	pcd := &mockPCDOps{
		bindings: map[string]bool{bindKey(20, "VN011"): true},
		counts:   map[string]int64{"VN011": 1},
	}
	svc, _ := newTestService(
		&mockCardReader{cards: map[uint]*model.IotCard{11: card}},
		&mockDeviceReader{},
		pcd,
		&mockPCIOps{},
	)

	err := svc.Bind(context.Background(), nil, 20, "iot_card", 11)

	if err != nil {
		t.Fatalf("期望无错误，实际: %v", err)
	}
	if len(pcd.created) != 0 {
		t.Fatalf("期望不创建新记录，实际创建了 %d 条", len(pcd.created))
	}
}

// 验证：非首次绑定（已有其他客户绑定）→ 创建记录但不触发 markAssetAsSold
func TestBind_有虚拟号卡_非首次绑定_创建记录不标记已售(t *testing.T) {
	card := &model.IotCard{VirtualNo: "VN012"}
	card.ID = 12

	pcd := &mockPCDOps{
		bindings: map[string]bool{},
		counts:   map[string]int64{"VN012": 1}, // 已有其他绑定
	}
	svc, sold := newTestService(
		&mockCardReader{cards: map[uint]*model.IotCard{12: card}},
		&mockDeviceReader{},
		pcd,
		&mockPCIOps{},
	)

	err := svc.Bind(context.Background(), nil, 30, "iot_card", 12)

	if err != nil {
		t.Fatalf("期望无错误，实际: %v", err)
	}
	if len(pcd.created) != 1 {
		t.Fatalf("期望创建 1 条记录，实际: %d", len(pcd.created))
	}
	if len(sold.items) != 0 {
		t.Errorf("期望不触发 markAssetAsSold，实际触发了: %v", sold.items)
	}
}

// ---- Issue 02: 无虚拟号卡 pci 路径测试 ----

// 验证：无虚拟号卡 + 客户有有效 PCI 绑定 → true
func TestOwnsAsset_无虚拟号卡_有效PCI绑定_返回true(t *testing.T) {
	card := &model.IotCard{ICCID: "89860000000000000001"} // VirtualNo 为空
	card.ID = 20

	pci := &mockPCIOps{
		bindings: map[string]bool{bindKey(10, "89860000000000000001"): true},
	}
	svc, _ := newTestService(
		&mockCardReader{cards: map[uint]*model.IotCard{20: card}},
		&mockDeviceReader{},
		&mockPCDOps{bindings: map[string]bool{}},
		pci,
	)

	owned, err := svc.OwnsAsset(context.Background(), 10, "iot_card", 20)

	if err != nil {
		t.Fatalf("期望无错误，实际: %v", err)
	}
	if !owned {
		t.Fatal("期望返回 true，实际返回 false")
	}
}

// 验证：无虚拟号卡 + 无 PCI 绑定 → false
func TestOwnsAsset_无虚拟号卡_无PCI绑定_返回false(t *testing.T) {
	card := &model.IotCard{ICCID: "89860000000000000002"} // VirtualNo 为空
	card.ID = 21

	svc, _ := newTestService(
		&mockCardReader{cards: map[uint]*model.IotCard{21: card}},
		&mockDeviceReader{},
		&mockPCDOps{bindings: map[string]bool{}},
		&mockPCIOps{bindings: map[string]bool{}},
	)

	owned, err := svc.OwnsAsset(context.Background(), 10, "iot_card", 21)

	if err != nil {
		t.Fatalf("期望无错误，实际: %v", err)
	}
	if owned {
		t.Fatal("期望返回 false（无 PCI 绑定），实际返回 true")
	}
}

// 验证：无虚拟号卡首次绑定 → 写入 pci 记录 + 触发 markAssetAsSold
func TestBind_无虚拟号卡_首次绑定_创建PCI记录并标记已售(t *testing.T) {
	card := &model.IotCard{ICCID: "89860000000000000010"} // VirtualNo 为空
	card.ID = 30

	pci := &mockPCIOps{
		bindings: map[string]bool{},
		counts:   map[string]int64{"89860000000000000010": 0}, // 首次绑定
	}
	svc, sold := newTestService(
		&mockCardReader{cards: map[uint]*model.IotCard{30: card}},
		&mockDeviceReader{},
		&mockPCDOps{bindings: map[string]bool{}},
		pci,
	)

	err := svc.Bind(context.Background(), nil, 50, "iot_card", 30)

	if err != nil {
		t.Fatalf("期望无错误，实际: %v", err)
	}
	if len(pci.created) != 1 {
		t.Fatalf("期望创建 1 条 pci 记录，实际: %d", len(pci.created))
	}
	if pci.created[0].ICCID != "89860000000000000010" {
		t.Errorf("期望 ICCID=89860000000000000010，实际: %s", pci.created[0].ICCID)
	}
	if pci.created[0].CustomerID != 50 {
		t.Errorf("期望 CustomerID=50，实际: %d", pci.created[0].CustomerID)
	}
	if len(sold.items) != 1 || sold.items[0] != "iot_card" {
		t.Errorf("期望触发 markAssetAsSold(iot_card)，实际: %v", sold.items)
	}
}

// 验证：无虚拟号卡已有绑定 → 不重复创建
func TestBind_无虚拟号卡_已有绑定_不重复创建(t *testing.T) {
	card := &model.IotCard{ICCID: "89860000000000000011"} // VirtualNo 为空
	card.ID = 31

	pci := &mockPCIOps{
		bindings: map[string]bool{bindKey(50, "89860000000000000011"): true},
		counts:   map[string]int64{"89860000000000000011": 1},
	}
	svc, _ := newTestService(
		&mockCardReader{cards: map[uint]*model.IotCard{31: card}},
		&mockDeviceReader{},
		&mockPCDOps{bindings: map[string]bool{}},
		pci,
	)

	err := svc.Bind(context.Background(), nil, 50, "iot_card", 31)

	if err != nil {
		t.Fatalf("期望无错误，实际: %v", err)
	}
	if len(pci.created) != 0 {
		t.Fatalf("期望不创建新记录，实际创建了 %d 条", len(pci.created))
	}
}

// ---- Issue 03: Migrate 迁移测试 ----

// newMockPCDRecord 构建一条带 ID 的 pcd 记录（用于迁移测试）
func newMockPCDRecord(id, customerID uint, virtualNo string, status int) *model.PersonalCustomerDevice {
	r := &model.PersonalCustomerDevice{
		CustomerID: customerID,
		VirtualNo:  virtualNo,
		Status:     status,
	}
	r.ID = id
	return r
}

// newMockPCIRecord 构建一条带 ID 的 pci 记录（用于迁移测试）
func newMockPCIRecord(id, customerID uint, iccid string, status int) *model.PersonalCustomerICCID {
	r := &model.PersonalCustomerICCID{
		CustomerID: customerID,
		ICCID:      iccid,
		Status:     status,
	}
	r.ID = id
	return r
}

// 验证：旧卡有虚拟号 + pcd 有绑定 + 新卡有虚拟号 → pcd.virtual_no 更新为新卡虚拟号
func TestMigrate_有虚拟号旧卡_有绑定_换有虚拟号新卡_更新VirtualNo(t *testing.T) {
	oldCard := &model.IotCard{VirtualNo: "OLD_VN"}
	oldCard.ID = 100
	newCard := &model.IotCard{VirtualNo: "NEW_VN"}
	newCard.ID = 101

	existing := newMockPCDRecord(1, 50, "OLD_VN", 1)
	pcd := &mockPCDOps{
		bindings:   map[string]bool{},
		allRecords: []*model.PersonalCustomerDevice{existing},
	}
	svc, _ := newTestService(
		&mockCardReader{cards: map[uint]*model.IotCard{100: oldCard, 101: newCard}},
		&mockDeviceReader{},
		pcd,
		&mockPCIOps{},
	)

	err := svc.Migrate(context.Background(), nil, "iot_card", 100, "iot_card", 101)

	if err != nil {
		t.Fatalf("期望无错误，实际: %v", err)
	}
	if existing.VirtualNo != "NEW_VN" {
		t.Errorf("期望 VirtualNo 更新为 NEW_VN，实际: %s", existing.VirtualNo)
	}
}

// 验证：旧卡有虚拟号 + pcd 有绑定 + 新卡无虚拟号 → 旧 pcd status=0，新 pci 创建
func TestMigrate_有虚拟号旧卡_有绑定_换无虚拟号新卡_迁移到PCI(t *testing.T) {
	oldCard := &model.IotCard{VirtualNo: "OLD_VN2"}
	oldCard.ID = 110
	newCard := &model.IotCard{ICCID: "89860000000000000099"} // 无虚拟号
	newCard.ID = 111

	existing := newMockPCDRecord(2, 60, "OLD_VN2", 1)
	pcd := &mockPCDOps{
		allRecords: []*model.PersonalCustomerDevice{existing},
	}
	pci := &mockPCIOps{}
	svc, _ := newTestService(
		&mockCardReader{cards: map[uint]*model.IotCard{110: oldCard, 111: newCard}},
		&mockDeviceReader{},
		pcd,
		pci,
	)

	err := svc.Migrate(context.Background(), nil, "iot_card", 110, "iot_card", 111)

	if err != nil {
		t.Fatalf("期望无错误，实际: %v", err)
	}
	if existing.Status != 0 {
		t.Errorf("期望旧 pcd 记录 status=0，实际: %d", existing.Status)
	}
	if len(pci.created) != 1 {
		t.Fatalf("期望创建 1 条 pci 记录，实际: %d", len(pci.created))
	}
	if pci.created[0].CustomerID != 60 {
		t.Errorf("期望 pci CustomerID=60，实际: %d", pci.created[0].CustomerID)
	}
	if pci.created[0].ICCID != "89860000000000000099" {
		t.Errorf("期望 pci ICCID=89860000000000000099，实际: %s", pci.created[0].ICCID)
	}
}

// 验证：旧卡有虚拟号 + pcd 无绑定 + 新卡无虚拟号 → 跳过，无写入
func TestMigrate_有虚拟号旧卡_无绑定_换无虚拟号新卡_跳过(t *testing.T) {
	oldCard := &model.IotCard{VirtualNo: "OLD_VN3"}
	oldCard.ID = 120
	newCard := &model.IotCard{ICCID: "89860000000000000088"} // 无虚拟号
	newCard.ID = 121

	pcd := &mockPCDOps{allRecords: []*model.PersonalCustomerDevice{}} // 空
	pci := &mockPCIOps{}
	svc, _ := newTestService(
		&mockCardReader{cards: map[uint]*model.IotCard{120: oldCard, 121: newCard}},
		&mockDeviceReader{},
		pcd,
		pci,
	)

	err := svc.Migrate(context.Background(), nil, "iot_card", 120, "iot_card", 121)

	if err != nil {
		t.Fatalf("期望无错误，实际: %v", err)
	}
	if len(pci.created) != 0 {
		t.Fatalf("期望无写入，实际创建了 %d 条 pci 记录", len(pci.created))
	}
}

// 验证：旧卡无虚拟号 + pci 有绑定 + 新卡有虚拟号 → 旧 pci status=0，新 pcd 创建
func TestMigrate_无虚拟号旧卡_有绑定_换有虚拟号新卡_迁移到PCD(t *testing.T) {
	oldCard := &model.IotCard{ICCID: "89860000000000000077"} // 无虚拟号
	oldCard.ID = 130
	newCard := &model.IotCard{VirtualNo: "NEW_VN3"}
	newCard.ID = 131

	existingPCI := newMockPCIRecord(3, 70, "89860000000000000077", 1)
	pci := &mockPCIOps{allRecords: []*model.PersonalCustomerICCID{existingPCI}}
	pcd := &mockPCDOps{}
	svc, _ := newTestService(
		&mockCardReader{cards: map[uint]*model.IotCard{130: oldCard, 131: newCard}},
		&mockDeviceReader{},
		pcd,
		pci,
	)

	err := svc.Migrate(context.Background(), nil, "iot_card", 130, "iot_card", 131)

	if err != nil {
		t.Fatalf("期望无错误，实际: %v", err)
	}
	if existingPCI.Status != 0 {
		t.Errorf("期望旧 pci 记录 status=0，实际: %d", existingPCI.Status)
	}
	if len(pcd.created) != 1 {
		t.Fatalf("期望创建 1 条 pcd 记录，实际: %d", len(pcd.created))
	}
	if pcd.created[0].CustomerID != 70 {
		t.Errorf("期望 pcd CustomerID=70，实际: %d", pcd.created[0].CustomerID)
	}
	if pcd.created[0].VirtualNo != "NEW_VN3" {
		t.Errorf("期望 pcd VirtualNo=NEW_VN3，实际: %s", pcd.created[0].VirtualNo)
	}
}

// 验证：旧卡无虚拟号 + pci 有绑定 + 新卡也无虚拟号 → 旧 pci status=0，新 pci 创建新 ICCID
func TestMigrate_无虚拟号旧卡_有绑定_换无虚拟号新卡_迁移PCI(t *testing.T) {
	oldCard := &model.IotCard{ICCID: "89860000000000000066"} // 无虚拟号
	oldCard.ID = 140
	newCard := &model.IotCard{ICCID: "89860000000000000055"} // 无虚拟号
	newCard.ID = 141

	existingPCI := newMockPCIRecord(4, 80, "89860000000000000066", 1)
	pci := &mockPCIOps{allRecords: []*model.PersonalCustomerICCID{existingPCI}}
	pcd := &mockPCDOps{}
	svc, _ := newTestService(
		&mockCardReader{cards: map[uint]*model.IotCard{140: oldCard, 141: newCard}},
		&mockDeviceReader{},
		pcd,
		pci,
	)

	err := svc.Migrate(context.Background(), nil, "iot_card", 140, "iot_card", 141)

	if err != nil {
		t.Fatalf("期望无错误，实际: %v", err)
	}
	if existingPCI.Status != 0 {
		t.Errorf("期望旧 pci 记录 status=0，实际: %d", existingPCI.Status)
	}
	if len(pci.created) != 1 {
		t.Fatalf("期望创建 1 条新 pci 记录，实际: %d", len(pci.created))
	}
	if pci.created[0].CustomerID != 80 {
		t.Errorf("期望 pci CustomerID=80，实际: %d", pci.created[0].CustomerID)
	}
	if pci.created[0].ICCID != "89860000000000000055" {
		t.Errorf("期望 pci ICCID=89860000000000000055，实际: %s", pci.created[0].ICCID)
	}
}

// 验证：无虚拟号卡非首次绑定（已有其他客户）→ 创建记录但不触发 markAssetAsSold
func TestBind_无虚拟号卡_非首次绑定_创建记录不标记已售(t *testing.T) {
	card := &model.IotCard{ICCID: "89860000000000000012"} // VirtualNo 为空
	card.ID = 32

	pci := &mockPCIOps{
		bindings: map[string]bool{},
		counts:   map[string]int64{"89860000000000000012": 1}, // 已有其他客户绑定
	}
	svc, sold := newTestService(
		&mockCardReader{cards: map[uint]*model.IotCard{32: card}},
		&mockDeviceReader{},
		&mockPCDOps{bindings: map[string]bool{}},
		pci,
	)

	err := svc.Bind(context.Background(), nil, 60, "iot_card", 32)

	if err != nil {
		t.Fatalf("期望无错误，实际: %v", err)
	}
	if len(pci.created) != 1 {
		t.Fatalf("期望创建 1 条记录，实际: %d", len(pci.created))
	}
	if len(sold.items) != 0 {
		t.Errorf("期望不触发 markAssetAsSold，实际触发了: %v", sold.items)
	}
}
