// Package customer_binding 封装客户与资产之间的绑定创建和归属验证逻辑
// 屏蔽"有虚拟号走 tb_personal_customer_device / 无虚拟号走 tb_personal_customer_iccid"的路由细节
package customer_binding

import (
	"context"
	"sort"
	"time"

	"gorm.io/gorm"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/store/postgres"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
)

const (
	assetTypeIotCard = "iot_card"
	assetTypeDevice  = "device"
)

// normalizeAssetType 统一资产类型字符串
// assetService.Resolve 返回 "card"，client_auth 内部使用 "iot_card"，两者等价
func normalizeAssetType(assetType string) string {
	if assetType == "card" {
		return assetTypeIotCard
	}
	return assetType
}

// cardReader 按 ID 查询 IoT 卡（OwnsAsset 使用，无需感知事务）
type cardReader interface {
	GetByID(ctx context.Context, id uint) (*model.IotCard, error)
}

// deviceReader 按 ID 查询设备（OwnsAsset 使用，无需感知事务）
type deviceReader interface {
	GetByID(ctx context.Context, id uint) (*model.Device, error)
}

// pcdOps tb_personal_customer_device 操作接口
type pcdOps interface {
	ExistsByCustomerAndDevice(ctx context.Context, customerID uint, deviceNo string) (bool, error)
	Create(ctx context.Context, record *model.PersonalCustomerDevice) error
	CountByVirtualNo(ctx context.Context, virtualNo string) (int64, error)
	GetByDeviceNo(ctx context.Context, deviceNo string) ([]*model.PersonalCustomerDevice, error)
	UpdateStatus(ctx context.Context, id uint, status int) error
	UpdateVirtualNo(ctx context.Context, id uint, newVirtualNo string) error
}

// pciOps tb_personal_customer_iccid 操作接口
type pciOps interface {
	ExistsByCustomerAndICCID(ctx context.Context, customerID uint, iccid string) (bool, error)
	Create(ctx context.Context, record *model.PersonalCustomerICCID) error
	CountByICCID(ctx context.Context, iccid string) (int64, error)
	GetByICCID(ctx context.Context, iccid string) ([]*model.PersonalCustomerICCID, error)
	UpdateStatus(ctx context.Context, id uint, status int) error
}

// Service 客户绑定服务
type Service struct {
	db         *gorm.DB
	cards      cardReader
	devices    deviceReader
	makePCD    func(*gorm.DB) pcdOps
	makePCI    func(*gorm.DB) pciOps
	markAsSold func(ctx context.Context, tx *gorm.DB, assetType string, assetID uint) error
	// readCard/readDevice 用于 Bind/Migrate 内部，接收事务 db 以保持读写在同一事务内
	readCard   func(ctx context.Context, db *gorm.DB, id uint) (*model.IotCard, error)
	readDevice func(ctx context.Context, db *gorm.DB, id uint) (*model.Device, error)
}

// New 创建客户绑定服务实例
func New(db *gorm.DB, iotCardStore cardReader, deviceStore deviceReader) *Service {
	return &Service{
		db:      db,
		cards:   iotCardStore,
		devices: deviceStore,
		makePCD: func(db *gorm.DB) pcdOps {
			return postgres.NewPersonalCustomerDeviceStore(db)
		},
		makePCI: func(db *gorm.DB) pciOps {
			return postgres.NewPersonalCustomerICCIDStore(db)
		},
		markAsSold: realMarkAssetAsSold,
		readCard: func(ctx context.Context, db *gorm.DB, id uint) (*model.IotCard, error) {
			var card model.IotCard
			if err := db.WithContext(ctx).First(&card, id).Error; err != nil {
				if err == gorm.ErrRecordNotFound {
					return nil, errors.New(errors.CodeAssetNotFound)
				}
				return nil, errors.Wrap(errors.CodeInternalError, err, "查询卡资产失败")
			}
			return &card, nil
		},
		readDevice: func(ctx context.Context, db *gorm.DB, id uint) (*model.Device, error) {
			var device model.Device
			if err := db.WithContext(ctx).First(&device, id).Error; err != nil {
				if err == gorm.ErrRecordNotFound {
					return nil, errors.New(errors.CodeAssetNotFound)
				}
				return nil, errors.Wrap(errors.CodeInternalError, err, "查询设备资产失败")
			}
			return &device, nil
		},
	}
}

