package exchange

import (
	"context"
	"fmt"
	"testing"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/model/dto"
	customerBindingSvc "github.com/break/junhong_cmp_fiber/internal/service/customer_binding"
	"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"
)

// TestResolveAssetByIdentifierUsesAuthoritativeSnapshot 验证任意受支持标识都生成权威换货快照。
func TestResolveAssetByIdentifierUsesAuthoritativeSnapshot(t *testing.T) {
	tx := testutil.NewPostgresTransaction(t)
	service := &Service{
		iotCardStore: postgres.NewIotCardStore(tx, nil),
		deviceStore:  postgres.NewDeviceStore(tx, nil),
	}
	ctx := context.Background()

	card := &model.IotCard{ICCID: "89860012345678901234", ICCID19: "8986001234567890123", MSISDN: "13800138000", VirtualNo: "UR45-CARD", AssetStatus: constants.AssetStatusInStock}
	if err := tx.Create(card).Error; err != nil {
		t.Fatalf("创建测试卡失败：%v", err)
	}
	for _, identifier := range []string{card.ICCID, card.MSISDN, card.VirtualNo} {
		asset, err := service.resolveAssetByIdentifier(ctx, constants.ExchangeAssetTypeIotCard, identifier)
		if err != nil {
			t.Fatalf("通过 %s 解析测试卡失败：%v", identifier, err)
		}
		if asset.Identifier != card.ICCID {
			t.Fatalf("卡快照应为 ICCID，输入 %s，实际 %s", identifier, asset.Identifier)
		}
	}

	device := &model.Device{VirtualNo: "UR45-DEVICE", IMEI: "860000000000001", SN: "UR45-SN-1", AssetStatus: constants.AssetStatusInStock}
	if err := tx.Create(device).Error; err != nil {
		t.Fatalf("创建设备失败：%v", err)
	}
	asset, err := service.resolveAssetByIdentifier(ctx, constants.ExchangeAssetTypeDevice, device.IMEI)
	if err != nil {
		t.Fatalf("解析设备失败：%v", err)
	}
	if asset.Identifier != device.VirtualNo {
		t.Fatalf("设备应优先保存虚拟号，实际 %s", asset.Identifier)
	}
	for _, testCase := range []struct {
		device   *model.Device
		expected string
	}{
		{device: &model.Device{IMEI: "860000000000002", SN: "UR45-SN-2"}, expected: "860000000000002"},
		{device: &model.Device{SN: "UR45-SN-3"}, expected: "UR45-SN-3"},
	} {
		if actual := newResolvedDeviceAsset(testCase.device).Identifier; actual != testCase.expected {
			t.Fatalf("设备快照优先级错误，期望 %s，实际 %s", testCase.expected, actual)
		}
	}
}

