package com.czg.service.order.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.digest.MD5;
import com.czg.PayCst;
import com.czg.account.dto.shopuser.ShopUserMoneyEditDTO;
import com.czg.account.entity.ShopInfo;
import com.czg.account.entity.ShopUser;
import com.czg.account.entity.UserInfo;
import com.czg.account.service.ShopInfoService;
import com.czg.account.service.ShopUserService;
import com.czg.account.service.UserInfoService;
import com.czg.config.RabbitPublisher;
import com.czg.config.RedisCst;
import com.czg.constants.PayTypeConstants;
import com.czg.enums.CzgPayEnum;
import com.czg.enums.ShopUserFlowBizEnum;
import com.czg.exception.CzgException;
import com.czg.exception.PaySuccessException;
import com.czg.market.entity.MkShopRechargeDetail;
import com.czg.market.enums.PointsConstant;
import com.czg.market.service.*;
import com.czg.market.vo.MkShopRechargeVO;
import com.czg.order.dto.CheckOrderPay;
import com.czg.order.dto.OrderInfoRefundDTO;
import com.czg.order.entity.OrderDetail;
import com.czg.order.entity.OrderInfo;
import com.czg.order.entity.OrderPayment;
import com.czg.order.enums.PayEnums;
import com.czg.order.service.CreditBuyerOrderService;
import com.czg.order.service.OrderDetailService;
import com.czg.order.service.OrderInfoCustomService;
import com.czg.order.service.OrderPaymentService;
import com.czg.pay.CzgPayBaseReq;
import com.czg.pay.CzgRefundReq;
import com.czg.pay.RefundRespDTO;
import com.czg.resp.CzgResult;
import com.czg.service.RedisService;
import com.czg.service.order.dto.OrderPayParamDTO;
import com.czg.service.order.enums.OrderStatusEnums;
import com.czg.service.order.print.PrintConfig;
import com.czg.service.order.service.OrderPayService;
import com.czg.service.order.service.PayService;
import com.czg.utils.AssertUtil;
import com.czg.utils.CzgRandomUtils;
import com.czg.utils.FunUtils;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotBlank;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author ww
 */
@Slf4j
@Service
public class OrderPayServiceImpl implements OrderPayService {
    @Resource
    private PayService payService;
    @Resource
    private OrderPaymentService paymentService;
    @DubboReference
    private ShopUserService shopUserService;
    @DubboReference
    private UserInfoService userInfoService;
    @DubboReference
    private ShopInfoService shopInfoService;
    @Resource
    private OrderInfoCustomService orderInfoCustomService;
    @Resource
    private OrderInfoService orderInfoService;
    @Resource
    private CreditBuyerOrderService buyerOrderService;
    @Resource
    private RedisService redisService;
    @Resource
    private RabbitPublisher rabbitPublisher;
    @Resource
    private PrintConfig printConfig;
    @Resource
    private MkShopRechargeService shopRechargeService;
    @Resource
    private MkShopRechargeDetailService shopRechargeDetailService;
    @Resource
    private OrderDetailService orderDetailService;
    @Resource
    private MkDistributionUserService distributionUserService;
    @Resource
    private MkShopConsumerCouponService consumerCouponService;
    @Resource
    private MkPointsUserService mkPointsUserService;
    @Resource
    private MkConsumeCashbackService consumeCashbackService;
    @Resource
    private MkPointsConfigService pointsConfigService;

    private OrderInfo checkPay(CheckOrderPay checkOrderPay) {
        OrderInfo orderInfo = orderInfoCustomService.checkOrderPay(checkOrderPay);
        if (orderInfo.getOrderAmount().compareTo(BigDecimal.ZERO) == 0) {
            //发送打票信息
            rabbitPublisher.sendOrderPrintMsg(orderInfo.getId(), !"after-pay".equals(orderInfo.getPayMode()) ? 1 : 0, 1, 1, "0元付款");
            redisService.del(RedisCst.classKeyExpired.EXPIRED_ORDER + orderInfo.getId());
            throw new PaySuccessException("支付成功");
        }
        return orderInfo;
    }

    @Override
    @Transactional(noRollbackFor = PaySuccessException.class)
    public CzgResult<Object> creditPayOrder(OrderPayParamDTO payParam) {
        AssertUtil.isNull(payParam.getCreditBuyerId(), "请选择挂账人后支付");
        OrderInfo orderInfo = checkPay(payParam.getCheckOrderPay());
        orderInfo.setCreditBuyerId(payParam.getCreditBuyerId());
        orderInfoCustomService.upOrderInfo(orderInfo, orderInfo.getOrderAmount(),
                LocalDateTime.now(), null, PayEnums.CREDIT_PAY);
        //挂账后续逻辑
        buyerOrderService.save(orderInfo, payParam.getCreditBuyerId().toString(), orderInfo.getId());
        return CzgResult.success();
    }

