package packagepkg

import (
	"context"
	"fmt"
	"time"

	"gorm.io/gorm"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/model/dto"
	"github.com/break/junhong_cmp_fiber/internal/service/packageprice"
	"github.com/break/junhong_cmp_fiber/internal/store"
	"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"
	"github.com/break/junhong_cmp_fiber/pkg/middleware"
)

type Service struct {
	packageStore              *postgres.PackageStore
	packageSeriesStore        *postgres.PackageSeriesStore
	packageAllocationStore    *postgres.ShopPackageAllocationStore
	shopSeriesAllocationStore *postgres.ShopSeriesAllocationStore
}

func New(
	packageStore *postgres.PackageStore,
	packageSeriesStore *postgres.PackageSeriesStore,
	packageAllocationStore *postgres.ShopPackageAllocationStore,
	shopSeriesAllocationStore *postgres.ShopSeriesAllocationStore,
) *Service {
	return &Service{
		packageStore:              packageStore,
		packageSeriesStore:        packageSeriesStore,
		packageAllocationStore:    packageAllocationStore,
		shopSeriesAllocationStore: shopSeriesAllocationStore,
	}
}

func (s *Service) Create(ctx context.Context, req *dto.CreatePackageRequest) (*dto.PackageResponse, error) {
	currentUserID := middleware.GetUserIDFromContext(ctx)
	if currentUserID == 0 {
		return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
	}
	if err := validateGiftPackagePermission(ctx, req.IsGift); err != nil {
		return nil, err
	}

	existing, _ := s.packageStore.GetByCode(ctx, req.PackageCode)
	if existing != nil {
		return nil, errors.New(errors.CodeConflict, "套餐编码已存在")
	}

	// 校验虚流量配置：启用时虚流量必须 > 0 且 ≤ 真流量
	if req.EnableVirtualData {
		if req.VirtualDataMB == nil || *req.VirtualDataMB <= 0 {
			return nil, errors.New(errors.CodeInvalidParam, "启用虚流量时，虚流量额度必须大于0")
		}
		realDataMB := int64(0)
		if req.RealDataMB != nil {
			realDataMB = *req.RealDataMB
		}
		if *req.VirtualDataMB > realDataMB {
			return nil, errors.New(errors.CodeInvalidParam, "虚流量额度不能大于真流量额度")
		}
	}

	// 校验套餐周期类型和时长配置
	calendarType := constants.PackageCalendarTypeByDay // 默认按天
	if req.CalendarType != nil {
		calendarType = *req.CalendarType
	}
	if calendarType == constants.PackageCalendarTypeNaturalMonth {
		// 自然月套餐：必须提供 duration_months
		if req.DurationMonths <= 0 {
			return nil, errors.New(errors.CodeInvalidParam, "自然月套餐必须提供有效的duration_months")
		}
	} else if calendarType == constants.PackageCalendarTypeByDay {
		// 按天套餐：必须提供 duration_days
		if req.DurationDays == nil || *req.DurationDays <= 0 {
			return nil, errors.New(errors.CodeInvalidParam, "按天套餐必须提供有效的duration_days")
		}
	}

	var seriesName *string
	if req.SeriesID != nil && *req.SeriesID > 0 {
		series, err := s.packageSeriesStore.GetByID(ctx, *req.SeriesID)
		if err != nil {
			if err == gorm.ErrRecordNotFound {
				return nil, errors.New(errors.CodeNotFound, "套餐系列不存在")
			}
			return nil, errors.Wrap(errors.CodeInternalError, err, "获取套餐系列失败")
		}
		seriesName = &series.SeriesName
	}

	priceConfigStatus, storedRetailPrice, err := packageprice.ResolvePackagePriceState(req.IsGift, req.SuggestedRetailPrice)
	if err != nil {
		return nil, err
	}

	pkg := &model.Package{
		PackageCode:          req.PackageCode,
		PackageName:          req.PackageName,
		PackageType:          req.PackageType,
		IsGift:               req.IsGift,
		DurationMonths:       req.DurationMonths,
		CostPrice:            req.CostPrice,
		PriceConfigStatus:    priceConfigStatus,
		SuggestedRetailPrice: storedRetailPrice,
		EnableVirtualData:    req.EnableVirtualData,
		CalendarType:         calendarType,
		Status:               constants.StatusEnabled,
		ShelfStatus:          2,
	}
	if req.SeriesID != nil {
		pkg.SeriesID = *req.SeriesID
	}
	if req.RealDataMB != nil {
		pkg.RealDataMB = *req.RealDataMB
	}
	if req.VirtualDataMB != nil {
		pkg.VirtualDataMB = *req.VirtualDataMB
	}
	if req.DurationDays != nil {
		pkg.DurationDays = *req.DurationDays
	}
	if req.DataResetCycle != nil {
		pkg.DataResetCycle = *req.DataResetCycle
	} else {
		// 默认月重置
		pkg.DataResetCycle = constants.PackageDataResetMonthly
	}
	if req.ExpiryBase != "" {
		pkg.ExpiryBase = req.ExpiryBase
	} else {
		pkg.ExpiryBase = "from_activation"
	}
	pkg.VirtualRatio = calculateVirtualRatio(pkg.EnableVirtualData, pkg.RealDataMB, pkg.VirtualDataMB)
	pkg.Creator = currentUserID

	if err := s.packageStore.Create(ctx, pkg); err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "创建套餐失败")
	}

	resp := s.toResponse(ctx, pkg)
	resp.SeriesName = seriesName
	return resp, nil
}

