package biz

import (
	"context"
	"fmt"
	"time"
	err2 "voucher/api/err"
	"voucher/internal/biz/bo"
	"voucher/internal/biz/businesserr"
	"voucher/internal/biz/vo"
)

func (this *VoucherBiz) CmbOrder(ctx context.Context, req *bo.OrderCreateReqBo) (*bo.OrderBo, *bo.ProductBo, error) {

	order, err3 := this.GetByOutBizNo(ctx, req)
	if err3 != nil {
		return nil, nil, err3
	}

	if order != nil {

		if order.Status.IsFail() || order.Status.IsIng() {

			if err4 := this.orderRetry(ctx, order); err4 != nil {
				return order, nil, err4
			}
		}

		return order, nil, nil
	}

	product, err3 := this.ProductRepo.GetByProductNo(ctx, req.ProductNo)
	if err3 != nil {
		return nil, product, err3
	}

	if product.StartTime == nil {
		return nil, product, businesserr.BatchNotSetStartedError
	}
	if product.EndTime == nil {
		return nil, product, businesserr.BatchSetEndedError
	}

	nowTime := time.Now()
	if nowTime.Before(*product.StartTime) {
		return nil, product, businesserr.BatchNotStartedError
	}
	if nowTime.After(*product.EndTime) {
		return nil, product, businesserr.BatchEndedError
	}

	order, err3 = this.order(ctx, req, product)
	if err3 != nil {
		return nil, product, err3
	}

	return order, product, nil
}

func (this *VoucherBiz) order(ctx context.Context, req *bo.OrderCreateReqBo, product *bo.ProductBo) (*bo.OrderBo, error) {

	order, err := this.create(ctx, req, product)
	if err != nil {
		return nil, err
	}

	voucherNo, err := this.send(ctx, order)
	if err != nil {
		if err3 := this.fail(ctx, order, err); err3 != nil {
			return nil, err3
		}
		return nil, err
	}

	if err = this.success(ctx, order, voucherNo); err != nil {
		return nil, err
	}

	return order, nil
}

func (this *VoucherBiz) send(ctx context.Context, order *bo.OrderBo) (string, error) {

	if order.ActivityId == "" {
		return this.WechatCpnRepo.Order(ctx, order)
	}

	return this.BankMultiActivityRepo.Order(order)
}

func (this *VoucherBiz) orderRetry(ctx context.Context, order *bo.OrderBo) error {

	voucherNo, err := this.send(ctx, order)

	if err != nil {
		if err3 := this.fail(ctx, order, err); err3 != nil {
			return err3
		}
		return err
	}

	return this.success(ctx, order, voucherNo)
}

func (this *VoucherBiz) create(ctx context.Context, req *bo.OrderCreateReqBo, product *bo.ProductBo) (*bo.OrderBo, error) {

	o := &bo.OrderBo{
		OrderNo:     this.GenerateMixRepo.GeneratorString(ctx, fmt.Sprintf("%d%s", req.Type, req.OutBizNo)),
		OutBizNo:    req.OutBizNo,
		ProductNo:   req.ProductNo,
		Account:     req.Account,
		AppID:       req.AppID,
		MerchantNo:  product.MchId,
		Channel:     product.Channel,
		BatchNo:     product.BatchNo,
		NotifyUrl:   req.NotifyUrl,
		AccountType: vo.OrderAccountTypeOpenId,
		Type:        req.Type,
		Status:      vo.OrderStatusIng, // 同步发放，状态至为发放中
		Attach:      req.Attach,

		ActivityId: product.ActivityId, // 多笔立减活动
	}

	if product.ActivityId != "" {
		o.NotifyUrl = this.bc.Cmb.MultiNotifyUrl
	}

	return this.OrderRepo.Create(ctx, o)
}

func (this *VoucherBiz) ing(ctx context.Context, id uint64) error {

	return this.OrderRepo.Ing(ctx, id)
}

func (this *VoucherBiz) success(ctx context.Context, order *bo.OrderBo, voucherNo string) error {

	return this.OrderRepo.Success(ctx, order.ID, voucherNo)
}

func (this *VoucherBiz) fail(ctx context.Context, order *bo.OrderBo, errReq error) error {

	errMsg := errReq.Error()

	if err := this.OrderRepo.Fail(ctx, order.ID, errMsg); err != nil {
		return err
	}

	// 微信：不同微信号，领取了很多次，自然人限领，发放频率限制，微信研发排期，后续这个错误信息应该会更新，近期没那么快同步上去
	eqMsg := fmt.Sprintf(`Post "https://api.mch.weixin.qq.com/v3/marketing/favor/users/%s/coupons": EOF`, order.Account)
	if errMsg == eqMsg {
		return nil
	}

	if err2.IsWechatAccountFail(errReq) || err2.IsWechatUserMaxCoupons(errReq) {
		return nil // 过滤调该类型错误通知
	}

	return this.alarm(ctx, order, errReq.Error())
}

func (this *VoucherBiz) GetByOutBizNo(ctx context.Context, req *bo.OrderCreateReqBo) (*bo.OrderBo, error) {

	order, err := this.OrderRepo.GetByOutBizNo(ctx, vo.OrderTypeCmb, req.OutBizNo)

	if err != nil && !err2.IsDbNotFound(err) {
		return nil, err
	}

	return order, nil
}

func (this *VoucherBiz) UpdateOrderStatus(ctx context.Context, orderId uint64, status vo.OrderStatus) error {

	if status.IsSuccess() {

		return this.OrderRepo.Available(ctx, orderId)

	} else if status.IsUse() {

		return this.OrderRepo.Used(ctx, orderId)

	} else if status.IsExpired() {

		return this.OrderRepo.Expired(ctx, orderId)
	}

	return fmt.Errorf("notice 未知券状态,orderId:%d,statuText:%s", orderId, status.GetText())
}