    @Override
    @Transactional(noRollbackFor = PaySuccessException.class)
    public CzgResult<Object> cashPayOrder(OrderPayParamDTO payParam) {
        OrderInfo orderInfo = checkPay(payParam.getCheckOrderPay());
        orderInfoCustomService.upOrderInfo(orderInfo, orderInfo.getOrderAmount(),
                LocalDateTime.now(), null, PayEnums.CASH_PAY);
        return CzgResult.success();
    }

    @Override
    @Transactional(noRollbackFor = PaySuccessException.class)
    public CzgResult<Object> vipPayOrder(OrderPayParamDTO payParam) {
        ShopInfo shopInfo = shopInfoService.getById(payParam.getShopId());
        AssertUtil.isNull(shopInfo, "店铺不存在");
        if (!shopInfo.getIsAccountPay().equals(1)) {
            return CzgResult.failure("支付失败，店铺暂未开启会员余额支付。");
        }
        ShopUser shopUser = new ShopUser();
        if ("scanCode".equals(payParam.getPayType())) {
            AssertUtil.isBlank(payParam.getAuthCode(), "会员码不能为空");
            Object o = redisService.get(RedisCst.SHOP_USER_DYNAMIC_CODE + payParam.getShopId() + ":" + payParam.getAuthCode());
            AssertUtil.isNull(o, "会员码已失效");
            shopUser = shopUserService.getById(o.toString());
        } else {
            if ("userPay".equals(payParam.getPayType())) {
                AssertUtil.isNull(payParam.getShopUserId(), "请选择付款人后重试");
                shopUser = shopUserService.getById(payParam.getShopUserId());
            } else if ("accountPay".equals(payParam.getPayType())) {
                shopUser = shopUserService.getById(payParam.getShopUserId());
                AssertUtil.isNull(shopUser, "会员不存在");
                UserInfo userInfo = userInfoService.getById(shopUser.getUserId());
                AssertUtil.isNull(userInfo, "用户信息不存在");
                if (userInfo.getUsePayPwd() == 1) {
                    AssertUtil.isBlank(payParam.getPwd(), "支付密码不能为空");
                    if (userInfo.getPayPwd() == null || !userInfo.getPayPwd().equals(MD5.create().digestHex((payParam.getPwd())))) {
                        return CzgResult.failure("支付密码错误");
                    }
                }
            }
        }
        if (shopUser == null || shopUser.getId() == null) {
            AssertUtil.isNull(shopUser, "会员不存在");
        }
        payParam.getCheckOrderPay().setUserId(shopUser.getUserId());
        OrderInfo orderInfo = checkPay(payParam.getCheckOrderPay());
        if (shopInfo.getIsHeadShop().equals(1)) {
            if (!shopUser.getMainShopId().equals(orderInfo.getShopId())) {
                return CzgResult.failure("违规操作，请确认店铺后重试");
            }
        } else {
            boolean exists = shopInfoService.exists(QueryWrapper.create()
                    .eq(ShopInfo::getMainId, shopInfo.getMainId())
                    .eq(ShopInfo::getId, orderInfo.getShopId()));
            if (!exists) {
                return CzgResult.failure("违规操作，请确认店铺后重试");
            }
        }
        if (shopUser.getAmount().compareTo(orderInfo.getOrderAmount()) < 0) {
            return CzgResult.failure("会员余额不足");
        }
        ShopUserMoneyEditDTO shopUserMoneyEditDTO = new ShopUserMoneyEditDTO()
                .setId(shopUser.getId())
                .setMoney(orderInfo.getOrderAmount())
                .setType(0)
                .setBizEnum(ShopUserFlowBizEnum.ORDER_PAY)
                .setRelationId(orderInfo.getId());
        //更新会员余额 并生成流水
        if (payParam.getCheckOrderPay() != null && StrUtil.isNotBlank(payParam.getCheckOrderPay().getRemark())) {
            orderInfo.setRemark(payParam.getCheckOrderPay().getRemark());
        }

        Long flowId = shopUserService.updateMoney(shopUserMoneyEditDTO);
        orderInfoCustomService.upOrderInfo(orderInfo, orderInfo.getOrderAmount(),
                LocalDateTime.now(), flowId, PayEnums.VIP_PAY);
        return CzgResult.success();
    }

