package logic

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"fusenapi/constants"
	"fusenapi/model/gmodel"
	"fusenapi/server/inventory/internal/types"
	"fusenapi/utils/auth"
	"fusenapi/utils/basic"
	"fusenapi/utils/id_generator"
	"gorm.io/gorm"
	"time"

	"fusenapi/server/inventory/internal/svc"
	"github.com/zeromicro/go-zero/core/logx"
)

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

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

func (l *TakeLogic) Take(req *types.TakeReq, userinfo *auth.UserInfo) (resp *basic.Response) {
	if userinfo.GetIdType() != auth.IDTYPE_User {
		return resp.SetStatusWithMessage(basic.CodeServiceErr, "please login first")
	}
	if len(req.Form) == 0 {
		return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "param err :form can`t be empty array")
	}
	if req.AddressId <= 0 {
		return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "param err :address_id is required")
	}
	//获取地址信息
	addressInfo, err := l.svcCtx.AllModels.FsAddress.GetOne(l.ctx, req.AddressId, userinfo.UserId)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return resp.SetStatusWithMessage(basic.CodeDbRecordNotFoundErr, "your address is not exists")
		}
		logx.Error(err)
		return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to get address info")
	}
	stockIds := make([]int64, 0, len(req.Form))
	for _, v := range req.Form {
		stockIds = append(stockIds, v.Id)
	}
	//提货单总单
	addressInfoBytes, _ := json.Marshal(addressInfo)
	addressInfoJson := string(addressInfoBytes)
	trackNum := id_generator.GenSnNum()
	status := int64(constants.STATUS_ORDERD)
	now := time.Now().Unix()
	pickUpData := gmodel.FsCloudPickUp{
		UserId:      &userinfo.UserId,
		TrackNum:    &trackNum,
		AddressId:   &req.AddressId,
		AddressInfo: &addressInfoJson,
		Status:      &status,
		Ctime:       &now,
	}
	//箱数验证
	boxes := int64(0)
	//需要更新的库存信息
	stockUpdateList := make([]gmodel.FsUserStock, 0, len(req.Form))
	//需要新增的提货详情单
	pickUpDetailAddList := make([]gmodel.FsCloudPickUpDetail, 0, len(req.Form))
	for k, val := range req.Form {
		formItem := val
		//验证提取数量
		if formItem.Num <= 0 {
			return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, fmt.Sprintf("row %d inventory data`s take num can`be less than 0", k+1))
		}
		//获取库存信息(枷锁)
		stockInfo, err := l.svcCtx.AllModels.FsUserStock.FindOne(l.ctx, formItem.Id, userinfo.UserId, true)
		if err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				return resp.SetStatusWithMessage(basic.CodeDbRecordNotFoundErr, fmt.Sprintf("row %d inventory data is not availabled for you", k+1))
			}
			logx.Error(err)
			return resp.SetStatusWithMessage(basic.CodeDbSqlErr, fmt.Sprintf("failed to get row %d`s stock info ", k+1))
		}
		//校验取货数量
		if *stockInfo.Stick < formItem.Num {
			return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, fmt.Sprintf("row %d inventory data is shortage", k+1))
		}
		if *stockInfo.EachBoxNum <= 0 {
			return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, fmt.Sprintf("row %d inventory data each box num can`t be less than 0", k+1))
		}
		boxes += (formItem.Num / *stockInfo.EachBoxNum)
		//库存变更
		newStick := *stockInfo.Stick - formItem.Num
		newTransNum := *stockInfo.TransNum + formItem.Num
		stockUpdateList = append(stockUpdateList, gmodel.FsUserStock{
			Id:       stockInfo.Id,
			Stick:    &newStick,
			TransNum: &newTransNum,
		})
		//提货详情单
		detailBoxes := formItem.Num / *stockInfo.EachBoxNum
		pickUpDetailAddList = append(pickUpDetailAddList, gmodel.FsCloudPickUpDetail{
			PickId:  nil, //到model里方法会给他赋值pick总单id
			StockId: &formItem.Id,
			Num:     &formItem.Num,
			Boxes:   &detailBoxes,
			Ctime:   &now,
		})
	}
	if boxes < 3 {
		return resp.SetStatusWithMessage(basic.CodeRequestParamsErr, "Take out more than three boxes")
	}
	//事务处理数据
	err = l.svcCtx.AllModels.FsCloudPickUp.SavePickUpWithTransaction(l.ctx, &pickUpData, stockUpdateList, pickUpDetailAddList)
	if err != nil {
		logx.Error(err)
		return resp.SetStatusWithMessage(basic.CodeDbSqlErr, "failed to take your goods")
	}
	return resp.SetStatusWithMessage(basic.CodeOK, "success")
}