func (s *Service) Get(ctx context.Context, id uint) (*dto.PackageResponse, error) {
	pkg, err := s.packageStore.GetByID(ctx, id)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeNotFound, "套餐不存在")
		}
		return nil, errors.Wrap(errors.CodeInternalError, err, "获取套餐失败")
	}

	resp := s.toResponse(ctx, pkg)
	// 查询系列名称
	if pkg.SeriesID > 0 {
		series, err := s.packageSeriesStore.GetByID(ctx, pkg.SeriesID)
		if err == nil {
			resp.SeriesName = &series.SeriesName

			// 代理用户：增强佣金信息（与 List 接口保持一致）
			userType := middleware.GetUserTypeFromContext(ctx)
			shopID := middleware.GetShopIDFromContext(ctx)
			if userType == constants.UserTypeAgent && shopID > 0 && series.EnableOneTimeCommission {
				config, _ := series.GetOneTimeCommissionConfig()
				if config != nil && config.Enable {
					allocations, saErr := s.shopSeriesAllocationStore.GetByShopID(ctx, shopID)
					if saErr == nil {
						seriesAllocationMap := make(map[uint]*model.ShopSeriesAllocation)
						seriesConfigMap := make(map[uint]*model.OneTimeCommissionConfig)
						for _, alloc := range allocations {
							if alloc.SeriesID == pkg.SeriesID {
								seriesAllocationMap[alloc.SeriesID] = alloc
								break
							}
						}
						seriesConfigMap[pkg.SeriesID] = config
						s.fillCommissionInfo(resp, pkg.SeriesID, seriesAllocationMap, seriesConfigMap)
					}
				}
			}
		}
	}
	return resp, nil
}

