package purchase_validation

import (
	"context"
	"time"

	"github.com/break/junhong_cmp_fiber/internal/model"
	packagepkg "github.com/break/junhong_cmp_fiber/internal/service/package"
	"github.com/break/junhong_cmp_fiber/internal/service/packageprice"
	"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"
	"gorm.io/gorm"
)

type allowGiftPackagesKey struct{}

// WithAllowGiftPackages 标记当前校验上下文允许后台赠送订单进入验证流程。
func WithAllowGiftPackages(ctx context.Context) context.Context {
	return context.WithValue(ctx, allowGiftPackagesKey{}, true)
}

func canAllowGiftPackages(ctx context.Context) bool {
	allowed, _ := ctx.Value(allowGiftPackagesKey{}).(bool)
	return allowed
}

type Service struct {
	db                     *gorm.DB
	iotCardStore           *postgres.IotCardStore
	deviceStore            *postgres.DeviceStore
	packageStore           *postgres.PackageStore
	packageAllocationStore *postgres.ShopPackageAllocationStore
}

func New(
	db *gorm.DB,
	iotCardStore *postgres.IotCardStore,
	deviceStore *postgres.DeviceStore,
	packageStore *postgres.PackageStore,
	packageAllocationStore *postgres.ShopPackageAllocationStore,
) *Service {
	return &Service{
		db:                     db,
		iotCardStore:           iotCardStore,
		deviceStore:            deviceStore,
		packageStore:           packageStore,
		packageAllocationStore: packageAllocationStore,
	}
}

type PurchaseValidationResult struct {
	Card       *model.IotCard
	Device     *model.Device
	Packages   []*model.Package
	TotalPrice int64
}

func (s *Service) ValidateCardPurchase(ctx context.Context, cardID uint, packageIDs []uint) (*PurchaseValidationResult, error) {
	return s.validateCardPurchase(ctx, cardID, packageIDs, false)
}

// ValidatePersonalCardPurchase 校验个人客户卡套餐购买，并允许当前世代生效中套餐续费。
func (s *Service) ValidatePersonalCardPurchase(ctx context.Context, cardID uint, packageIDs []uint) (*PurchaseValidationResult, error) {
	return s.validateCardPurchase(ctx, cardID, packageIDs, true)
}

func (s *Service) validateCardPurchase(ctx context.Context, cardID uint, packageIDs []uint, allowRenewal bool) (*PurchaseValidationResult, error) {
	card, err := s.iotCardStore.GetByID(ctx, cardID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeIotCardNotFound, "IoT卡不存在")
		}
		return nil, err
	}

	if !card.IsStandalone {
		return nil, errors.New(errors.CodeInvalidParam, "该卡已绑定设备，请前往设备页面购买套餐")
	}

	if card.SeriesID == nil || *card.SeriesID == 0 {
		return nil, errors.New(errors.CodeInvalidParam, "该卡未关联套餐系列，无法购买套餐")
	}

	// 确定卖家店铺ID：卡所属店铺即为卖家（代理渠道），平台自营时为0
	var sellerShopID uint
	if card.ShopID != nil {
		sellerShopID = *card.ShopID
	}

	renewablePackageIDs, err := s.loadRenewablePackageIDs(ctx, constants.AssetWalletResourceTypeIotCard, card.ID, card.Generation, packageIDs, allowRenewal)
	if err != nil {
		return nil, err
	}
	packages, totalPrice, err := s.validatePackages(ctx, packageIDs, *card.SeriesID, sellerShopID, renewablePackageIDs)
	if err != nil {
		return nil, err
	}

	if err := s.validatePackageUsageRules(ctx, constants.AssetWalletResourceTypeIotCard, card.ID, packages); err != nil {
		return nil, err
	}

	return &PurchaseValidationResult{
		Card:       card,
		Packages:   packages,
		TotalPrice: totalPrice,
	}, nil
}

func (s *Service) ValidateDevicePurchase(ctx context.Context, deviceID uint, packageIDs []uint) (*PurchaseValidationResult, error) {
	return s.validateDevicePurchase(ctx, deviceID, packageIDs, false)
}

