package goods

import (
	"errors"
	"math"
	"spaceofcheng/services/models"
	"spaceofcheng/services/models/goods"
	"spaceofcheng/services/models/model"
	"spaceofcheng/services/utils"
	"strconv"
	"time"
)

// Orders 下单
func (s *GoodsServ) Orders(
	info *model.TaGoodsOrders,
	details []model.TaGoodsOrdersDetail,
	coupons []model.TaGoodsOrdersCoupon) error {

	// 校验下单内容
	if err := s.validOrdersInfo(info); err != nil {
		return err
	}

	// 校验各种金额
	if err := s.validBillCharges(info, details); err != nil {
		return err
	}

	// 保存主订单
	if err := s.dao.SaveOrders(info); err != nil {
		return err
	}

	// 保存订单明细
	if err := s.dao.SaveOrdersDetail(details, info.OrdersId); err != nil {
		return err
	}

	// 内部人员, 可以直接购买
	if info.PayType == models.CONSUME_INNER {

		// TODO
		// 通知后端有新订单 - websocket
		// utils.SendMessage()

		return nil
	}

	account, err := s.custDAO.GetAccountByCust(info.UserId)
	if err != nil {
		utils.LogError("查询用户账户信息出错: " + err.Error())
		return errors.New("查询用户账户信息出错")
	}

	// 如果是使用优惠券
	if coupons != nil && len(coupons) > 0 {
		info.PayType = models.CONSUME_COUPON
		// TODO
		// 校验优惠券相关
	} else {
		// 如果是使用城币
		info.PayType = models.CONSUME_COINCHG
		accMoney, _ := strconv.ParseFloat(account.Amount, 64)
		payMoney, _ := strconv.ParseFloat(info.ActualAmount, 64)

		if accMoney < payMoney {
			return errors.New("账户余额不足")
		}
	}

	// 保存优惠券使用记录
	if coupons != nil && len(coupons) > 0 {
		if err := s.dao.SaveOrdersCoupon(coupons, info); err != nil {
			utils.LogError("保存优惠信息出错: " + err.Error())
			return errors.New("保存优惠信息出错")
		}

		// TODO
		// 核销优惠券
	}

	// 如果是城币, 则插入用户账户消费记录
	if info.PayType == models.CONSUME_COINCHG {
		if err := s.saveCustomerPayRec(account, info); err != nil {
			return err
		}
	}

	// TODO
	// 通知后端有新订单 - websocket
	// utils.SendMessage()

	return nil
}

// saveCustomerPayRec 保存账户消费流水
func (s *GoodsServ) saveCustomerPayRec(account *model.TaCustomerAccount, info *model.TaGoodsOrders) error {
	accBill := new(model.TaAccountChange)
	accBill.AccountId = account.AccountId
	accBill.Amount = info.ActualAmount
	accBill.CaseId = info.CaseId
	accBill.ChangeSource = models.ACCSOURCE_GOODS_ORDERS
	accBill.ChangeType = models.CONSUME_COINCHG
	accBill.CustomerId = info.UserId
	accBill.CustomerName = info.UserName
	accBill.FloatType = models.ACCOUNT_SPENT
	accBill.SourceId = info.OrdersId
	accBill.SourceName = ""

	return s.custDAO.InsertAccountRecords(accBill)
}