    @Override
    public CzgResult<Map<String, Object>> rechargePayOrder(String clintIp, OrderPayParamDTO payParam) {
        OrderInfo orderInfo = checkPay(payParam.getCheckOrderPay());
        ShopInfo shopInfo = shopInfoService.getById(payParam.getShopId());
        AssertUtil.isNull(shopInfo, "店铺不存在");
        AssertUtil.isNull(payParam.getShopUserId(), "请选择付款人后重试");
        ShopUser shopUser = shopUserService.getById(payParam.getShopUserId());
        AssertUtil.isNull(shopUser, "支付失败 该店铺用户不存在");
        AssertUtil.isBlank(payParam.getPayType(), "支付方式不能为空");
        UserInfo userInfo = userInfoService.getById(shopUser.getUserId());
        AssertUtil.isNull(payParam.getRechargeId(), "请选择充值配置后重试");
        AssertUtil.isNull(payParam.getRechargeDetailId(), "请选择充值配置后重试");
        //查询活动Id 获取金额字段
        MkShopRechargeDetail rechargeDetail = shopRechargeDetailService.getOne(
                new QueryWrapper().eq(MkShopRechargeDetail::getId, payParam.getRechargeDetailId())
                        .eq(MkShopRechargeDetail::getShopRechargeId, payParam.getRechargeId()));
        AssertUtil.isNull(rechargeDetail, "充值配置不存在");
        if (orderInfo.getOrderAmount().compareTo(rechargeDetail.getAmount()) >= 0) {
            log.info("充值金额小于订单金额,充值金额:{} 订单金额:{}", rechargeDetail.getAmount(), orderInfo.getOrderAmount());
            return CzgResult.failure("支付失败 充值金额必须大雨订单金额");
        }
        String payOrderNo = orderInfo.getPlatformType() + CzgRandomUtils.snowflake();
        Long paymentId = payService.initPayment(OrderPayment.pay(payParam.getShopId(), shopUser.getId(), PayTypeConstants.SourceType.MEMBER_IN,
                payOrderNo, rechargeDetail.getAmount(), "", rechargeDetail.getId()));
        upOrderPayInfo(orderInfo.getId(), PayEnums.VIP_PAY, paymentId,
                payParam.getCheckOrderPay() == null ? null : payParam.getCheckOrderPay().getRemark());
        return payService.pay(payParam.getShopId(), CzgPayEnum.LT_PAY,
                CzgPayBaseReq.ltPayReq(
                        payOrderNo, "充值并支付",
                        rechargeDetail.getAmount().multiply(PayService.MONEY_RATE).longValue(), payParam.getPayType(),
                        PayCst.Type.WECHAT.equals(payParam.getPayType()) ? userInfo.getWechatOpenId() : userInfo.getAlipayOpenId(), clintIp));
    }

    @Override
    @Transactional(noRollbackFor = PaySuccessException.class)
    public CzgResult<Map<String, Object>> h5PayOrder(@NonNull String clintIp, OrderPayParamDTO payParam) {
        OrderInfo orderInfo = checkPay(payParam.getCheckOrderPay());
        String payOrderNo = orderInfo.getPlatformType() + CzgRandomUtils.snowflake();
        Long paymentId = payService.initPayment(OrderPayment.orderPay(payParam.getShopId(), orderInfo.getId(), payOrderNo, orderInfo.getOrderAmount(), ""));
        upOrderPayInfo(orderInfo.getId(), PayEnums.H5_PAY, paymentId, payParam.getCheckOrderPay() == null ? null : payParam.getCheckOrderPay().getRemark());
        return payService.pay(payParam.getShopId(), CzgPayEnum.H5_PAY,
                CzgPayBaseReq.h5PayReq(payOrderNo, "点餐支付", orderInfo.getOrderAmount().multiply(PayService.MONEY_RATE).longValue(), clintIp));
    }


    @Override
    @Transactional(noRollbackFor = PaySuccessException.class)
    public CzgResult<Map<String, Object>> jsPayOrder(@NonNull String clintIp, OrderPayParamDTO payParam) {
        OrderInfo orderInfo = checkPay(payParam.getCheckOrderPay());
        AssertUtil.isBlank(payParam.getPayType(), "支付方式不能为空");
        AssertUtil.isBlank(payParam.getOpenId(), "用户小程序ID不能为空");
        String payOrderNo = orderInfo.getPlatformType() + CzgRandomUtils.snowflake();
        Long paymentId = payService.initPayment(OrderPayment.orderPay(payParam.getShopId(), orderInfo.getId(), payOrderNo, orderInfo.getOrderAmount(), ""));
        upOrderPayInfo(orderInfo.getId(), PayCst.Type.ALIPAY.equals(payParam.getPayType()) ? PayEnums.ALIPAY_MINI : PayEnums.WECHAT_MINI, paymentId,
                payParam.getCheckOrderPay() == null ? null : payParam.getCheckOrderPay().getRemark());
        return payService.pay(payParam.getShopId(), CzgPayEnum.JS_PAY,
                CzgPayBaseReq.jsPayReq(payOrderNo, "点餐支付", orderInfo.getOrderAmount().multiply(PayService.MONEY_RATE).longValue(),
                        payParam.getPayType(), payParam.getOpenId(), clintIp));
    }