// ValidatePersonalDevicePurchase 校验个人客户设备套餐购买，并允许当前世代生效中套餐续费。
func (s *Service) ValidatePersonalDevicePurchase(ctx context.Context, deviceID uint, packageIDs []uint) (*PurchaseValidationResult, error) {
	return s.validateDevicePurchase(ctx, deviceID, packageIDs, true)
}

func (s *Service) validateDevicePurchase(ctx context.Context, deviceID uint, packageIDs []uint, allowRenewal bool) (*PurchaseValidationResult, error) {
	device, err := s.deviceStore.GetByID(ctx, deviceID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeNotFound, "设备不存在")
		}
		return nil, err
	}

	if device.SeriesID == nil || *device.SeriesID == 0 {
		return nil, errors.New(errors.CodeInvalidParam, "该设备未关联套餐系列，无法购买套餐")
	}

	// 确定卖家店铺ID：设备所属店铺即为卖家（代理渠道），平台自营时为0
	var sellerShopID uint
	if device.ShopID != nil {
		sellerShopID = *device.ShopID
	}

	renewablePackageIDs, err := s.loadRenewablePackageIDs(ctx, constants.AssetWalletResourceTypeDevice, device.ID, device.Generation, packageIDs, allowRenewal)
	if err != nil {
		return nil, err
	}
	packages, totalPrice, err := s.validatePackages(ctx, packageIDs, *device.SeriesID, sellerShopID, renewablePackageIDs)
	if err != nil {
		return nil, err
	}

	if err := s.validatePackageUsageRules(ctx, constants.AssetWalletResourceTypeDevice, device.ID, packages); err != nil {
		return nil, err
	}

	return &PurchaseValidationResult{
		Device:     device,
		Packages:   packages,
		TotalPrice: totalPrice,
	}, nil
}

// validatePackages 验证套餐列表是否可购买
// sellerShopID > 0 表示代理渠道，校验该代理的 allocation.shelf_status；
// sellerShopID == 0 表示平台自营渠道，校验 package.shelf_status
func (s *Service) validatePackages(ctx context.Context, packageIDs []uint, seriesID uint, sellerShopID uint, renewablePackageIDs map[uint]struct{}) ([]*model.Package, int64, error) {
	if len(packageIDs) == 0 {
		return nil, 0, errors.New(errors.CodeInvalidParam, "请选择至少一个套餐")
	}

	var packages []*model.Package
	var totalPrice int64

	for _, pkgID := range packageIDs {
		pkg, err := s.packageStore.GetByID(ctx, pkgID)
		if err != nil {
			if err == gorm.ErrRecordNotFound {
				return nil, 0, errors.New(errors.CodeInvalidParam, "套餐不存在")
			}
			return nil, 0, err
		}

		if pkg.SeriesID != seriesID {
			return nil, 0, errors.New(errors.CodeInvalidParam, "套餐不在可购买范围内")
		}
		if pkg.IsGift {
			if sellerShopID > 0 || !canAllowGiftPackages(ctx) {
				return nil, 0, errors.New(errors.CodeInvalidParam, "赠送套餐仅支持后台发放")
			}
		}

		// Package.status 为全局开关，任何渠道都必须检查
		if pkg.Status != constants.StatusEnabled {
			return nil, 0, errors.New(errors.CodeInvalidParam, "套餐已禁用")
		}

		_, canRenewOffShelf := renewablePackageIDs[pkgID]
		if sellerShopID > 0 {
			// 代理渠道：检查上架状态并获取分配记录，使用零售价
			allocation, allocErr := s.validateAgentAllocation(ctx, sellerShopID, pkgID, canRenewOffShelf)
			if allocErr != nil {
				return nil, 0, allocErr
			}
			effectiveRetailPrice := packageprice.AllocationEffectiveRetailPrice(allocation)
			// 生效零售价低于成本价时视为不可购买，防止亏损售卖
			if effectiveRetailPrice < allocation.CostPrice {
				return nil, 0, errors.New(errors.CodeInvalidParam, "套餐价格配置异常，暂不可购买")
			}
			totalPrice += effectiveRetailPrice
		} else {
			if pkg.ShelfStatus != constants.ShelfStatusOn && !canRenewOffShelf {
				return nil, 0, errors.New(errors.CodeInvalidParam, "套餐已下架")
			}
			totalPrice += packageprice.PackageEffectiveRetailPrice(pkg)
		}

		packages = append(packages, pkg)
	}

	return packages, totalPrice, nil
}