// OwnsAsset 验证客户是否持有指定资产的有效绑定（status=1）
// 内部根据 assetType + assetID 自动路由到对应绑定表，调用方无需感知底层存储细节
func (s *Service) OwnsAsset(ctx context.Context, customerID uint, assetType string, assetID uint) (bool, error) {
	pcd := s.makePCD(s.db)

	switch normalizeAssetType(assetType) {
	case assetTypeIotCard:
		pci := s.makePCI(s.db)
		card, err := s.cards.GetByID(ctx, assetID)
		if err != nil {
			if err == gorm.ErrRecordNotFound {
				return false, errors.New(errors.CodeAssetNotFound)
			}
			return false, errors.Wrap(errors.CodeInternalError, err, "查询卡资产失败")
		}
		if card.VirtualNo != "" {
			return pcd.ExistsByCustomerAndDevice(ctx, customerID, card.VirtualNo)
		}
		// 无虚拟号：查 pci 表（Issue 02 完整实现，此处已路由到正确路径）
		return pci.ExistsByCustomerAndICCID(ctx, customerID, card.ICCID)

	case assetTypeDevice:
		device, err := s.devices.GetByID(ctx, assetID)
		if err != nil {
			if err == gorm.ErrRecordNotFound {
				return false, errors.New(errors.CodeAssetNotFound)
			}
			return false, errors.Wrap(errors.CodeInternalError, err, "查询设备资产失败")
		}
		key := device.VirtualNo
		if key == "" {
			key = device.IMEI
		}
		return pcd.ExistsByCustomerAndDevice(ctx, customerID, key)
	}

	return false, errors.New(errors.CodeInvalidParam)
}

// ActiveCustomerIDsByAsset 返回当前启用且关联指定资产的个人客户 ID，结果去重并稳定排序。
func (s *Service) ActiveCustomerIDsByAsset(ctx context.Context, tx *gorm.DB, assetType string, assetID uint) ([]uint, error) {
	if tx == nil {
		tx = s.db
	}
	customerIDs := make(map[uint]struct{})
	switch normalizeAssetType(assetType) {
	case assetTypeIotCard:
		card, err := s.readCard(ctx, tx, assetID)
		if err != nil {
			return nil, err
		}
		if card.VirtualNo != "" {
			records, err := s.makePCD(tx).GetByDeviceNo(ctx, card.VirtualNo)
			if err != nil {
				return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询卡关联客户失败")
			}
			collectActiveDeviceCustomerIDs(customerIDs, records)
		} else {
			records, err := s.makePCI(tx).GetByICCID(ctx, card.ICCID)
			if err != nil {
				return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询卡关联客户失败")
			}
			collectActiveCardCustomerIDs(customerIDs, records)
		}
	case assetTypeDevice:
		device, err := s.readDevice(ctx, tx, assetID)
		if err != nil {
			return nil, err
		}
		identifier := device.VirtualNo
		if identifier == "" {
			identifier = device.IMEI
		}
		records, err := s.makePCD(tx).GetByDeviceNo(ctx, identifier)
		if err != nil {
			return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询设备关联客户失败")
		}
		collectActiveDeviceCustomerIDs(customerIDs, records)
	default:
		return nil, errors.New(errors.CodeInvalidParam, "无效的资产类型")
	}
	result := make([]uint, 0, len(customerIDs))
	for customerID := range customerIDs {
		result = append(result, customerID)
	}
	sort.Slice(result, func(i, j int) bool { return result[i] < result[j] })
	return result, nil
}

func collectActiveDeviceCustomerIDs(target map[uint]struct{}, records []*model.PersonalCustomerDevice) {
	for _, record := range records {
		if record != nil && record.Status == constants.StatusEnabled && record.CustomerID > 0 {
			target[record.CustomerID] = struct{}{}
		}
	}
}