func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdatePackageRequest) (*dto.PackageResponse, error) {
	currentUserID := middleware.GetUserIDFromContext(ctx)
	if currentUserID == 0 {
		return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
	}

	pkg, err := s.packageStore.GetByID(ctx, id)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeNotFound, "套餐不存在")
		}
		return nil, errors.Wrap(errors.CodeInternalError, err, "获取套餐失败")
	}

	var seriesName *string
	if req.SeriesID != nil && *req.SeriesID > 0 {
		series, err := s.packageSeriesStore.GetByID(ctx, *req.SeriesID)
		if err != nil {
			if err == gorm.ErrRecordNotFound {
				return nil, errors.New(errors.CodeNotFound, "套餐系列不存在")
			}
			return nil, errors.Wrap(errors.CodeInternalError, err, "获取套餐系列失败")
		}
		pkg.SeriesID = *req.SeriesID
		seriesName = &series.SeriesName
	} else if pkg.SeriesID > 0 {
		series, err := s.packageSeriesStore.GetByID(ctx, pkg.SeriesID)
		if err == nil {
			seriesName = &series.SeriesName
		}
	}

	if req.PackageName != nil {
		pkg.PackageName = *req.PackageName
	}
	if req.PackageType != nil {
		pkg.PackageType = *req.PackageType
	}
	nextIsGift := pkg.IsGift
	if req.IsGift != nil {
		nextIsGift = *req.IsGift
	}
	if err := validateGiftPackagePermission(ctx, nextIsGift); err != nil {
		return nil, err
	}
	if req.DurationMonths != nil {
		pkg.DurationMonths = *req.DurationMonths
	}
	if req.RealDataMB != nil {
		pkg.RealDataMB = *req.RealDataMB
	}
	if req.VirtualDataMB != nil {
		pkg.VirtualDataMB = *req.VirtualDataMB
	}
	if req.EnableVirtualData != nil {
		pkg.EnableVirtualData = *req.EnableVirtualData
	}
	if req.CostPrice != nil {
		pkg.CostPrice = *req.CostPrice
	}
	if req.CalendarType != nil {
		pkg.CalendarType = *req.CalendarType
	}
	if req.DurationDays != nil {
		pkg.DurationDays = *req.DurationDays
	}
	if req.DataResetCycle != nil {
		pkg.DataResetCycle = *req.DataResetCycle
	}
	if req.ExpiryBase != nil {
		pkg.ExpiryBase = *req.ExpiryBase
	}

	var rawSuggestedRetailPrice *int64
	switch {
	case req.ClearSuggestedRetailPrice:
		rawSuggestedRetailPrice = nil
	case req.SuggestedRetailPrice != nil:
		rawSuggestedRetailPrice = req.SuggestedRetailPrice
	default:
		rawSuggestedRetailPrice = packageprice.PackageRawSuggestedRetailPrice(pkg)
	}
	priceConfigStatus, storedRetailPrice, err := packageprice.ResolvePackagePriceState(nextIsGift, rawSuggestedRetailPrice)
	if err != nil {
		return nil, err
	}
	pkg.IsGift = nextIsGift
	pkg.PriceConfigStatus = priceConfigStatus
	pkg.SuggestedRetailPrice = storedRetailPrice

	// 校验套餐周期类型和时长配置
	if pkg.CalendarType == constants.PackageCalendarTypeNaturalMonth {
		if pkg.DurationMonths <= 0 {
			return nil, errors.New(errors.CodeInvalidParam, "自然月套餐必须提供有效的duration_months")
		}
	} else if pkg.CalendarType == constants.PackageCalendarTypeByDay {
		if pkg.DurationDays <= 0 {
			return nil, errors.New(errors.CodeInvalidParam, "按天套餐必须提供有效的duration_days")
		}
	}

	// 校验虚流量配置
	if pkg.EnableVirtualData {
		if pkg.VirtualDataMB <= 0 {
			return nil, errors.New(errors.CodeInvalidParam, "启用虚流量时，虚流量额度必须大于0")
		}
		if pkg.VirtualDataMB > pkg.RealDataMB {
			return nil, errors.New(errors.CodeInvalidParam, "虚流量额度不能大于真流量额度")
		}
	}

	pkg.VirtualRatio = calculateVirtualRatio(pkg.EnableVirtualData, pkg.RealDataMB, pkg.VirtualDataMB)
	pkg.Updater = currentUserID

	if err := s.packageStore.Update(ctx, pkg); err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "更新套餐失败")
	}

	resp := s.toResponse(ctx, pkg)
	resp.SeriesName = seriesName
	return resp, nil
}

func (s *Service) Delete(ctx context.Context, id uint) error {
	_, err := s.packageStore.GetByID(ctx, id)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return errors.New(errors.CodeNotFound, "套餐不存在")
		}
		return errors.Wrap(errors.CodeInternalError, err, "获取套餐失败")
	}

	if err := s.packageStore.Delete(ctx, id); err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "删除套餐失败")
	}

	return nil
}