// validatePackageUsageRules 校验主套餐和加油包的购买前置条件。
func (s *Service) validatePackageUsageRules(ctx context.Context, carrierType string, carrierID uint, packages []*model.Package) error {
	formalCount := 0
	hasAddon := false
	for _, pkg := range packages {
		if pkg == nil {
			continue
		}
		switch pkg.PackageType {
		case constants.PackageTypeFormal:
			formalCount++
		case constants.PackageTypeAddon:
			hasAddon = true
		}
		if formalCount > 0 && hasAddon {
			return errors.New(errors.CodeMixedOrderForbidden)
		}
	}

	if formalCount > 1 {
		return errors.New(errors.CodeMainPackageExists, "一次只能购买一个主套餐")
	}

	if hasAddon {
		if _, err := packagepkg.FindAttachableMainPackageForAddon(
			s.db.WithContext(ctx),
			carrierType,
			carrierID,
			time.Now(),
		); err != nil {
			if err == gorm.ErrRecordNotFound {
				return errors.New(errors.CodeNoMainPackage)
			}
			return errors.Wrap(errors.CodeDatabaseError, err, "查询主套餐失败")
		}
	}

	return nil
}

// validateAgentAllocation 校验卖家代理的分配记录上架状态，并返回分配记录
func (s *Service) validateAgentAllocation(ctx context.Context, sellerShopID, packageID uint, allowOffShelf bool) (*model.ShopPackageAllocation, error) {
	allocation, err := s.packageAllocationStore.GetByShopAndPackageForSystem(ctx, sellerShopID, packageID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeInvalidParam, "套餐已下架")
		}
		return nil, errors.Wrap(errors.CodeInternalError, err, "查询套餐分配记录失败")
	}

	if allocation.Status != constants.StatusEnabled {
		return nil, errors.New(errors.CodeInvalidParam, "套餐已禁用")
	}
	if allocation.ShelfStatus != constants.ShelfStatusOn && !allowOffShelf {
		return nil, errors.New(errors.CodeInvalidParam, "套餐已下架")
	}

	return allocation, nil
}

func (s *Service) loadRenewablePackageIDs(ctx context.Context, assetType string, assetID uint, generation int, packageIDs []uint, allowRenewal bool) (map[uint]struct{}, error) {
	result := make(map[uint]struct{})
	if !allowRenewal || len(packageIDs) == 0 {
		return result, nil
	}

	query := s.db.WithContext(ctx).Model(&model.PackageUsage{}).
		Distinct("package_id").
		Where("generation = ? AND status = ? AND refund_id IS NULL AND package_id IN ?", generation, constants.PackageUsageStatusActive, packageIDs)
	switch assetType {
	case constants.AssetWalletResourceTypeIotCard:
		query = query.Where("usage_type = ? AND iot_card_id = ?", constants.PackageUsageTypeSingleCard, assetID)
	case constants.AssetWalletResourceTypeDevice:
		query = query.Where("usage_type = ? AND device_id = ?", constants.AssetWalletResourceTypeDevice, assetID)
	default:
		return nil, errors.New(errors.CodeInvalidParam)
	}

	var ids []uint
	if err := query.Pluck("package_id", &ids).Error; err != nil {
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询套餐续费资格失败")
	}
	for _, id := range ids {
		result[id] = struct{}{}
	}
	return result, nil
}