    @Override
    @Transactional(noRollbackFor = PaySuccessException.class)
    public CzgResult<Map<String, Object>> js2PayOrder(@NonNull String clintIp, OrderPayParamDTO payParam) {
        AssertUtil.isBlank(payParam.getPayType(), "支付方式不能为空");
        AssertUtil.isBlank(payParam.getOpenId(), "用户小程序ID不能为空");
        OrderInfo orderInfo;
        if (payParam.getCheckOrderPay().getOrderId() == null) {
            if (payParam.getCheckOrderPay().getOrderAmount() == null || payParam.getCheckOrderPay().getOrderAmount().compareTo(BigDecimal.ZERO) <= 0) {
                throw new CzgException("支付金额不合法");
            }
            orderInfo = orderInfoCustomService.createPayOrder(payParam.getShopId(), payParam.getCheckOrderPay().getOrderAmount(),
                    payParam.getCheckOrderPay().getRemark());
        } else {
            orderInfo = orderInfoService.getById(payParam.getCheckOrderPay().getOrderId());
        }
        String payOrderNo = orderInfo.getPlatformType() + CzgRandomUtils.snowflake();
        Long paymentId = payService.initPayment(OrderPayment.orderPay(payParam.getShopId(), orderInfo.getId(), payOrderNo, orderInfo.getOrderAmount(), ""));
        upOrderPayInfo(orderInfo.getId(), PayEnums.MAIN_SCAN, paymentId,
                payParam.getCheckOrderPay() == null ? null : payParam.getCheckOrderPay().getRemark());
        return payService.pay(payParam.getShopId(), CzgPayEnum.JS_PAY,
                CzgPayBaseReq.jsPayReq(payOrderNo, "扫码支付", orderInfo.getOrderAmount().multiply(PayService.MONEY_RATE).longValue(),
                        payParam.getPayType(), payParam.getOpenId(), clintIp));
    }

    @Override
    @Transactional(noRollbackFor = PaySuccessException.class)
    public CzgResult<Map<String, Object>> ltPayOrder(@NonNull String clintIp, OrderPayParamDTO payParam) {
        OrderInfo orderInfo = checkPay(payParam.getCheckOrderPay());
        AssertUtil.isBlank(payParam.getPayType(), "支付方式不能为空");
        AssertUtil.isBlank(payParam.getOpenId(), "用户小程序ID不能为空");
        String payOrderNo = orderInfo.getPlatformType() + CzgRandomUtils.snowflake();
        Long paymentId = payService.initPayment(OrderPayment.orderPay(payParam.getShopId(), orderInfo.getId(), payOrderNo, orderInfo.getOrderAmount(), ""));
        upOrderPayInfo(orderInfo.getId(), PayCst.Type.ALIPAY.equals(payParam.getPayType()) ? PayEnums.ALIPAY_MINI : PayEnums.WECHAT_MINI, paymentId,
                payParam.getCheckOrderPay() == null ? null : payParam.getCheckOrderPay().getRemark());
        return payService.pay(payParam.getShopId(), CzgPayEnum.LT_PAY,
                CzgPayBaseReq.ltPayReq(payOrderNo, "点餐支付", orderInfo.getOrderAmount().multiply(PayService.MONEY_RATE).longValue(),
                        payParam.getPayType(), payParam.getOpenId(), clintIp));
    }