func collectActiveCardCustomerIDs(target map[uint]struct{}, records []*model.PersonalCustomerICCID) {
	for _, record := range records {
		if record != nil && record.Status == constants.StatusEnabled && record.CustomerID > 0 {
			target[record.CustomerID] = struct{}{}
		}
	}
}

// Bind 在事务中创建客户与资产的绑定关系
// 有虚拟号的 IoT 卡 / 设备 → tb_personal_customer_device
// 无虚拟号的 IoT 卡 → tb_personal_customer_iccid（Issue 02）
func (s *Service) Bind(ctx context.Context, tx *gorm.DB, customerID uint, assetType string, assetID uint) error {
	if tx == nil {
		tx = s.db
	}
	pcd := s.makePCD(tx)
	pci := s.makePCI(tx)

	switch normalizeAssetType(assetType) {
	case assetTypeIotCard:
		card, err := s.readCard(ctx, tx, assetID)
		if err != nil {
			return err
		}
		if card.VirtualNo != "" {
			return s.bindViaPCD(ctx, pcd, tx, customerID, card.VirtualNo, assetTypeIotCard, assetID)
		}
		// 无虚拟号路径（Issue 02）
		return s.bindViaPCI(ctx, pci, tx, customerID, card.ICCID, assetTypeIotCard, assetID)

	case assetTypeDevice:
		device, err := s.readDevice(ctx, tx, assetID)
		if err != nil {
			return err
		}
		key := device.VirtualNo
		if key == "" {
			key = device.IMEI
		}
		return s.bindViaPCD(ctx, pcd, tx, customerID, key, assetType, assetID)
	}

	return errors.New(errors.CodeInvalidParam)
}

// bindViaPCD 通过 tb_personal_customer_device 创建绑定
func (s *Service) bindViaPCD(ctx context.Context, pcd pcdOps, tx *gorm.DB, customerID uint, virtualNo string, assetType string, assetID uint) error {
	count, err := pcd.CountByVirtualNo(ctx, virtualNo)
	if err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "查询资产绑定数量失败")
	}
	firstEverBind := count == 0

	exists, err := pcd.ExistsByCustomerAndDevice(ctx, customerID, virtualNo)
	if err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "查询客户资产绑定关系失败")
	}

	if !exists {
		record := &model.PersonalCustomerDevice{
			CustomerID: customerID,
			VirtualNo:  virtualNo,
			Status:     1,
		}
		if err := pcd.Create(ctx, record); err != nil {
			return errors.Wrap(errors.CodeInternalError, err, "创建资产绑定关系失败")
		}
	}

	if firstEverBind {
		return s.markAsSold(ctx, tx, assetType, assetID)
	}

	return nil
}

// bindViaPCI 通过 tb_personal_customer_iccid 创建绑定（无虚拟号卡专用，Issue 02）
func (s *Service) bindViaPCI(ctx context.Context, pci pciOps, tx *gorm.DB, customerID uint, iccid string, assetType string, assetID uint) error {
	count, err := pci.CountByICCID(ctx, iccid)
	if err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "查询 ICCID 绑定数量失败")
	}
	firstEverBind := count == 0

	exists, err := pci.ExistsByCustomerAndICCID(ctx, customerID, iccid)
	if err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "查询客户 ICCID 绑定关系失败")
	}

	if !exists {
		iccid19 := iccid
		if len(iccid) == 20 {
			iccid19 = iccid[:19]
		}
		record := &model.PersonalCustomerICCID{
			CustomerID: customerID,
			ICCID:      iccid,
			ICCID19:    iccid19,
			Status:     1,
		}
		if err := pci.Create(ctx, record); err != nil {
			return errors.Wrap(errors.CodeInternalError, err, "创建 ICCID 绑定关系失败")
		}
	}

	if firstEverBind {
		return s.markAsSold(ctx, tx, assetType, assetID)
	}

	return nil
}