// TestExchangeWriteEntrypointsPersistAuthoritativeCardSnapshots 验证三个写入入口持久化卡的权威 ICCID。
func TestExchangeWriteEntrypointsPersistAuthoritativeCardSnapshots(t *testing.T) {
	testCases := []struct {
		name       string
		identifier func(*model.IotCard) string
	}{
		{name: "ICCID", identifier: func(card *model.IotCard) string { return card.ICCID }},
		{name: "接入号", identifier: func(card *model.IotCard) string { return card.MSISDN }},
		{name: "虚拟号", identifier: func(card *model.IotCard) string { return card.VirtualNo }},
	}

	for index, testCase := range testCases {
		t.Run(testCase.name, func(t *testing.T) {
			tx := testutil.NewPostgresTransaction(t)
			service := newExchangeSnapshotTestService(tx)
			oldCard := createExchangeSnapshotCard(t, tx, index*10+1)
			newCard := createExchangeSnapshotCard(t, tx, index*10+2)

			direct, err := service.Create(context.Background(), &dto.CreateExchangeRequest{
				OldAssetType:   constants.ExchangeAssetTypeIotCard,
				OldIdentifier:  testCase.identifier(oldCard),
				FlowType:       constants.ExchangeFlowTypeDirect,
				NewIdentifier:  testCase.identifier(newCard),
				ExchangeReason: "UR45 卡快照测试",
			})
			if err != nil {
				t.Fatalf("创建直接换货失败：%v", err)
			}
			if direct.OldAssetIdentifier != oldCard.ICCID || direct.NewAssetIdentifier != newCard.ICCID {
				t.Fatalf("直接换货卡快照错误：old=%s new=%s", direct.OldAssetIdentifier, direct.NewAssetIdentifier)
			}

			shippingOld := createExchangeSnapshotCard(t, tx, index*10+3)
			shippingNew := createExchangeSnapshotCard(t, tx, index*10+4)
			shipping, err := service.Create(context.Background(), &dto.CreateExchangeRequest{
				OldAssetType:   constants.ExchangeAssetTypeIotCard,
				OldIdentifier:  testCase.identifier(shippingOld),
				FlowType:       constants.ExchangeFlowTypeShipping,
				ExchangeReason: "UR45 物流换货快照测试",
			})
			if err != nil {
				t.Fatalf("创建物流换货失败：%v", err)
			}
			if shipping.OldAssetIdentifier != shippingOld.ICCID {
				t.Fatalf("物流换货旧卡快照应为 ICCID，实际 %s", shipping.OldAssetIdentifier)
			}
			if err = tx.Model(&model.ExchangeOrder{}).Where("id = ?", shipping.ID).Updates(map[string]any{
				"status": constants.ExchangeStatusPendingShip, "recipient_name": "测试用户",
				"recipient_phone": "13800138000", "recipient_address": "测试地址",
			}).Error; err != nil {
				t.Fatalf("建立待发货测试前置状态失败：%v", err)
			}
			shipped, err := service.Ship(context.Background(), shipping.ID, &dto.ExchangeShipRequest{
				ExpressCompany: "测试快递", ExpressNo: "UR45-EXPRESS", NewIdentifier: testCase.identifier(shippingNew),
			})
			if err != nil {
				t.Fatalf("物流换货发货失败：%v", err)
			}
			if shipped.NewAssetIdentifier != shippingNew.ICCID {
				t.Fatalf("物流换货新卡快照应为 ICCID，实际 %s", shipped.NewAssetIdentifier)
			}
		})
	}
}