    @Override
    @Transactional(noRollbackFor = PaySuccessException.class)
    public CzgResult<Map<String, Object>> scanPayOrder(@NonNull String clintIp, OrderPayParamDTO payParam) {
        OrderInfo orderInfo = checkPay(payParam.getCheckOrderPay());
        ShopUser shopUser = shopUserService.getById(payParam.getShopUserId());
        MkShopRechargeVO rechargeVO = shopRechargeService.detail(payParam.getShopId());
        if (payParam.getRechargeDetailId() == null && rechargeVO.getIsCustom() == 0) {
            throw new CzgException("未开启自定义充值金额");
        }
        Long mainShopId = shopInfoService.getMainIdByShopId(payParam.getShopId());
        BigDecimal amount = shopRechargeService.checkRecharge(mainShopId, payParam.getShopId(), shopUser.getUserId(), payParam.getRechargeDetailId(), payParam.getAmount());
        payParam.setAmount(amount);
        String payOrderNo = orderInfo.getPlatformType() + CzgRandomUtils.snowflake();
        Long paymentId = payService.initPayment(OrderPayment.orderPay(payParam.getShopId(), orderInfo.getId(), payOrderNo, orderInfo.getOrderAmount(), ""));
        upOrderPayInfo(orderInfo.getId(), PayEnums.MAIN_SCAN, paymentId,
                payParam.getCheckOrderPay() == null ? null : payParam.getCheckOrderPay().getRemark());
        return payService.pay(payParam.getShopId(), CzgPayEnum.SCAN_PAY,
                CzgPayBaseReq.scanPayReq(payOrderNo, "点餐支付", orderInfo.getOrderAmount().multiply(PayService.MONEY_RATE).longValue(), clintIp));
    }

    @Override
    @Transactional(noRollbackFor = PaySuccessException.class)
    public CzgResult<Map<String, Object>> microPayOrder(OrderPayParamDTO payParam) {
        OrderInfo orderInfo = checkPay(payParam.getCheckOrderPay());
        AssertUtil.isBlank(payParam.getAuthCode(), "扫描码不能为空");


        if (payParam.getShopUserId() != null) {
            ShopUser shopUser = shopUserService.getById(payParam.getShopUserId());
            MkShopRechargeVO rechargeVO = shopRechargeService.detail(payParam.getShopId());
            if (payParam.getRechargeDetailId() == null && rechargeVO.getIsCustom() == 0) {
                throw new CzgException("未开启自定义充值金额");
            }
            Long mainShopId = shopInfoService.getMainIdByShopId(payParam.getShopId());
            BigDecimal amount = shopRechargeService.checkRecharge(mainShopId, payParam.getShopId(), shopUser.getUserId(), payParam.getRechargeDetailId(), payParam.getAmount());
            payParam.setAmount(amount);
        }

        String payOrderNo = orderInfo.getPlatformType() + CzgRandomUtils.snowflake();
        Long paymentId = payService.initPayment(OrderPayment.orderPay(payParam.getShopId(), orderInfo.getId(), payOrderNo, orderInfo.getOrderAmount(), payParam.getAuthCode()));
        CzgResult<Map<String, Object>> mapCzgResult = payService.pay(payParam.getShopId(), CzgPayEnum.MICRO_PAY,
                CzgPayBaseReq.microPay(payOrderNo, "点餐支付", orderInfo.getOrderAmount().multiply(PayService.MONEY_RATE).longValue(), payParam.getAuthCode()));
        if (mapCzgResult.getCode() == 200) {
            orderInfoCustomService.upOrderInfo(orderInfo, orderInfo.getOrderAmount(),
                    LocalDateTime.now(), paymentId, PayEnums.BACK_SCAN);
            rabbitPublisher.sendOrderPrintMsg(orderInfo.getId(), !"after-pay".equals(orderInfo.getPayMode()) ? 1 : 0, 1, 1, "扫码收款");
        } else {
            upOrderPayInfo(orderInfo.getId(), PayEnums.BACK_SCAN, paymentId,
                    payParam.getCheckOrderPay() == null ? null : payParam.getCheckOrderPay().getRemark());
        }
        return mapCzgResult;
    }