// Migrate 将旧资产的所有有效客户绑定迁移到新资产（换货专用）
// 无绑定时静默跳过；按旧/新资产虚拟号有无路由到 pcd 或 pci
func (s *Service) Migrate(ctx context.Context, tx *gorm.DB, oldAssetType string, oldAssetID uint, newAssetType string, newAssetID uint) error {
	if tx == nil {
		tx = s.db
	}
	pcd := s.makePCD(tx)
	pci := s.makePCI(tx)

	switch normalizeAssetType(oldAssetType) {
	case assetTypeIotCard:
		oldCard, err := s.readCard(ctx, tx, oldAssetID)
		if err != nil {
			return err
		}
		if oldCard.VirtualNo != "" {
			return s.migrateFromPCD(ctx, tx, pcd, pci, oldCard.VirtualNo, newAssetType, newAssetID)
		}
		return s.migrateFromPCI(ctx, tx, pcd, pci, oldCard.ICCID, newAssetType, newAssetID)

	case assetTypeDevice:
		oldDevice, err := s.readDevice(ctx, tx, oldAssetID)
		if err != nil {
			return err
		}
		key := oldDevice.VirtualNo
		if key == "" {
			key = oldDevice.IMEI
		}
		return s.migrateFromPCD(ctx, tx, pcd, pci, key, newAssetType, newAssetID)
	}

	return errors.New(errors.CodeInvalidParam)
}

// migrateFromPCD 将 tb_personal_customer_device 中 oldKey 的所有有效绑定迁移到新资产
func (s *Service) migrateFromPCD(ctx context.Context, db *gorm.DB, pcd pcdOps, pci pciOps, oldKey string, newAssetType string, newAssetID uint) error {
	records, err := pcd.GetByDeviceNo(ctx, oldKey)
	if err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "查询旧资产绑定记录失败")
	}

	// 仅处理 status=1 的有效记录
	var active []*model.PersonalCustomerDevice
	for _, r := range records {
		if r.Status == 1 {
			active = append(active, r)
		}
	}
	if len(active) == 0 {
		return nil
	}

	switch normalizeAssetType(newAssetType) {
	case assetTypeIotCard:
		newCard, err := s.readCard(ctx, db, newAssetID)
		if err != nil {
			return err
		}
		if newCard.VirtualNo != "" {
			// 新卡有虚拟号：更新 pcd virtual_no
			for _, rec := range active {
				if err := pcd.UpdateVirtualNo(ctx, rec.ID, newCard.VirtualNo); err != nil {
					return errors.Wrap(errors.CodeInternalError, err, "迁移客户绑定关系失败")
				}
			}
		} else {
			// 新卡无虚拟号：禁用旧 pcd + 创建新 pci
			iccid19 := newCard.ICCID
			if len(newCard.ICCID) == 20 {
				iccid19 = newCard.ICCID[:19]
			}
			for _, rec := range active {
				if err := pcd.UpdateStatus(ctx, rec.ID, 0); err != nil {
					return errors.Wrap(errors.CodeInternalError, err, "禁用旧客户绑定关系失败")
				}
				newPCI := &model.PersonalCustomerICCID{
					CustomerID: rec.CustomerID,
					ICCID:      newCard.ICCID,
					ICCID19:    iccid19,
					Status:     1,
				}
				if err := pci.Create(ctx, newPCI); err != nil {
					return errors.Wrap(errors.CodeInternalError, err, "创建新客户绑定关系失败")
				}
			}
		}

	case assetTypeDevice:
		newDevice, err := s.readDevice(ctx, db, newAssetID)
		if err != nil {
			return err
		}
		newKey := newDevice.VirtualNo
		if newKey == "" {
			newKey = newDevice.IMEI
		}
		for _, rec := range active {
			if err := pcd.UpdateVirtualNo(ctx, rec.ID, newKey); err != nil {
				return errors.Wrap(errors.CodeInternalError, err, "迁移设备客户绑定关系失败")
			}
		}

	default:
		return errors.New(errors.CodeInvalidParam)
	}

	return nil
}

