package biz

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

func (v *VoucherBiz) CmbOrder(ctx context.Context, req *bo.OrderCreateReqBo) (orderNo string, err error) {

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

	if order != nil {

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

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

		return order.OrderNo, err
	}

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

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

	return order.OrderNo, nil
}

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

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

	voucherNo, err := v.WechatCpnRepo.Order(ctx, order)
	if err != nil {
		if err3 := v.fail(ctx, order, err); err3 != nil {
			return nil, err3
		}
		return nil, err
	}

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

	return order, nil
}

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

	voucherNo, err := v.WechatCpnRepo.Order(ctx, order)

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

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

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

	o := &bo.OrderBo{
		OrderNo:     v.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,
	}

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

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

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

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

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

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

	if err := v.OrderRepo.Fail(ctx, order.ID, errReq.Error()); err != nil {
		return err
	}

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

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

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

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

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

	return order, nil
}

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

	if status.IsSuccess() {

		return v.OrderRepo.Available(ctx, orderId)

	} else if status.IsUse() {

		return v.OrderRepo.Used(ctx, orderId)

	} else if status.IsExpired() {

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

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