    @Override
    @Transactional
    public CzgResult<Object> refundOrderBefore(OrderInfoRefundDTO param) {
        OrderInfo orderInfo = orderInfoService.getById(param.getOrderId());
        if (orderInfo.getStatus().equals(OrderStatusEnums.CANCELLED.getCode())) {
            throw new CzgException("订单已过期不可退单");
        }
        boolean isFirstRefund = orderInfo.getRefundAmount().compareTo(BigDecimal.ZERO) == 0;
        ShopInfo shopInfo = shopInfoService.getById(orderInfo.getShopId());
        Map<Long, BigDecimal> productStockMap = new HashMap<>();
        boolean isPay = true;
        String refPayOrderNo = "REFO" + IdUtil.getSnowflakeNextId();
        if (orderInfo.getStatus().equals(OrderStatusEnums.UNPAID.getCode())) {
            isPay = false;
            refPayOrderNo = "";
        }
        if (isPay) {
            if (shopInfo.getIsReturnPwd().equals(1)) {
                AssertUtil.isBlank(shopInfo.getOperationPwd(), "请设置操作密码后使用");
                if (!SecureUtil.md5(param.getPwd()).equals(shopInfo.getOperationPwd())) {
                    throw new CzgException("操作密码错误");
                }
            }
            orderInfo.setRefundAmount(orderInfo.getRefundAmount().add(param.getRefundAmount()));
            if (orderInfo.getRefundAmount().compareTo(orderInfo.getPayAmount()) > 0) {
                throw new CzgException("退单失败，可退金额不足");
            }
        }
        if (CollUtil.isNotEmpty(param.getRefundDetails())) {
            for (OrderDetail refundDetail : param.getRefundDetails()) {
                AssertUtil.isNull(refundDetail.getNum(), "退单数量不能为空");
                //退款数量
                BigDecimal refNum = refundDetail.getNum();
                OrderDetail orderDetail = orderDetailService.getById(refundDetail.getId());
                if (orderDetail == null) {
                    throw new CzgException("退单失败，订单明细不存在");
                }
                refundDetail.setProductName(orderDetail.getProductName());
                refundDetail.setSkuName(orderDetail.getSkuName());

                //可退数量=订单数量-退单数量-退菜数量
                BigDecimal returnNum = orderDetail.getNum().subtract(orderDetail.getRefundNum()).subtract(orderDetail.getReturnNum());
                if (returnNum.compareTo(BigDecimal.ZERO) <= 0 || returnNum.compareTo(refundDetail.getNum()) < 0) {
                    throw new CzgException("退单失败，" + orderDetail.getProductName() + "可退数量不足");
                }
                refundDetail.setReturnAmount(refundDetail.getNum().multiply(orderDetail.getUnitPrice()).setScale(2, RoundingMode.UP));
                BigDecimal yuanReturnAmount = orderDetail.getReturnAmount();
                if (isPay) {
                    orderDetail.setRefundNum(orderDetail.getRefundNum().add(refNum));
                    if (orderDetail.getNum().compareTo(orderDetail.getRefundNum().add(orderDetail.getReturnNum())) == 0) {
                        orderDetail.setStatus(OrderStatusEnums.REFUND.getCode());
                    } else {
                        orderDetail.setStatus(OrderStatusEnums.PART_REFUND.getCode());
                    }
                    orderDetail.setReturnAmount(orderDetail.getReturnAmount().add(refundDetail.getReturnAmount()));
                } else {
                    orderDetail.setReturnNum(orderDetail.getReturnNum().add(refNum));
                    if (orderDetail.getPackNumber().compareTo(BigDecimal.ZERO) > 0 && orderDetail.getPackNumber().compareTo(orderDetail.getNum().subtract(orderDetail.getReturnNum())) > 0) {
                        orderDetail.setPackNumber(orderDetail.getNum().subtract(orderDetail.getReturnNum()));
                    }
                }
                orderDetail.setRefundNo(refPayOrderNo);
                orderDetail.setRefundRemark(orderDetail.getRefundRemark() + param.getRefundReason());
                if (orderDetail.getReturnAmount().compareTo(orderDetail.getPayAmount()) > 0) {
                    orderDetail.setReturnAmount(orderDetail.getPayAmount());
                    refundDetail.setReturnAmount(orderDetail.getPayAmount().subtract(yuanReturnAmount));
                }
                orderDetailService.updateById(orderDetail);
                if (orderDetail.getProductId() != null && orderDetail.getProductId() > 0) {
                    if (productStockMap.containsKey(orderDetail.getProductId())) {
                        productStockMap.put(orderDetail.getProductId(), productStockMap.get(orderDetail.getProductId()).add(refundDetail.getNum()));
                    } else {
                        productStockMap.put(orderDetail.getProductId(), refundDetail.getNum());
                    }
                }
            }
            long count = orderDetailService.count(QueryWrapper.create().eq(OrderDetail::getOrderId, orderInfo.getId())
                    .ne(OrderDetail::getStatus, OrderStatusEnums.REFUND.getCode()));
            if (count > 0 && isPay) {
                orderInfo.setStatus(OrderStatusEnums.PART_REFUND.getCode());
            } else if (isPay) {
                orderInfo.setStatus(OrderStatusEnums.REFUND.getCode());
            }
        } else {
            orderInfo.setStatus(OrderStatusEnums.REFUND.getCode());
            List<OrderDetail> orderDetails = orderDetailService.list(
                    QueryWrapper.create().select(OrderDetail::getId, OrderDetail::getProductId, OrderDetail::getNum, OrderDetail::getReturnNum, OrderDetail::getPackAmount, OrderDetail::getReturnNum)
                            .eq(OrderDetail::getOrderId, orderInfo.getId()));
            for (OrderDetail orderDetail : orderDetails) {
                if (isPay) {
                    if (orderDetail.getProductId() != null && orderDetail.getProductId() > 0) {
                        if (productStockMap.containsKey(orderDetail.getProductId())) {
                            productStockMap.put(orderDetail.getProductId(), productStockMap.get(orderDetail.getProductId()).add(orderDetail.getNum().subtract(orderDetail.getReturnNum()).subtract(orderDetail.getRefundNum())));
                        } else {
                            productStockMap.put(orderDetail.getProductId(), orderDetail.getNum().subtract(orderDetail.getReturnNum()).subtract(orderDetail.getRefundNum()));
                        }
                    }
                    orderDetail.setReturnAmount(orderDetail.getPayAmount());
                    orderDetail.setRefundNum(orderDetail.getNum().subtract(orderDetail.getReturnNum()));
                    orderDetail.setStatus(OrderStatusEnums.REFUND.getCode());
                } else {
                    if (orderDetail.getProductId() != null && orderDetail.getProductId() > 0) {
                        if (productStockMap.containsKey(orderDetail.getProductId())) {
                            productStockMap.put(orderDetail.getProductId(), productStockMap.get(orderDetail.getProductId()).add(orderDetail.getNum().subtract(orderDetail.getReturnNum())));
                        } else {
                            productStockMap.put(orderDetail.getProductId(), orderDetail.getNum().subtract(orderDetail.getReturnNum()));
                        }
                    }
                    orderDetail.setReturnNum(orderDetail.getNum());
                    orderDetail.setStatus(OrderStatusEnums.CANCELLED.getCode());
                }
            }
            orderDetailService.updateBatch(orderDetails);
        }
        //总退款金额
        String printTitle = "退菜单";
        //TODO 退款 券 未处理
        if (isPay) {
            printTitle = "退款单";
            orderInfo.setRefundType("cash");
            //非现金退款
            if (!param.isCash()) {
                if (orderInfo.getPayType().equals(PayEnums.VIP_PAY.getValue())) {
                    ShopUser shopUser = shopUserService.getShopUserInfo(orderInfo.getShopId(), orderInfo.getUserId());
                    //会员支付 退钱
                    ShopUserMoneyEditDTO shopUserMoneyEditDTO = new ShopUserMoneyEditDTO()
                            .setId(shopUser.getId())
                            .setMoney(param.getRefundAmount())
                            .setType(1)
                            .setRelationId(orderInfo.getId())
                            .setBizEnum(ShopUserFlowBizEnum.ORDER_REFUND);
                    shopUserService.updateMoney(shopUserMoneyEditDTO);
                } else if (orderInfo.getPayType().equals(PayEnums.CREDIT_PAY.getValue())) {
                    AssertUtil.isNull(orderInfo.getCreditBuyerId(), "挂账单退款失败，未查询到挂账人");
                    buyerOrderService.partRefund(orderInfo.getCreditBuyerId().toString(), orderInfo.getId(), orderInfo.getOrderNo(), param.getRefundAmount());
                } else if (!orderInfo.getPayType().equals(PayEnums.CASH_PAY.getValue())) {
                    //退款 param.getRefundAmount()
                    if (orderInfo.getPayOrderId() == null) {
                        throw new CzgException("退款失败,支付记录不存在");
                    }
                    refundOrder(orderInfo.getShopId(), orderInfo.getId(), orderInfo.getPayOrderId(),
                            refPayOrderNo, StrUtil.isBlank(param.getRefundReason()) ? "退款" : param.getRefundReason(), param.getRefundAmount());
                }
                orderInfo.setRefundType("payBack");
            }
        } else {
            orderInfo.setOrderAmount(orderInfo.getOrderAmount().subtract(param.getRefundAmount()));
        }
        orderInfo.setRefundRemark(orderInfo.getRefundRemark() + param.getRefundReason());
        orderInfoService.updateById(orderInfo);
        //退款后续 退款单/退菜单
        String finalPrintTitle = printTitle;
        boolean finalIsPay = isPay;
        FunUtils.safeRunVoid(() -> printConfig.refundOrderHandler(finalPrintTitle, StrUtil.isNotBlank(param.getOperator()) ? param.getOperator() : ""
                        , finalIsPay ? param.getRefundAmount().toPlainString() : "0"
                        , param.getRefundReason(), orderInfo.getRefundType(), orderInfo, param.getRefundDetails()),
                "订单id:{} 退款,前台打印消息失败", orderInfo.getId());
        //后厨退菜单
        FunUtils.safeRunVoid(() -> printConfig.kitchenRefundAllHandler(StrUtil.isNotBlank(param.getOperator()) ? param.getOperator() : "", orderInfo, param.getRefundDetails()),
                "订单id:{} 退款,后厨退菜单打印消息失败", orderInfo.getId());
        //退款返还库存
        if (CollUtil.isNotEmpty(productStockMap)) {
            FunUtils.safeRunVoid(() -> orderInfoCustomService.refundStock(shopInfo, orderInfo.getId(), productStockMap, param.isRefundStock()),
                    "订单id:{} 退款,库存处理失败", orderInfo.getId());
        }
        refundOrderAfter(orderInfo.getId(), orderInfo.getShopId(), orderInfo.getUserId(), orderInfo.getOrderNo(),
                orderInfo.getPointsNum(), isFirstRefund, orderInfo.getStatus().equals(OrderStatusEnums.REFUND.getCode()));
        return CzgResult.success();
    }