func (s *Service) List(ctx context.Context, req *dto.PackageListRequest) ([]*dto.PackageResponse, int64, error) {
	opts := &store.QueryOptions{
		Page:     req.Page,
		PageSize: req.PageSize,
		OrderBy:  "id DESC",
	}
	if opts.Page == 0 {
		opts.Page = 1
	}
	if opts.PageSize == 0 {
		opts.PageSize = constants.DefaultPageSize
	}

	filters := make(map[string]interface{})
	if req.PackageName != nil {
		filters["package_name"] = *req.PackageName
	}
	if req.SeriesID != nil {
		filters["series_id"] = *req.SeriesID
	}
	if req.Status != nil {
		filters["status"] = *req.Status
	}
	if req.ShelfStatus != nil {
		filters["shelf_status"] = *req.ShelfStatus
	}
	if req.PackageType != nil {
		filters["package_type"] = *req.PackageType
	}

	packages, total, err := s.packageStore.List(ctx, opts, filters)
	if err != nil {
		return nil, 0, errors.Wrap(errors.CodeInternalError, err, "查询套餐列表失败")
	}

	// 收集所有唯一的 series_id 和 package_id
	seriesIDMap := make(map[uint]bool)
	packageIDs := make([]uint, len(packages))
	for i, pkg := range packages {
		packageIDs[i] = pkg.ID
		if pkg.SeriesID > 0 {
			seriesIDMap[pkg.SeriesID] = true
		}
	}

	// 批量查询套餐系列（名称和配置）
	seriesMap := make(map[uint]string)
	seriesConfigMap := make(map[uint]*model.OneTimeCommissionConfig)
	if len(seriesIDMap) > 0 {
		seriesIDs := make([]uint, 0, len(seriesIDMap))
		for id := range seriesIDMap {
			seriesIDs = append(seriesIDs, id)
		}
		seriesList, err := s.packageSeriesStore.GetByIDs(ctx, seriesIDs)
		if err != nil {
			return nil, 0, errors.Wrap(errors.CodeInternalError, err, "批量查询套餐系列失败")
		}
		for _, series := range seriesList {
			seriesMap[series.ID] = series.SeriesName
			// 解析一次性佣金配置
			if series.EnableOneTimeCommission {
				config, _ := series.GetOneTimeCommissionConfig()
				if config != nil {
					seriesConfigMap[series.ID] = config
				}
			}
		}
	}

	userType := middleware.GetUserTypeFromContext(ctx)
	shopID := middleware.GetShopIDFromContext(ctx)
	var allocationMap map[uint]*model.ShopPackageAllocation
	var seriesAllocationMap map[uint]*model.ShopSeriesAllocation
	if userType == constants.UserTypeAgent && shopID > 0 && len(packageIDs) > 0 {
		allocationMap = s.batchGetAllocationsForShop(ctx, shopID, packageIDs)
		// 批量获取店铺的系列分配
		seriesAllocationMap = s.batchGetSeriesAllocationsForShop(ctx, shopID, seriesIDMap)
	}

	responses := make([]*dto.PackageResponse, len(packages))
	for i, pkg := range packages {
		resp := s.toResponseWithAllocation(ctx, pkg, allocationMap, seriesAllocationMap, seriesConfigMap)
		if pkg.SeriesID > 0 {
			if seriesName, ok := seriesMap[pkg.SeriesID]; ok {
				resp.SeriesName = &seriesName
			}
		}
		responses[i] = resp
	}

	return responses, total, nil
}

// batchGetSeriesAllocationsForShop 批量获取店铺的系列分配
func (s *Service) batchGetSeriesAllocationsForShop(ctx context.Context, shopID uint, seriesIDMap map[uint]bool) map[uint]*model.ShopSeriesAllocation {
	result := make(map[uint]*model.ShopSeriesAllocation)
	if len(seriesIDMap) == 0 {
		return result
	}

	allocations, err := s.shopSeriesAllocationStore.GetByShopID(ctx, shopID)
	if err != nil || len(allocations) == 0 {
		return result
	}

	for _, alloc := range allocations {
		if seriesIDMap[alloc.SeriesID] {
			result[alloc.SeriesID] = alloc
		}
	}

	return result
}

func (s *Service) UpdateStatus(ctx context.Context, id uint, status int) error {
	currentUserID := middleware.GetUserIDFromContext(ctx)
	if currentUserID == 0 {
		return errors.New(errors.CodeUnauthorized, "未授权访问")
	}

	pkg, err := s.packageStore.GetByID(ctx, id)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return errors.New(errors.CodeNotFound, "套餐不存在")
		}
		return errors.Wrap(errors.CodeInternalError, err, "获取套餐失败")
	}

	pkg.Status = status
	pkg.Updater = currentUserID

	if status == constants.StatusDisabled {
		pkg.ShelfStatus = 2
	}

	if err := s.packageStore.Update(ctx, pkg); err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "更新套餐状态失败")
	}

	return nil
}

