package logic

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"fusenapi/constants"
	"fusenapi/model/gmodel"
	"fusenapi/utils/auth"
	"fusenapi/utils/basic"
	"gorm.io/gorm"
	"math"
	"strconv"
	"time"

	"fusenapi/server/inventory/internal/svc"
	"fusenapi/server/inventory/internal/types"

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

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

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

func (l *GetPickupListLogic) GetPickupList(req *types.GetPickupListReq, userinfo *auth.UserInfo) (resp *basic.Response) {
	if userinfo.GetIdType() != auth.IDTYPE_User {
		return resp.SetStatusWithMessage(basic.CodeServiceErr, "please login first")
	}
	if req.Page <= 0 {
		req.Page = constants.DEFAULT_PAGE
	}
	if req.PageSize <= 0 || req.PageSize > constants.MAX_PAGE_SIZE {
		req.PageSize = constants.DEFAULT_PAGE_SIZE
	}
	//获取列表
	pickListReq := gmodel.GetPickupListByParamReq{
		UserId: &userinfo.UserId,
		Page:   req.Page,
		Limit:  req.PageSize,
	}
	//状态筛选
	if req.Status != -1 {
		pickListReq.Status = &req.Status
	}
	//空的就返回
	pickupList, total, err := l.svcCtx.AllModels.FsCloudPickUp.GetPickupListByParam(l.ctx, pickListReq)
	if err != nil {
		logx.Error(err)
		return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get pickup list")
	}
	pickupIds := make([]int64, 0, len(pickupList))
	for _, v := range pickupList {
		pickupIds = append(pickupIds, v.Id)
	}
	//获取详情数据
	pickupDetailList, err := l.svcCtx.AllModels.FsCloudPickUpDetail.GetAllByIds(l.ctx, pickupIds)
	if err != nil {
		logx.Error(err)
		return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get pickup detail list")
	}
	stockIds := make([]int64, 0, len(pickupList))
	for _, v := range pickupDetailList {
		stockIds = append(stockIds, *v.StockId)
	}
	stockList, _, err := l.svcCtx.AllModels.FsUserStock.GetStockList(l.ctx, gmodel.GetStockListReq{
		UserId: userinfo.UserId,
		Ids:    stockIds,
		Page:   1,
		Limit:  len(stockIds),
	})
	if err != nil {
		logx.Error(err)
		return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get stock list")
	}
	designIds := make([]int64, 0, len(stockList))
	mapStock := make(map[int64]int)
	for k, v := range stockList {
		designIds = append(designIds, *v.DesignId)
		mapStock[v.Id] = k
	}
	//获取设计列表
	designList, err := l.svcCtx.AllModels.FsProductDesign.GetAllByIdsWithoutStatus(l.ctx, designIds)
	if err != nil {
		logx.Error(err)
		return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get design list")
	}
	productIds := make([]int64, 0, len(designList))
	sizeIds := make([]int64, 0, len(designList))
	optionalIds := make([]int64, 0, len(designList))
	mapDesign := make(map[int64]int)
	for k, v := range designList {
		productIds = append(productIds, *v.ProductId)
		sizeIds = append(sizeIds, *v.SizeId)
		optionalIds = append(optionalIds, *v.OptionalId)
		mapDesign[v.Id] = k
	}
	//获取产品信息
	productList, err := l.svcCtx.AllModels.FsProduct.GetProductListByIdsWithoutStatus(l.ctx, productIds, "")
	if err != nil {
		logx.Error(err)
		return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get product list ")
	}
	mapProduct := make(map[int64]int)
	for k, v := range productList {
		mapProduct[v.Id] = k
	}
	//获取尺寸信息
	sizeList, err := l.svcCtx.AllModels.FsProductSize.GetAllByIdsWithoutStatus(l.ctx, sizeIds, "")
	if err != nil {
		logx.Error(err)
		return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get product size list ")
	}
	mapSize := make(map[int64]int)
	for k, v := range sizeList {
		mapSize[v.Id] = k
	}
	//获取配件信息
	model3dList, err := l.svcCtx.AllModels.FsProductModel3d.GetAllByIdsWithoutStatus(l.ctx, optionalIds)
	if err != nil {
		logx.Error(err)
		return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get product 3d model list ")
	}
	mapModel3d := make(map[int64]int)
	for k, v := range model3dList {
		mapModel3d[v.Id] = k
	}
	//获取时间配置
	var (
		factoryDeliverDay int64 = 2
		deliverUpsDay     int64 = 35
		upsTransDay       int64 = 5
	)
	if timeSetting, err := l.svcCtx.AllModels.FsWebSet.FindValueByKey(l.ctx, "time_info"); err != nil {
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			logx.Error(err)
			return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get web setting:time_info")
		}
	} else { //存在记录
		if timeSetting.Value != nil {
			var timeInfo map[string]string
			if err = json.Unmarshal([]byte(*timeSetting.Value), &timeInfo); err != nil {
				logx.Error(err)
				return resp.SetStatusWithMessage(basic.CodeServiceErr, "failed to parse time_info ")
			}
			factoryDeliverDay, _ = strconv.ParseInt(timeInfo["factory_deliver_day"], 10, 64)
			deliverUpsDay, _ = strconv.ParseInt(timeInfo["deliver_ups_day"], 10, 64)
			upsTransDay, _ = strconv.ParseInt(timeInfo["ups_trans_day"], 10, 64)
		}
	}
	//处理提货单列表详情数据
	type mapPickupProductItem struct {
		List      []types.Product
		PickNum   int64
		PickBoxes int64
	}
	mapPickupProduct := make(map[int64]*mapPickupProductItem)
	for _, v := range pickupDetailList {
		stockIndex, ok := mapStock[*v.StockId]
		if !ok {
			continue
		}
		designIndex, ok := mapDesign[*stockList[stockIndex].DesignId]
		if !ok {
			continue
		}
		designInfo := designList[designIndex]
		productIndex, ok := mapProduct[*designInfo.ProductId]
		if !ok {
			continue
		}
		productInfo := productList[productIndex]
		sizeIndex, ok := mapSize[*designInfo.SizeId]
		if !ok {
			continue
		}
		sizeInfo := sizeList[sizeIndex]
		fitting := ""
		if model3dIndex, ok := mapModel3d[*designInfo.OptionalId]; ok {
			fitting = *model3dList[model3dIndex].Title
		}
		productItem := types.Product{
			Id:          v.Id,
			PickId:      *v.PickId,
			StockId:     *v.StockId,
			Num:         *v.Num,
			Boxes:       *v.Boxes,
			Ctime:       *v.Ctime,
			ProductName: *productInfo.Title,
			Pcs:         *v.Num,
			PcsBox:      *v.Boxes,
			Cover:       *designInfo.Cover,
			Size:        *sizeInfo.Capacity,
			Fitting:     fitting,
		}
		//已经存在
		if _, ok := mapPickupProduct[*v.PickId]; ok {
			mapPickupProduct[*v.PickId].List = append(mapPickupProduct[*v.PickId].List, productItem)
			mapPickupProduct[*v.PickId].PickNum += *v.Num
			mapPickupProduct[*v.PickId].PickBoxes += *v.Boxes
		} else { //不存在
			mapPickupProduct[*v.PickId] = &mapPickupProductItem{
				List:      []types.Product{productItem},
				PickNum:   *v.Num,
				PickBoxes: *v.Boxes,
			}
		}
	}
	//处理提货单数据
	listRsp := make([]types.PickupItem, 0, len(pickupList))
	for _, v := range pickupList {
		//地址处理
		address := ""
		if *v.AddressInfo != "" {
			var addressInfo map[string]interface{}
			if err = json.Unmarshal([]byte(*v.AddressInfo), &addressInfo); err != nil {
				logx.Error(err)
				return resp.SetStatusWithMessage(basic.CodeServiceErr, fmt.Sprintf("failed to parse address,pickup_id = %d", v.Id))
			}
			address += addressInfo["street"].(string) + " " + addressInfo["suite"].(string) + ","
			address += addressInfo["city"].(string) + "," + addressInfo["state"].(string) + " " + addressInfo["zip_code"].(string)
		}
		if *v.Status < int64(constants.STATUS_SHIPPING) {
			*v.ShippingTime = *v.Ctime + factoryDeliverDay*24*3600
		}
		if *v.Status < int64(constants.STATUS_PICK_UP) {
			*v.UpsTime = *v.ShippingTime + deliverUpsDay*24*3600
		}
		if *v.Status < int64(constants.STATUS_ARRIVAL) {
			*v.ArrivalTime = *v.UpsTime + upsTransDay*24*3600
		}
		d := types.PickupItem{
			Id:              v.Id,
			UserId:          *v.UserId,
			TrackNum:        *v.TrackNum,
			Ctime:           time.Unix(*v.Ctime, 0).Format("2006-01-02 15:04:05"),
			Status:          *v.Status,
			UpsSn:           *v.UpsSn,
			Address:         address,
			ProductList:     nil,
			Pcs:             0,
			PcsBox:          0,
			LogisticsStatus: *v.Status,
			StatusTimes: []types.StatusTimesItem{
				{Key: int64(constants.STATUS_ORDERD), Time: ""},
				{Key: int64(constants.STATUS_SHIPPING), Time: time.Unix(*v.ShippingTime, 0).Format("2006-01-02 15:04:05")},
				{Key: int64(constants.STATUS_PICK_UP), Time: time.Unix(*v.UpsTime, 0).Format("2006-01-02 15:04:05")},
				{Key: int64(constants.STATUS_ARRIVAL), Time: time.Unix(*v.ArrivalTime, 0).Format("2006-01-02 15:04:05")},
			},
		}
		if pickupProduct, ok := mapPickupProduct[v.Id]; ok {
			d.ProductList = pickupProduct.List
			d.Pcs = pickupProduct.PickNum
			d.PcsBox = pickupProduct.PickBoxes
		}

		listRsp = append(listRsp, d)
	}
	return resp.SetStatusWithMessage(basic.CodeOK, "success", types.GetPickupListRsp{
		PickupList: listRsp,
		Meta: types.Meta{
			TotalCount:  total,
			PageCount:   int64(math.Ceil(float64(total) / float64(req.PageSize))),
			CurrentPage: req.Page,
			PerPage:     req.PageSize,
		},
	})
}