    private void refundOrder(@NonNull Long shopId, @NonNull Long orderId, @NonNull Long payOrderId, @NonNull String refPayOrderNo,
                             @NonNull String refundReason, @NonNull BigDecimal refundAmount) {
        OrderPayment payment = paymentService.getById(payOrderId);
        AssertUtil.isNull(payment, "退款失败支付记录不存在");
        Long refundId = payService.initPayment(OrderPayment.refund(payment.getChannel(), shopId, orderId, PayTypeConstants.SourceType.ORDER,
                refPayOrderNo, refundAmount, payment.getId(), payment.getPlatformType()));

        CzgResult<RefundRespDTO> refund = payService.refund(shopId, new CzgRefundReq(refPayOrderNo, refundReason, refundAmount.multiply(PayService.MONEY_RATE).longValue(),
                payment.getAmount().multiply(PayService.MONEY_RATE).longValue(), payment.getOrderNo(), "", payment.getPlatformType()));
        if (refund.getCode() != 200 || refund.getData() == null || !"SUCCESS".equals(refund.getData().getStatus())) {
            if (refund.getData() != null && refund.getData().getErrMessage() != null) {
                throw new CzgException(refund.getData().getErrMessage());
            }
            throw new CzgException(refund.getMsg());
        } else {
            OrderPayment upPayment = new OrderPayment();
            upPayment.setPayStatus(PayTypeConstants.PayStatus.SUCCESS);
            upPayment.setPayTime(LocalDateTimeUtil.parse(refund.getData().getRefundTime()));
            upPayment.setTradeNumber(refund.getData().getThirdRefundNo());
            upPayment.setRespJson(refund.getData().getOriginalData());
            paymentService.update(upPayment, new QueryWrapper().eq(OrderPayment::getId, refundId));
        }
    }

