package logic

import (
	"encoding/json"
	"errors"
	"fmt"
	"fusenapi/constants"
	"fusenapi/model/gmodel"
	"fusenapi/utils/auth"
	"fusenapi/utils/basic"
	"fusenapi/utils/format"
	"gorm.io/gorm"

	"context"

	"fusenapi/server/product/internal/svc"
	"fusenapi/server/product/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
)

type CalculateProductPriceLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewCalculateProductPriceLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CalculateProductPriceLogic {
	return &CalculateProductPriceLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

// 处理进入前逻辑w,r
// func (l *CalculateProductPriceLogic) BeforeLogic(w http.ResponseWriter, r *http.Request) {
// }

func (l *CalculateProductPriceLogic) CalculateProductPrice(req *types.CalculateProductPriceReq, userinfo *auth.UserInfo) (resp *basic.Response) {
	if req.ProductId <= 0 {
		return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "err param:product id")
	}
	if req.SizeId <= 0 {
		return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "err param:size id")
	}
	if req.PurchaseQuantity <= 0 {
		return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "err param:purchase quantity")
	}
	//获取产品信息(只是获取id)
	_, err := l.svcCtx.AllModels.FsProduct.FindOne(l.ctx, req.ProductId, "id")
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return resp.SetStatusWithMessage(basic.CodeDbRecordNotFoundErr, "the product is not exists")
		}
		logx.Error(err)
		return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get product info")
	}
	//根据产品跟尺寸获取模型价格信息
	modelInfo, err := l.svcCtx.AllModels.FsProductModel3d.FindOneByProductIdSizeIdTag(l.ctx, req.ProductId, req.SizeId, constants.TAG_MODEL, "id,step_price")
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return resp.SetStatusWithMessage(basic.CodeDbRecordNotFoundErr, "model info is not exists")
		}
		return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get model info")
	}
	var stepPrice gmodel.StepPriceJsonStruct
	if modelInfo.StepPrice != nil && len(*modelInfo.StepPrice) != 0 {
		if err = json.Unmarshal(*modelInfo.StepPrice, &stepPrice); err != nil {
			logx.Error(err)
			return resp.SetStatusWithMessage(basic.CodeJsonErr, "failed to parse step price")
		}
	}
	//配件
	fittingPrice := int64(0)
	if req.FittingId > 0 {
		fittingInfo, err := l.svcCtx.AllModels.FsProductModel3d.FindOne(l.ctx, req.FittingId, "id,price")
		if err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				return resp.SetStatusWithMessage(basic.CodeDbRecordNotFoundErr, "fitting info is not exists")
			}
			logx.Error(err)
			return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get fitting info")
		}
		fittingPrice = *fittingInfo.Price
	}
	rangeLen := len(stepPrice.PriceRange)
	stepRange := make([]interface{}, 0, rangeLen)
	for rIndex, rangeInfo := range stepPrice.PriceRange {
		//最后一个
		if rIndex+1 == rangeLen {
			begin := format.NumToStringWithThousandthPercentile(fmt.Sprintf("%d", rangeInfo.StartQuantity))
			stepRange = append(stepRange, map[string]interface{}{
				"start":             rangeInfo.StartQuantity,
				"end":               rangeInfo.EndQuantity,
				"range_description": fmt.Sprintf(">=%s Units", begin),
				"item_price":        format.CentitoDollar(rangeInfo.Price+fittingPrice, 3),
			})
			break
		}
		begin := format.NumToStringWithThousandthPercentile(fmt.Sprintf("%d", rangeInfo.StartQuantity))
		end := format.NumToStringWithThousandthPercentile(fmt.Sprintf("%d", rangeInfo.EndQuantity))
		stepRange = append(stepRange, map[string]interface{}{
			"start":             rangeInfo.StartQuantity,
			"end":               rangeInfo.EndQuantity,
			"range_description": fmt.Sprintf("%s-%s Units", begin, end),
			"item_price":        format.CentitoDollar(rangeInfo.Price+fittingPrice, 3),
		})
	}
	totalPrice, itemPrice, err := l.svcCtx.Repositories.NewShoppingCart.CaculateStepPrice(req.PurchaseQuantity, stepPrice, fittingPrice)
	if err != nil {
		logx.Error(err)
		return resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to calculate product price ")
	}
	return resp.SetStatusWithMessage(basic.CodeOK, "success", types.CalculateProductPriceRsp{
		ItemPrice:  format.NumToStringWithThousandthPercentile(format.CentitoDollar(itemPrice, 3)),
		TotalPrice: format.NumToStringWithThousandthPercentile(format.CentitoDollarWithNoHalfAdjust(totalPrice, 2)),
		StepRange:  stepRange,
	})
}

// 处理逻辑后 w,r 如:重定向, resp 必须重新处理
// func (l *CalculateProductPriceLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
// // httpx.OkJsonCtx(r.Context(), w, resp)
// }