func (s *Service) UpdateShelfStatus(ctx context.Context, id uint, shelfStatus int) error {
	currentUserID := middleware.GetUserIDFromContext(ctx)
	if currentUserID == 0 {
		return errors.New(errors.CodeUnauthorized, "未授权访问")
	}

	userType := middleware.GetUserTypeFromContext(ctx)

	// 代理用户：修改自己分配记录的 shelf_status，不影响平台全局套餐状态
	if userType == constants.UserTypeAgent {
		return s.updateAgentShelfStatus(ctx, id, shelfStatus, currentUserID)
	}

	// 平台/超管：修改套餐全局 shelf_status
	pkg, err := s.packageStore.GetByID(ctx, id)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return errors.New(errors.CodeNotFound, "套餐不存在")
		}
		return errors.Wrap(errors.CodeInternalError, err, "获取套餐失败")
	}

	if shelfStatus == constants.ShelfStatusOn && pkg.Status == constants.StatusDisabled {
		return errors.New(errors.CodeInvalidStatus, "禁用的套餐不能上架，请先启用")
	}

	pkg.ShelfStatus = shelfStatus
	pkg.Updater = currentUserID

	if err := s.packageStore.Update(ctx, pkg); err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "更新套餐上架状态失败")
	}

	return nil
}

// UpdateRetailPrice 代理修改自己店铺的套餐零售价
func (s *Service) UpdateRetailPrice(ctx context.Context, packageID uint, retailPrice int64) error {
	currentUserID := middleware.GetUserIDFromContext(ctx)
	if currentUserID == 0 {
		return errors.New(errors.CodeUnauthorized, "未授权访问")
	}

	userType := middleware.GetUserTypeFromContext(ctx)
	if userType != constants.UserTypeAgent {
		return errors.New(errors.CodeForbidden, "仅代理用户可修改零售价")
	}

	shopID := middleware.GetShopIDFromContext(ctx)
	if shopID == 0 {
		return errors.New(errors.CodeUnauthorized, "当前用户不属于任何店铺")
	}

	allocation, err := s.packageAllocationStore.GetByShopAndPackage(ctx, shopID, packageID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return errors.New(errors.CodeNotFound, "该套餐未分配给您")
		}
		return errors.Wrap(errors.CodeInternalError, err, "获取分配记录失败")
	}

	pkg, err := s.packageStore.GetByID(ctx, packageID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return errors.New(errors.CodeNotFound, "套餐不存在")
		}
		return errors.Wrap(errors.CodeInternalError, err, "获取套餐失败")
	}
	if pkg.IsGift {
		return errors.New(errors.CodeForbidden, "赠送套餐不允许代理修改零售价")
	}
	priceConfigStatus, storedRetailPrice, err := packageprice.ResolveAllocationPriceState(&retailPrice)
	if err != nil {
		return err
	}
	if retailPrice < allocation.CostPrice {
		return errors.New(errors.CodeInvalidParam, "零售价不能低于成本价")
	}

	if err := s.packageAllocationStore.UpdateRetailPrice(ctx, allocation.ID, storedRetailPrice, priceConfigStatus, currentUserID); err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "更新零售价失败")
	}

	return nil
}

// updateAgentShelfStatus 代理上下架路径：更新分配记录的 shelf_status
func (s *Service) updateAgentShelfStatus(ctx context.Context, packageID uint, shelfStatus int, updaterID uint) error {
	shopID := middleware.GetShopIDFromContext(ctx)
	if shopID == 0 {
		return errors.New(errors.CodeUnauthorized, "当前用户不属于任何店铺")
	}

	// 查找代理对该套餐的分配记录
	allocation, err := s.packageAllocationStore.GetByShopAndPackage(ctx, shopID, packageID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return errors.New(errors.CodeNotFound, "该套餐未分配给您，无法操作上下架")
		}
		return errors.Wrap(errors.CodeInternalError, err, "获取分配记录失败")
	}

	// 上架时检查套餐全局禁用状态
	if shelfStatus == constants.ShelfStatusOn {
		pkg, err := s.packageStore.GetByID(ctx, packageID)
		if err != nil {
			if err == gorm.ErrRecordNotFound {
				return errors.New(errors.CodeNotFound, "套餐不存在")
			}
			return errors.Wrap(errors.CodeInternalError, err, "获取套餐失败")
		}
		if pkg.Status == constants.StatusDisabled {
			return errors.New(errors.CodeInvalidStatus, "套餐已禁用，无法上架")
		}
	}

	if err := s.packageAllocationStore.UpdateShelfStatus(ctx, allocation.ID, shelfStatus, updaterID); err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "更新上下架状态失败")
	}

	return nil
}