// GetPurchasePrice 获取购买价格
// 代理渠道（sellerShopID > 0）返回 allocation.RetailPrice，平台渠道返回 Package.SuggestedRetailPrice
func (s *Service) GetPurchasePrice(ctx context.Context, pkg *model.Package, sellerShopID uint) (int64, error) {
	if sellerShopID > 0 {
		allocation, err := s.packageAllocationStore.GetByShopAndPackageForSystem(ctx, sellerShopID, pkg.ID)
		if err != nil {
			if err == gorm.ErrRecordNotFound {
				return 0, errors.New(errors.CodeInvalidParam, "套餐已下架")
			}
			return 0, errors.Wrap(errors.CodeInternalError, err, "查询套餐分配记录失败")
		}
		return packageprice.AllocationEffectiveRetailPrice(allocation), nil
	}
	return packageprice.PackageEffectiveRetailPrice(pkg), nil
}

// GetCostPrice 获取代理渠道的套餐成本价
// sellerShopID == 0 表示平台自营，返回 0（平台直销无成本价概念）
func (s *Service) GetCostPrice(ctx context.Context, pkg *model.Package, sellerShopID uint) (int64, error) {
	if sellerShopID == 0 {
		return 0, nil
	}
	allocation, err := s.packageAllocationStore.GetByShopAndPackageForSystem(ctx, sellerShopID, pkg.ID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return 0, errors.New(errors.CodeInvalidParam, "店铺没有该套餐的分配配置")
		}
		return 0, errors.Wrap(errors.CodeInternalError, err, "查询套餐分配记录失败")
	}
	return allocation.CostPrice, nil
}

// ValidateAdminOfflineCardPurchase 后台 offline 订单专用卡验证
// 绕过代理 Allocation 上架检查，仅验证套餐全局状态
func (s *Service) ValidateAdminOfflineCardPurchase(ctx context.Context, cardID uint, packageIDs []uint) (*PurchaseValidationResult, error) {
	card, err := s.iotCardStore.GetByID(ctx, cardID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeIotCardNotFound, "IoT卡不存在")
		}
		return nil, err
	}

	if !card.IsStandalone {
		return nil, errors.New(errors.CodeInvalidParam, "该卡已绑定设备，请前往设备页面购买套餐")
	}

	if card.SeriesID == nil || *card.SeriesID == 0 {
		return nil, errors.New(errors.CodeInvalidParam, "该卡未关联套餐系列，无法购买套餐")
	}

	packages, totalPrice, err := s.validatePackages(ctx, packageIDs, *card.SeriesID, 0, nil)
	if err != nil {
		return nil, err
	}

	if err := s.validatePackageUsageRules(ctx, constants.AssetWalletResourceTypeIotCard, card.ID, packages); err != nil {
		return nil, err
	}

	return &PurchaseValidationResult{
		Card:       card,
		Packages:   packages,
		TotalPrice: totalPrice,
	}, nil
}

// ValidateAdminOfflineDevicePurchase 后台 offline 订单专用设备验证
// 绕过代理 Allocation 上架检查，仅验证套餐全局状态
func (s *Service) ValidateAdminOfflineDevicePurchase(ctx context.Context, deviceID uint, packageIDs []uint) (*PurchaseValidationResult, error) {
	device, err := s.deviceStore.GetByID(ctx, deviceID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeNotFound, "设备不存在")
		}
		return nil, err
	}

	if device.SeriesID == nil || *device.SeriesID == 0 {
		return nil, errors.New(errors.CodeInvalidParam, "该设备未关联套餐系列，无法购买套餐")
	}

	packages, totalPrice, err := s.validatePackages(ctx, packageIDs, *device.SeriesID, 0, nil)
	if err != nil {
		return nil, err
	}

	if err := s.validatePackageUsageRules(ctx, constants.AssetWalletResourceTypeDevice, device.ID, packages); err != nil {
		return nil, err
	}

	return &PurchaseValidationResult{
		Device:     device,
		Packages:   packages,
		TotalPrice: totalPrice,
	}, nil
}