// validOrdersInfo 校验下单内容
// 没有对金额, 数量的关系进行校验
func (s *GoodsServ) validOrdersInfo(info *model.TaGoodsOrders) error {

	caseID := info.CaseId
	if caseID == "" {
		return errors.New("未指定消费案场")
	}

	areaID := info.AreaId
	if areaID == "" {
		return errors.New("未指定消费区域")
	}

	tableID := info.TableId
	if tableID == "" {
		return errors.New("未指定消费桌位")
	}

	custID := info.UserId
	if custID == "" {
		return errors.New("未指定下单人员")
	}

	// 验证区域
	caseArea, err := s.caseDAO.GetCaseAreaByID(areaID)
	if err != nil {
		utils.LogError("查询案场区域出错: " + err.Error())
		return errors.New("验证案场区域出错")
	}

	info.AreaName = caseArea.AreaName
	info.OrgId = caseArea.OrgId

	// 验证桌位
	table, err := s.caseDAO.GetCaseTableByID(tableID)
	if err != nil {
		utils.LogError("查询案场桌位出错: " + err.Error())
		return errors.New("验证案场桌位出错")
	}

	info.TableNo = table.TableNo

	// 验证人员
	cust, err := s.custDAO.GetCustomerByID(custID)
	if err != nil {
		utils.LogError("查询下单人出错: " + err.Error())
		return errors.New("验证下单人出错")
	}

	if cust.Status != models.STATUS_NORMAL {
		return errors.New("下单人状态不正确, 不能下单")
	}

	// 如果是管理人员
	if cust.UserId != "" {
		// 内部人员
		info.PayType = models.CONSUME_INNER

		// 查找用户类型
		userWithTypes, err := s.userDAO.GetUserWithTypeByID(cust.UserId)
		if err != nil {
			utils.LogError("查询下单人类型出错: " + err.Error())
			return errors.New("验证下单人出错")
		}
		if userWithTypes != nil && len(userWithTypes) > 0 {
			for _, ut := range userWithTypes {
				if ut.Status != models.STATUS_NORMAL {
					return errors.New("下单人状态不正确, 不能下单")
				}

				if ut.CaseId == caseID {
					info.UserType = ut.TypeId

					// 下单人姓名 - 姓名不存在则用昵称
					info.UserName = ut.RealName
					if info.UserName == "" {
						info.UserName = cust.CustomerName
					}
				}
			}
		}

		// 查找禁用名单
		fibUsers, err := s.userDAO.GetForbidUserByUserID(cust.UserId)
		if err != nil {
			utils.LogError("查询下单人是否被禁出错: " + err.Error())
			return errors.New("验证下单人出错")
		}

		if fibUsers != nil && len(fibUsers) > 0 {
			for _, u := range fibUsers {
				if u.ForbidType == models.FORBID_ORDER && u.Status == models.STATUS_NORMAL {
					return errors.New("当前下单人被禁止点单")
				}
			}
		}
	}

	return nil
}

// validBillCharges 验证下单各种金额
func (s *GoodsServ) validBillCharges(info *model.TaGoodsOrders, details []model.TaGoodsOrdersDetail) error {
	if details == nil || len(details) == 0 {
		return errors.New("没有发现下单明细")
	}

	caseID := info.CaseId

	goodsAmount := float64(0.0)
	for _, item := range details {
		goods, err := s.dao.GetGoodsWithPriceByID(item.GoodsId, item.SpecId, caseID)
		if err != nil {
			utils.LogError("查询商品详情失败: " + err.Error())
			return errors.New("校验商品信息失败")
		}

		if goods.GoodsPrice != item.Price {
			return errors.New("商品金额计算错误")
		}

		// TODO 校验库存
		// 商品暂时无库存

		price, _ := strconv.ParseFloat(item.Price, 64)
		goodsAmount += price * float64(item.Number)
	}

	// 校验总金额
	orderAmount, _ := strconv.ParseFloat(info.Amount, 64)

	if math.Abs(orderAmount-goodsAmount) > 0.00001 {
		return errors.New("商品金额计算错误")
	}

	return nil
}

// GetOrderList 获取订单列表
// 管理端 - 查询统计
func (s *GoodsServ) GetOrderList(caseID, userType, status string, orderDate []time.Time, page, pagesize int) ([]goods.OrdersWithGoods, int64, error) {
	if err := utils.NewAuthEngine(s.ctx).CheckCase(caseID); err != nil {
		return nil, 0, err
	}

	var makeStatus []string
	if status != "" {
		makeStatus = []string{status}
	}

	if page < 1 {
		page = 1
	}
	if pagesize < 0 {
		pagesize = 0
	}
	limit := []int{
		pagesize,
		(page - 1) * pagesize,
	}

	orderList, cnt, err := s.dao.GetOrdersByCaseID(caseID, userType, makeStatus, orderDate, limit)
	if err != nil {
		utils.LogError("获取商品订单列表失败： " + err.Error())
		return nil, 0, errors.New("获取商品订单列表失败")
	}

	return orderList, cnt, nil
}

// GetOnLineOrders 获取订单列表
// 管理端 - 在线订单
func (s *GoodsServ) GetOnLineOrders(caseID string) ([]goods.OrdersWithGoods, error) {
	if err := utils.NewAuthEngine(s.ctx).CheckCase(caseID); err != nil {
		return nil, err
	}

	makeStatus := []string{goods.MAKESTATUS_NOTSTARTED, goods.MAKESTATUS_DOING}

	orderList, _, err := s.dao.GetOrdersByCaseID(caseID, "", makeStatus, nil, nil)
	if err != nil {
		utils.LogError("获取在线订单列表失败： " + err.Error())
		return nil, errors.New("获取在线订单列表失败")
	}

	return orderList, nil
}