func (s *Service) toResponse(ctx context.Context, pkg *model.Package) *dto.PackageResponse {
	var seriesID *uint
	if pkg.SeriesID > 0 {
		seriesID = &pkg.SeriesID
	}

	var durationDays *int
	if pkg.CalendarType == constants.PackageCalendarTypeByDay && pkg.DurationDays > 0 {
		durationDays = &pkg.DurationDays
	}

	resp := &dto.PackageResponse{
		ID:                      pkg.ID,
		PackageCode:             pkg.PackageCode,
		PackageName:             pkg.PackageName,
		SeriesID:                seriesID,
		PackageType:             pkg.PackageType,
		IsGift:                  pkg.IsGift,
		DurationMonths:          pkg.DurationMonths,
		RealDataMB:              pkg.RealDataMB,
		VirtualDataMB:           pkg.VirtualDataMB,
		EnableVirtualData:       pkg.EnableVirtualData,
		VirtualRatio:            calculateVirtualRatio(pkg.EnableVirtualData, pkg.RealDataMB, pkg.VirtualDataMB),
		CostPrice:               pkg.CostPrice,
		SuggestedRetailPrice:    packageprice.PackageRawSuggestedRetailPrice(pkg),
		PriceConfigStatus:       pkg.PriceConfigStatus,
		PriceConfigStatusName:   packagePriceConfigStatusName(pkg.PriceConfigStatus),
		CalendarType:            pkg.CalendarType,
		DurationDays:            durationDays,
		DataResetCycle:          pkg.DataResetCycle,
		ExpiryBase:  pkg.ExpiryBase,
		Status:      pkg.Status,
		ShelfStatus: pkg.ShelfStatus,
		CreatedAt:   pkg.CreatedAt.Format(time.RFC3339),
		UpdatedAt:   pkg.UpdatedAt.Format(time.RFC3339),
	}
	initPackageExpiryBaseFields(resp, pkg)

	userType := middleware.GetUserTypeFromContext(ctx)
	shopID := middleware.GetShopIDFromContext(ctx)
	if userType == constants.UserTypeAgent && shopID > 0 {
		allocation, err := s.packageAllocationStore.GetByShopAndPackage(ctx, shopID, pkg.ID)
		if err == nil && allocation != nil {
			resp.CostPrice = allocation.CostPrice
			resp.RetailPrice = packageprice.AllocationRawRetailPrice(allocation)
			retailPriceConfigStatus := allocation.RetailPriceConfigStatus
			resp.RetailPriceConfigStatus = &retailPriceConfigStatus
			effectiveRetailPrice := packageprice.AllocationEffectiveRetailPrice(allocation)
			resp.EffectiveRetailPrice = &effectiveRetailPrice
			profitMargin := effectiveRetailPrice - allocation.CostPrice
			resp.ProfitMargin = &profitMargin
			resp.ShelfStatus = allocation.ShelfStatus
			applyAllocationExpiryBase(resp, pkg, allocation)
		}
	} else {
		effectiveRetailPrice := packageprice.PackageEffectiveRetailPrice(pkg)
		resp.EffectiveRetailPrice = &effectiveRetailPrice
	}

	resp.StatusName = constants.GetStatusName(resp.Status)
	resp.ShelfStatusName = constants.GetShelfStatusName(resp.ShelfStatus)
	return resp
}

func (s *Service) batchGetAllocationsForShop(ctx context.Context, shopID uint, packageIDs []uint) map[uint]*model.ShopPackageAllocation {
	allocationMap := make(map[uint]*model.ShopPackageAllocation)

	allocations, err := s.packageAllocationStore.GetByShopAndPackages(ctx, shopID, packageIDs)
	if err != nil || len(allocations) == 0 {
		return allocationMap
	}

	for _, alloc := range allocations {
		allocationMap[alloc.PackageID] = alloc
	}

	return allocationMap
}

