package logic

import (
	"encoding/json"
	"errors"
	"fusenapi/constants"
	"fusenapi/model/gmodel"
	"fusenapi/utils/auth"
	"fusenapi/utils/basic"
	"fusenapi/utils/format"
	"fusenapi/utils/image"
	"fusenapi/utils/step_price"
	"strings"
	"sync"
	"time"

	"context"

	"fusenapi/server/shopping-cart-confirmation/internal/svc"
	"fusenapi/server/shopping-cart-confirmation/internal/types"

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

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

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

// 获取用户购物车列表
func (l *CartListLogic) CartList(req *types.CartListReq, userinfo *auth.UserInfo) (resp *basic.Response) {
	if userinfo.GetIdType() != auth.IDTYPE_User {
		return resp.SetStatusWithMessage(basic.CodeUnAuth, "please login first")
	}
	//获取当前图片应该返回的尺寸大小
	if req.Size > 0 {
		req.Size = image.GetCurrentSize(req.Size)
	}
	//获取用户购物车数据
	cartRelativeData, err := l.getUserCartRelativeList(l.ctx, userinfo.UserId)
	if err != nil {
		logx.Error(err)
		return resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to get cart list data")
	}
	if len(cartRelativeData.CartList) == 0 {
		return resp.SetStatusWithMessage(basic.CodeOK, "success")
	}
	//产品名map
	mapProduct := make(map[int64]int)
	for k, v := range cartRelativeData.ProductList {
		mapProduct[v.Id] = k
	}
	//产品尺寸size map
	mapProductSize := make(map[int64]int)
	for k, v := range cartRelativeData.ProductSizeList {
		mapProductSize[v.Id] = k
	}
	//设计map数据
	mapProductDesign := make(map[int64]int)
	for k, v := range cartRelativeData.ProductDesignList {
		mapProductDesign[v.Id] = k
	}
	//产品模型map数据
	mapProductModel3d := make(map[int64]int)
	for k, v := range cartRelativeData.ProductModel3dList {
		mapProductModel3d[v.Id] = k
	}
	//遍历组装数据
	rspList := make([]types.CartListRsp, 0, len(cartRelativeData.CartList))
	for _, v := range cartRelativeData.CartList {
		name := ""
		productSn := ""
		if productIndex, ok := mapProduct[*v.ProductId]; ok {
			name = *cartRelativeData.ProductList[productIndex].Title
			productSn = strings.ToLower(*cartRelativeData.ProductList[productIndex].Sn)
		}
		designSn := ""
		if designIndex, ok := mapProductDesign[*v.DesignId]; ok {
			designSn = strings.ToLower(*cartRelativeData.ProductDesignList[designIndex].Sn)
		}
		pcList, err := l.getPcList(cartRelativeData.ProductPriceList, *v.ProductId, *v.MaterialId, *v.SizeId)
		if err != nil {
			logx.Error(err)
			return resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to  calculate  step price")
		}
		d := types.CartListRsp{
			Id:        v.Id,
			Cover:     *v.Cover,
			Name:      name,
			Capacity:  "",
			ETA:       time.Now().AddDate(0, 0, 60).Format("2006-01-02 15:04:05"),
			Pcs:       *v.BuyNum,
			ProductSn: productSn,
			DesignSn:  designSn,
			Option:    nil,
			IsCheck:   *v.IsCheck,
			TsTime:    v.TsTime.Format("2006-01-02 15:04:05"),
			PcsList:   pcList,
			Size:      nil,
		}
		var sizeList types.CartSizeItem
		if sizeIndex, ok := mapProductSize[*v.SizeId]; ok {
			d.Capacity = *cartRelativeData.ProductSizeList[sizeIndex].Capacity
			err = json.Unmarshal([]byte(*cartRelativeData.ProductSizeList[sizeIndex].Title), &sizeList)
			if err != nil {
				logx.Error(err)
				return resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to parse product size`s title")
			}
			d.Size = &sizeList
		}
		if model3dIndex, ok := mapProductModel3d[*v.OptionalId]; ok {
			d.Option = &types.CartOption{
				Id:    cartRelativeData.ProductModel3dList[model3dIndex].Id,
				Title: *cartRelativeData.ProductModel3dList[model3dIndex].Title,
				Price: float64(*cartRelativeData.ProductModel3dList[model3dIndex].Price) / float64(100),
			}
		}
		rspList = append(rspList, d)

	}
	return resp.SetStatusWithMessage(basic.CodeOK, "success", rspList)
}

type GetUserCartRelativeListRsp struct {
	CartList              []gmodel.FsCart
	ProductDesignList     []gmodel.FsProductDesign
	ProductSizeList       []gmodel.FsProductSize
	ProductList           []gmodel.FsProduct
	ProductModel3dList    []gmodel.FsProductModel3d
	ProductTemplateV2List []gmodel.FsProductTemplateV2
	ProductPriceList      []gmodel.FsProductPrice
}

// 获取阶梯对应的价格
func (l *CartListLogic) getPcList(productPriceList []gmodel.FsProductPrice, productId int64, materialId int64, sizeId int64) (list []*types.PcsItem, err error) {
	for _, price := range productPriceList {
		if *price.ProductId != productId || *price.MaterialId != materialId || *price.SizeId != sizeId {
			continue
		}
		stepNumStrSlice := strings.Split(*price.StepNum, ",")
		stepNumSlice, err := format.StrSlicToIntSlice(stepNumStrSlice)
		if err != nil {
			return nil, err
		}
		stepPriceStrSlice := strings.Split(*price.StepPrice, ",")
		stepPriceSlice, err := format.StrSlicToIntSlice(stepPriceStrSlice)
		if err != nil {
			return nil, err
		}
		lenStepPriceSlice := len(stepPriceSlice)
		lenStepNumSlice := len(stepNumSlice)
		if lenStepPriceSlice == 0 || lenStepNumSlice == 0 {
			return nil, errors.New("step num or step price item count can`t be 0 ")
		}
		for int(*price.MinBuyNum) < stepNumSlice[lenStepNumSlice-1]+5 {
			list = append(list, &types.PcsItem{
				Num:      *price.MinBuyNum,
				TotalNum: *price.MinBuyNum * *price.EachBoxNum,
				Title:    *price.Title,
				Price:    step_price.GetStepPrice(int(*price.MinBuyNum), stepNumSlice, stepPriceSlice),
			})
			*price.MinBuyNum++
		}
	}
	return list, nil
}

// 获取用户购物车数据
func (l *CartListLogic) getUserCartRelativeList(ctx context.Context, userId int64) (resp GetUserCartRelativeListRsp, err error) {
	//购物车列表
	cartModel := gmodel.NewFsCartModel(l.svcCtx.MysqlConn)
	cartList, err := cartModel.GetAllByUserId(ctx, userId, "ctime-desc")
	if err != nil {
		return GetUserCartRelativeListRsp{}, err
	}
	if len(cartList) == 0 {
		return
	}
	resp.CartList = cartList
	productIds := make([]int64, 0, len(cartList))
	sizeIds := make([]int64, 0, len(cartList))
	designIds := make([]int64, 0, len(cartList))
	optionIds := make([]int64, 0, len(cartList))
	templateIds := make([]int64, 0, len(cartList))
	for _, v := range cartList {
		productIds = append(productIds, *v.ProductId)
		sizeIds = append(sizeIds, *v.SizeId)
		designIds = append(designIds, *v.DesignId)
		optionIds = append(optionIds, *v.OptionalId)
		templateIds = append(templateIds, *v.TemplateId)
	}
	//******************************协程并发开始***********************************
	wait := sync.WaitGroup{}
	errChan := make(chan error)
	wait.Add(6) //注意要加协程这里要记得加
	//获取产品数据1
	go func() {
		defer wait.Done()
		productModel := gmodel.NewFsProductModel(l.svcCtx.MysqlConn)
		productList, err := productModel.GetProductListByIds(ctx, productIds, "")
		if err != nil {
			errChan <- err
			return
		}
		resp.ProductList = productList
	}()
	//获取尺寸数据2
	go func() {
		defer wait.Done()
		productSizeModel := gmodel.NewFsProductSizeModel(l.svcCtx.MysqlConn)
		productSizeList, err := productSizeModel.GetAllByIds(ctx, sizeIds, "")
		if err != nil {
			errChan <- err
		}
		resp.ProductSizeList = productSizeList
	}()
	//获取设计列表3
	go func() {
		defer wait.Done()
		productDesignModel := gmodel.NewFsProductDesignModel(l.svcCtx.MysqlConn)
		productDesignList, err := productDesignModel.GetAllByIds(ctx, designIds)
		if err != nil {
			errChan <- err
		}
		resp.ProductDesignList = productDesignList
	}()
	//获取配件列表4
	go func() {
		defer wait.Done()
		productModel3dModel := gmodel.NewFsProductModel3dModel(l.svcCtx.MysqlConn)
		productModel3dList, err := productModel3dModel.GetAllByIdsTag(ctx, optionIds, constants.TAG_PARTS)
		if err != nil {
			errChan <- err
		}
		resp.ProductModel3dList = productModel3dList
	}()
	//获取模板信息5
	go func() {
		defer wait.Done()
		productTemplateModel := gmodel.NewFsProductTemplateV2Model(l.svcCtx.MysqlConn)
		templateV2List, err := productTemplateModel.FindAllByIds(ctx, templateIds)
		if err != nil {
			errChan <- err
		}
		resp.ProductTemplateV2List = templateV2List
	}()
	//获取产品价格列表6
	go func() {
		defer wait.Done()
		productPriceModel := gmodel.NewFsProductPriceModel(l.svcCtx.MysqlConn)
		productPriceList, err := productPriceModel.GetPriceListByProductIds(ctx, productIds)
		if err != nil {
			errChan <- err
		}
		resp.ProductPriceList = productPriceList
	}()
	//******************************协程并发结束***********************************
	go func() {
		wait.Wait()
		close(errChan)
	}()
	//获取错误信息
	for err = range errChan {
		if err != nil {
			return GetUserCartRelativeListRsp{}, err
		}
	}
	return resp, nil
}