// migrateFromPCI 将 tb_personal_customer_iccid 中 oldICCID 的所有有效绑定迁移到新资产
func (s *Service) migrateFromPCI(ctx context.Context, db *gorm.DB, pcd pcdOps, pci pciOps, oldICCID string, newAssetType string, newAssetID uint) error {
	records, err := pci.GetByICCID(ctx, oldICCID)
	if err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "查询旧 ICCID 绑定记录失败")
	}

	var active []*model.PersonalCustomerICCID
	for _, r := range records {
		if r.Status == 1 {
			active = append(active, r)
		}
	}
	if len(active) == 0 {
		return nil
	}

	switch normalizeAssetType(newAssetType) {
	case assetTypeIotCard:
		newCard, err := s.readCard(ctx, db, newAssetID)
		if err != nil {
			return err
		}
		if newCard.VirtualNo != "" {
			// 新卡有虚拟号：禁用旧 pci + 创建新 pcd
			for _, rec := range active {
				if err := pci.UpdateStatus(ctx, rec.ID, 0); err != nil {
					return errors.Wrap(errors.CodeInternalError, err, "禁用旧 ICCID 绑定关系失败")
				}
				newPCD := &model.PersonalCustomerDevice{
					CustomerID: rec.CustomerID,
					VirtualNo:  newCard.VirtualNo,
					Status:     1,
				}
				if err := pcd.Create(ctx, newPCD); err != nil {
					return errors.Wrap(errors.CodeInternalError, err, "创建新客户绑定关系失败")
				}
			}
		} else {
			// 新卡也无虚拟号：禁用旧 pci + 创建新 pci（新 ICCID）
			iccid19 := newCard.ICCID
			if len(newCard.ICCID) == 20 {
				iccid19 = newCard.ICCID[:19]
			}
			for _, rec := range active {
				if err := pci.UpdateStatus(ctx, rec.ID, 0); err != nil {
					return errors.Wrap(errors.CodeInternalError, err, "禁用旧 ICCID 绑定关系失败")
				}
				newPCI := &model.PersonalCustomerICCID{
					CustomerID: rec.CustomerID,
					ICCID:      newCard.ICCID,
					ICCID19:    iccid19,
					Status:     1,
				}
				if err := pci.Create(ctx, newPCI); err != nil {
					return errors.Wrap(errors.CodeInternalError, err, "创建新 ICCID 绑定关系失败")
				}
			}
		}

	case assetTypeDevice:
		newDevice, err := s.readDevice(ctx, db, newAssetID)
		if err != nil {
			return err
		}
		newKey := newDevice.VirtualNo
		if newKey == "" {
			newKey = newDevice.IMEI
		}
		for _, rec := range active {
			if err := pci.UpdateStatus(ctx, rec.ID, 0); err != nil {
				return errors.Wrap(errors.CodeInternalError, err, "禁用旧 ICCID 绑定关系失败")
			}
			newPCD := &model.PersonalCustomerDevice{
				CustomerID: rec.CustomerID,
				VirtualNo:  newKey,
				Status:     1,
			}
			if err := pcd.Create(ctx, newPCD); err != nil {
				return errors.Wrap(errors.CodeInternalError, err, "创建新客户绑定关系失败")
			}
		}

	default:
		return errors.New(errors.CodeInvalidParam)
	}

	return nil
}

// realMarkAssetAsSold 将资产状态从在库（1）更新为已销售（2）
func realMarkAssetAsSold(ctx context.Context, tx *gorm.DB, assetType string, assetID uint) error {
	now := time.Now()
	switch assetType {
	case assetTypeIotCard:
		if err := tx.WithContext(ctx).
			Model(&model.IotCard{}).
			Where("id = ? AND asset_status = ?", assetID, 1).
			Updates(map[string]any{"asset_status": 2, "updated_at": now}).Error; err != nil {
			return errors.Wrap(errors.CodeInternalError, err, "更新卡资产状态失败")
		}
	case assetTypeDevice:
		if err := tx.WithContext(ctx).
			Model(&model.Device{}).
			Where("id = ? AND asset_status = ?", assetID, 1).
			Updates(map[string]any{"asset_status": 2, "updated_at": now}).Error; err != nil {
			return errors.Wrap(errors.CodeInternalError, err, "更新设备资产状态失败")
		}
	default:
		return errors.New(errors.CodeInvalidParam)
	}
	return nil
}