func (s *Service) toResponseWithAllocation(_ context.Context, pkg *model.Package, allocationMap map[uint]*model.ShopPackageAllocation, seriesAllocationMap map[uint]*model.ShopSeriesAllocation, seriesConfigMap map[uint]*model.OneTimeCommissionConfig) *dto.PackageResponse {
	var seriesID *uint
	if pkg.SeriesID > 0 {
		seriesID = &pkg.SeriesID
	}

	var durationDays *int
	if pkg.CalendarType == constants.PackageCalendarTypeByDay && pkg.DurationDays > 0 {
		durationDays = &pkg.DurationDays
	}

	resp := &dto.PackageResponse{
		ID:                      pkg.ID,
		PackageCode:             pkg.PackageCode,
		PackageName:             pkg.PackageName,
		SeriesID:                seriesID,
		PackageType:             pkg.PackageType,
		IsGift:                  pkg.IsGift,
		DurationMonths:          pkg.DurationMonths,
		RealDataMB:              pkg.RealDataMB,
		VirtualDataMB:           pkg.VirtualDataMB,
		EnableVirtualData:       pkg.EnableVirtualData,
		VirtualRatio:            calculateVirtualRatio(pkg.EnableVirtualData, pkg.RealDataMB, pkg.VirtualDataMB),
		CostPrice:               pkg.CostPrice,
		SuggestedRetailPrice:    packageprice.PackageRawSuggestedRetailPrice(pkg),
		PriceConfigStatus:       pkg.PriceConfigStatus,
		PriceConfigStatusName:   packagePriceConfigStatusName(pkg.PriceConfigStatus),
		CalendarType:            pkg.CalendarType,
		DurationDays:            durationDays,
		DataResetCycle:          pkg.DataResetCycle,
		ExpiryBase:  pkg.ExpiryBase,
		Status:      pkg.Status,
		ShelfStatus: pkg.ShelfStatus,
		CreatedAt:   pkg.CreatedAt.Format(time.RFC3339),
		UpdatedAt:   pkg.UpdatedAt.Format(time.RFC3339),
	}
	initPackageExpiryBaseFields(resp, pkg)

	if allocationMap != nil {
		if allocation, ok := allocationMap[pkg.ID]; ok {
			resp.CostPrice = allocation.CostPrice
			resp.RetailPrice = packageprice.AllocationRawRetailPrice(allocation)
			retailPriceConfigStatus := allocation.RetailPriceConfigStatus
			resp.RetailPriceConfigStatus = &retailPriceConfigStatus
			effectiveRetailPrice := packageprice.AllocationEffectiveRetailPrice(allocation)
			resp.EffectiveRetailPrice = &effectiveRetailPrice
			profitMargin := effectiveRetailPrice - allocation.CostPrice
			resp.ProfitMargin = &profitMargin
			applyAllocationExpiryBase(resp, pkg, allocation)
			resp.ShelfStatus = allocation.ShelfStatus
		}
	} else {
		effectiveRetailPrice := packageprice.PackageEffectiveRetailPrice(pkg)
		resp.EffectiveRetailPrice = &effectiveRetailPrice
	}

	// 填充返佣信息（仅代理用户可见）
	if pkg.SeriesID > 0 && seriesAllocationMap != nil && seriesConfigMap != nil {
		s.fillCommissionInfo(resp, pkg.SeriesID, seriesAllocationMap, seriesConfigMap)
	}

	return resp
}

// initPackageExpiryBaseFields 初始化响应中生效条件默认字段（无分配覆盖时跟随套餐默认）。
func initPackageExpiryBaseFields(resp *dto.PackageResponse, pkg *model.Package) {
	resp.DefaultExpiryBase = pkg.ExpiryBase
	resp.DefaultExpiryBaseName = ExpiryBaseName(pkg.ExpiryBase)
	resp.EffectiveExpiryBase = pkg.ExpiryBase
	resp.EffectiveExpiryBaseName = ExpiryBaseName(pkg.ExpiryBase)
}

