package shop_package_batch_allocation

import (
	"context"

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

type Service struct {
	db                     *gorm.DB
	packageStore           *postgres.PackageStore
	packageAllocationStore *postgres.ShopPackageAllocationStore
	seriesAllocationStore  *postgres.ShopSeriesAllocationStore
	shopStore              *postgres.ShopStore
}

func New(
	db *gorm.DB,
	packageStore *postgres.PackageStore,
	packageAllocationStore *postgres.ShopPackageAllocationStore,
	seriesAllocationStore *postgres.ShopSeriesAllocationStore,
	shopStore *postgres.ShopStore,
) *Service {
	return &Service{
		db:                     db,
		packageStore:           packageStore,
		packageAllocationStore: packageAllocationStore,
		seriesAllocationStore:  seriesAllocationStore,
		shopStore:              shopStore,
	}
}

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

	userType := middleware.GetUserTypeFromContext(ctx)
	allocatorShopID := middleware.GetShopIDFromContext(ctx)

	if userType == constants.UserTypeAgent && allocatorShopID == 0 {
		return errors.New(errors.CodeUnauthorized, "当前用户不属于任何店铺")
	}

	targetShop, err := s.shopStore.GetByID(ctx, req.ShopID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return errors.New(errors.CodeNotFound, "目标店铺不存在")
		}
		return errors.Wrap(errors.CodeInternalError, err, "获取目标店铺失败")
	}

	if userType == constants.UserTypeAgent {
		if targetShop.ParentID == nil || *targetShop.ParentID != allocatorShopID {
			return errors.New(errors.CodeForbidden, "只能分配给直属下级店铺")
		}
	}

	packages, err := s.getEnabledPackagesBySeries(ctx, req.SeriesID)
	if err != nil {
		return err
	}

	if len(packages) == 0 {
		return errors.New(errors.CodeInvalidParam, "该系列下没有启用的套餐")
	}

	// 检查目标店铺是否有该系列的分配
	seriesAllocation, err := s.seriesAllocationStore.GetByShopAndSeries(ctx, req.ShopID, req.SeriesID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return errors.New(errors.CodeInvalidParam, "目标店铺没有该系列的分配权限")
		}
		return errors.Wrap(errors.CodeInternalError, err, "查询系列分配失败")
	}

	return s.db.Transaction(func(tx *gorm.DB) error {
		txPkgAllocStore := postgres.NewShopPackageAllocationStore(tx)

		for _, pkg := range packages {
			// 已存在该套餐的分配记录则跳过，避免唯一约束冲突
			_, existErr := txPkgAllocStore.GetByShopAndPackageForSystem(ctx, req.ShopID, pkg.ID)
			if existErr == nil {
				continue
			}

			costPrice := pkg.CostPrice
			if req.PriceAdjustment != nil {
				costPrice = s.calculateAdjustedPrice(pkg.CostPrice, req.PriceAdjustment)
			}

			allocation := &model.ShopPackageAllocation{
				BaseModel:          model.BaseModel{Creator: currentUserID, Updater: currentUserID},
				ShopID:             req.ShopID,
				PackageID:          pkg.ID,
				AllocatorShopID:    allocatorShopID,
				CostPrice:          costPrice,
				RetailPrice:        pkg.SuggestedRetailPrice,
				SeriesAllocationID: &seriesAllocation.ID,
				Status:             constants.StatusEnabled,
			}
			if err := tx.Create(allocation).Error; err != nil {
				return errors.Wrap(errors.CodeInternalError, err, "创建套餐分配失败")
			}
		}

		return nil
	})
}

func (s *Service) getEnabledPackagesBySeries(ctx context.Context, seriesID uint) ([]*model.Package, error) {
	filters := map[string]interface{}{
		"series_id":    seriesID,
		"status":       constants.StatusEnabled,
		"shelf_status": 1,
	}

	packages, _, err := s.packageStore.List(ctx, nil, filters)
	if err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "获取套餐列表失败")
	}

	return packages, nil
}

func (s *Service) calculateAdjustedPrice(basePrice int64, adjustment *dto.PriceAdjustment) int64 {
	if adjustment == nil {
		return basePrice
	}

	if adjustment.Type == "fixed" {
		return basePrice + adjustment.Value
	}

	return basePrice + (basePrice * adjustment.Value / 1000)
}