// TestExchangeWriteEntrypointsPersistPreferredDeviceSnapshots 验证设备输入标识不影响稳定快照优先级。
func TestExchangeWriteEntrypointsPersistPreferredDeviceSnapshots(t *testing.T) {
	testCases := []struct {
		name       string
		identifier func(*model.Device) string
	}{
		{name: "虚拟号", identifier: func(device *model.Device) string { return device.VirtualNo }},
		{name: "IMEI", identifier: func(device *model.Device) string { return device.IMEI }},
		{name: "SN", identifier: func(device *model.Device) string { return device.SN }},
	}

	for index, testCase := range testCases {
		t.Run(testCase.name, func(t *testing.T) {
			tx := testutil.NewPostgresTransaction(t)
			service := newExchangeSnapshotTestService(tx)
			oldDevice := createExchangeSnapshotDevice(t, tx, index*10+1, true, true)
			newDevice := createExchangeSnapshotDevice(t, tx, index*10+2, true, true)
			order, err := service.Create(context.Background(), &dto.CreateExchangeRequest{
				OldAssetType:   constants.ExchangeAssetTypeDevice,
				OldIdentifier:  testCase.identifier(oldDevice),
				FlowType:       constants.ExchangeFlowTypeDirect,
				NewIdentifier:  testCase.identifier(newDevice),
				ExchangeReason: "UR45 设备快照测试",
			})
			if err != nil {
				t.Fatalf("创建设备直接换货失败：%v", err)
			}
			if order.OldAssetIdentifier != oldDevice.VirtualNo || order.NewAssetIdentifier != newDevice.VirtualNo {
				t.Fatalf("设备快照应优先使用虚拟号：old=%s new=%s", order.OldAssetIdentifier, order.NewAssetIdentifier)
			}
			persisted, err := service.Get(context.Background(), order.ID)
			if err != nil || persisted.OldAssetIdentifier != oldDevice.VirtualNo || persisted.NewAssetIdentifier != newDevice.VirtualNo {
				t.Fatalf("详情未读回设备权威快照：order=%+v err=%v", persisted, err)
			}
		})
	}

	for index, testCase := range testCases {
		t.Run("物流"+testCase.name, func(t *testing.T) {
			tx := testutil.NewPostgresTransaction(t)
			service := newExchangeSnapshotTestService(tx)
			oldDevice := createExchangeSnapshotDevice(t, tx, 80+index*10+1, true, true)
			newDevice := createExchangeSnapshotDevice(t, tx, 80+index*10+2, true, true)
			shipping, err := service.Create(context.Background(), &dto.CreateExchangeRequest{
				OldAssetType: constants.ExchangeAssetTypeDevice, OldIdentifier: testCase.identifier(oldDevice),
				FlowType: constants.ExchangeFlowTypeShipping, ExchangeReason: "UR45 设备物流快照测试",
			})
			if err != nil {
				t.Fatalf("创建设备物流换货失败：%v", err)
			}
			if shipping.OldAssetIdentifier != oldDevice.VirtualNo {
				t.Fatalf("物流创建设备旧快照应使用虚拟号，实际 %s", shipping.OldAssetIdentifier)
			}
			if err = tx.Model(&model.ExchangeOrder{}).Where("id = ?", shipping.ID).Update("status", constants.ExchangeStatusPendingShip).Error; err != nil {
				t.Fatalf("建立待发货状态失败：%v", err)
			}
			shipped, err := service.Ship(context.Background(), shipping.ID, &dto.ExchangeShipRequest{
				ExpressCompany: "测试快递", ExpressNo: "UR45-DEVICE-EXPRESS", NewIdentifier: testCase.identifier(newDevice),
			})
			if err != nil {
				t.Fatalf("设备物流换货发货失败：%v", err)
			}
			if shipped.OldAssetIdentifier != oldDevice.VirtualNo || shipped.NewAssetIdentifier != newDevice.VirtualNo {
				t.Fatalf("设备物流快照应使用虚拟号：old=%s new=%s", shipped.OldAssetIdentifier, shipped.NewAssetIdentifier)
			}
		})
	}
}

func newExchangeSnapshotTestService(tx *gorm.DB) *Service {
	iotCardStore := postgres.NewIotCardStore(tx, nil)
	deviceStore := postgres.NewDeviceStore(tx, nil)
	return New(
		tx,
		postgres.NewExchangeOrderStore(tx),
		iotCardStore,
		deviceStore,
		postgres.NewAssetWalletStore(tx, nil),
		postgres.NewAssetWalletTransactionStore(tx, nil),
		postgres.NewPackageUsageStore(tx, nil),
		postgres.NewPackageUsageDailyRecordStore(tx, nil),
		postgres.NewResourceTagStore(tx),
		customerBindingSvc.New(tx, iotCardStore, deviceStore),
		zap.NewNop(),
	)
}

func createExchangeSnapshotCard(t *testing.T, tx *gorm.DB, suffix int) *model.IotCard {
	t.Helper()
	iccid := fmt.Sprintf("8986000000000000%04d", suffix)
	card := &model.IotCard{
		ICCID: iccid, ICCID19: iccid[:19], MSISDN: fmt.Sprintf("1390000%04d", suffix),
		VirtualNo: fmt.Sprintf("UR45-CARD-%04d", suffix), AssetStatus: constants.AssetStatusInStock,
	}
	if err := tx.Create(card).Error; err != nil {
		t.Fatalf("创建测试卡失败：%v", err)
	}
	return card
}

func createExchangeSnapshotDevice(t *testing.T, tx *gorm.DB, suffix int, withVirtualNo, withIMEI bool) *model.Device {
	t.Helper()
	device := &model.Device{SN: fmt.Sprintf("UR45-SN-%04d", suffix), AssetStatus: constants.AssetStatusInStock}
	if withVirtualNo {
		device.VirtualNo = fmt.Sprintf("UR45-DEVICE-%04d", suffix)
	}
	if withIMEI {
		device.IMEI = fmt.Sprintf("86000000000%04d", suffix)
	}
	if err := tx.Create(device).Error; err != nil {
		t.Fatalf("创建设备失败：%v", err)
	}
	return device
}