func applyAllocationExpiryBase(resp *dto.PackageResponse, pkg *model.Package, allocation *model.ShopPackageAllocation) {
	resp.ExpiryBaseOverride = allocation.ExpiryBaseOverride
	resp.ExpiryBaseOverrideName = ExpiryBaseOverrideName(allocation.ExpiryBaseOverride)
	resp.EffectiveExpiryBase = EffectiveExpiryBase(pkg, allocation)
	resp.EffectiveExpiryBaseName = ExpiryBaseName(resp.EffectiveExpiryBase)
}

// fillCommissionInfo 填充返佣信息到响应中
func (s *Service) fillCommissionInfo(resp *dto.PackageResponse, seriesID uint, seriesAllocationMap map[uint]*model.ShopSeriesAllocation, seriesConfigMap map[uint]*model.OneTimeCommissionConfig) {
	seriesAllocation, hasAllocation := seriesAllocationMap[seriesID]
	config, hasConfig := seriesConfigMap[seriesID]

	if !hasAllocation || !hasConfig {
		return
	}

	// 一次性佣金是否启用由 PackageSeries.enable_one_time_commission 控制
	if !config.Enable {
		return
	}

	// 设置一次性佣金金额
	oneTimeAmount := seriesAllocation.OneTimeCommissionAmount
	resp.OneTimeCommissionAmount = &oneTimeAmount

	// 设置当前返佣比例（格式化为可读字符串）
	if config.CommissionType == "fixed" {
		// 固定金额模式：显示代理能拿到的金额
		resp.CurrentCommissionRate = formatAmount(seriesAllocation.OneTimeCommissionAmount)
	} else if config.CommissionType == "tiered" && len(config.Tiers) > 0 {
		// 梯度模式：显示基础金额，并设置梯度信息
		resp.CurrentCommissionRate = formatAmount(seriesAllocation.OneTimeCommissionAmount)

		// 构建梯度信息
		tierInfo := s.buildTierInfo(config.Tiers, seriesAllocation.OneTimeCommissionAmount)
		if tierInfo != nil {
			resp.TierInfo = tierInfo
		}
	}
}

// buildTierInfo 构建梯度返佣信息
func (s *Service) buildTierInfo(tiers []model.OneTimeCommissionTier, currentAmount int64) *dto.CommissionTierInfo {
	if len(tiers) == 0 {
		return nil
	}

	tierInfo := &dto.CommissionTierInfo{
		CurrentRate: formatAmount(currentAmount),
	}

	// 找到下一个可达到的梯度
	// 梯度按 threshold 升序排列，找到第一个 amount > currentAmount 的梯度
	for _, tier := range tiers {
		if tier.Amount > currentAmount {
			tierInfo.NextThreshold = &tier.Threshold
			nextRate := formatAmount(tier.Amount)
			tierInfo.NextRate = nextRate
			break
		}
	}

	return tierInfo
}

func validateGiftPackagePermission(ctx context.Context, isGift bool) error {
	if !isGift {
		return nil
	}
	userType := middleware.GetUserTypeFromContext(ctx)
	if userType != constants.UserTypeSuperAdmin && userType != constants.UserTypePlatform {
		return errors.New(errors.CodeForbidden, "仅平台侧可创建或维护赠送套餐")
	}
	return nil
}

func packagePriceConfigStatusName(status int) string {
	switch status {
	case constants.PackagePriceConfigStatusGiftZero:
		return "赠送0价"
	case constants.PackagePriceConfigStatusConfigured:
		return "已配置非0"
	default:
		return "未配置"
	}
}

// formatAmount 格式化金额为可读字符串（分转元）
func formatAmount(amountFen int64) string {
	yuan := float64(amountFen) / 100
	if yuan == float64(int64(yuan)) {
		return fmt.Sprintf("%.0f元/张", yuan)
	}
	return fmt.Sprintf("%.2f元/张", yuan)
}

// calculateVirtualRatio 计算虚流量比例
// enable_virtual_data=true 且 virtual_data_mb>0 时 = real_data_mb/virtual_data_mb，否则 = 1.0
func calculateVirtualRatio(enableVirtualData bool, realDataMB, virtualDataMB int64) float64 {
	return model.CalculateDisplayGainRatio(enableVirtualData, realDataMB, virtualDataMB)
}