    //触发订单退款时 后续处理
    @Async
    public void refundOrderAfter(@NonNull Long orderId, @NonNull Long shopId, Long userId, String orderNo,
                                 Integer pointsNum, boolean isFirstRefund, boolean isAllRefund) {
        if (isFirstRefund) {
            // 退款分销还原
            FunUtils.safeRunVoid(() -> distributionUserService.refund(orderId, orderNo),
                    "订单id:{} 退款,分销处理失败", orderId);
            if (userId == null) {
                return;
            }
            FunUtils.safeRunVoid(() -> consumerCouponService.removeConsumerCoupon(shopId, userId, orderId),
                    "订单id:{} 退款,消费赠券回撤处理失败", orderId);
            FunUtils.safeRunVoid(() -> consumeCashbackService.removeCashback(shopId, userId, orderId, orderNo),
                    "订单id:{} 退款,消费返现扣除处理失败", orderId);
            FunUtils.safeRunVoid(() -> pointsConfigService.removeConsumeAwardPoints(shopId, userId, orderId, orderNo),
                    "订单id:{} 退款,赠送积分扣除失败", orderId);
        }
        if (isAllRefund && userId != null && pointsNum != null && pointsNum > 0) {
            FunUtils.safeRunVoid(() -> mkPointsUserService.alterPoints(userId, null, shopId, PointsConstant.ADD,
                            pointsNum, orderId, StrUtil.format("订单退款返还{}积分", pointsNum)),
                    "订单id:{} 退款,赠送积分扣除失败", orderId);

        }
    }


    private void upOrderPayInfo(@NonNull Long orderId, @NonNull PayEnums payType, @NotBlank Long paymentId, String remark) {
        if (paymentId == null) {
            throw new CzgException("未获取到支付记录");
        }
        orderInfoCustomService.updatePayOrderId(orderId, paymentId, payType.getValue(), remark);
    }
}
