package com.ruoyi.ss.refund.service.impl;

import com.ruoyi.common.annotation.DataScope;
import com.ruoyi.common.constant.ServiceConstants;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.ss.order.domain.Order;
import com.ruoyi.ss.order.domain.OrderVO;
import com.ruoyi.ss.order.service.IOrderService;
import com.ruoyi.ss.orderOper.service.IOrderOperService;
import com.ruoyi.ss.payBill.domain.PayBill;
import com.ruoyi.ss.payBill.domain.PayBillQuery;
import com.ruoyi.ss.payBill.domain.PayBillVO;
import com.ruoyi.ss.payBill.domain.enums.PayBillStatus;
import com.ruoyi.ss.payBill.service.PayBillService;
import com.ruoyi.ss.refund.domain.*;
import com.ruoyi.ss.refund.mapper.RefundMapper;
import com.ruoyi.ss.refund.service.IRefundService;
import com.ruoyi.ss.user.domain.UserVO;
import com.ruoyi.ss.user.service.IUserService;
import com.ruoyi.ss.pay.service.IWxPayService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;

import static com.ruoyi.common.constant.ServiceConstants.REFUND_STATUS_SUCCESS;
import static com.ruoyi.common.utils.SecurityUtils.getUserId;

/**
 * 退款订单Service业务层处理
 *
 * @author 邱贞招
 * @date 2024-05-10
 */
@Service
public class RefundServiceImpl implements IRefundService
{
    private static final Logger logger = LoggerFactory.getLogger(RefundServiceImpl.class);

    @Resource
    private RefundMapper refundMapper;

    @Resource
    private IOrderService orderService;

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private IOrderOperService orderOperService;

    @Autowired
    private IUserService userService;

    @Resource
    private IWxPayService wxPayService;

    @Autowired
    private PayBillService paymentService;


    /**
     * 查询退款订单
     *
     * @param id 退款订单主键
     * @return 退款订单
     */
    @Override
    public RefundVO selectEtRefundById(Long id)
    {
        return refundMapper.selectEtRefundById(id);
    }

    /**
     * 根据订单号查询退款订单对象
     *
     * @param orderNo 订单号
     * @return 退款订单
     */
    @Override
    public List<Refund> selectEtRefundByOrderNo(String orderNo)
    {
        return refundMapper.selectEtRefundByOrderNo(orderNo);
    }

    /**
     * 根据退款单号查询退款订单对象
     *
     * @param refundNo 退款订号
     * @return 退款订单
     */
    @Override
    public Refund selectEtRefundByRefundNo(String refundNo)
    {
        return refundMapper.selectEtRefundByRefundNo(refundNo);
    }


    /**
     * 查询退款订单列表
     *
     * @param rlRefund 退款订单
     * @return 退款订单
     */
    @Override
    public List<Refund> selectEtRefundList(RefundQuery rlRefund)
    {
        return refundMapper.selectEtRefundList(rlRefund);
    }

    /**
     * 查询退款订单列表
     *
     * @param rlRefund 退款订单
     * @return 退款订单
     */
    @Override
    @DataScope(merchantAlias = "m", userAlias = "u")
    public List<Refund> selectEtRefundListWithIsolate(RefundQuery rlRefund)
    {
        return refundMapper.selectEtRefundList(rlRefund);
    }

    /**
     * 退款
     * 1. 校验
     * 2. 创建退款对象
     * 3. 根据分成列表，退还相应比例服务费
     * 4. 更新支付表
     *      ① 改变支付单状态
     *      ② 增加退款中金额，在微信退款回调中改变已退款金额 清除退款中金额
     * 5. 更新订单表（取消，回调成功后更新）
     *      退款金额和退款状态
     * 6. 记录订单履历
     * 7. 发起退款
     *
     *
     * @param bo 退款bo
     * @return 结果
     */
    @Override
    public int refund(RefundBO bo)
    {
        OrderVO orderVO = bo.getOrder();
        RefundDTO dto = bo.getDto();
        PayBillVO payBill = bo.getPayBill();
        Boolean execute = transactionTemplate.execute(e -> {
            /** 2. 创建退款对象*/
            String refNo = saveRefund(dto, getUserId(),orderVO);
            /** todo 3. 根据分成列表，退还相应比例服务费   计算出退款的比例 保留两位小数点  比如退款金额为5元，  订单金额为32元  退款比例 = 5/32 = 0.16 （四舍五入）由系统来平账*/
//            refundDividendHandle(rlOrderVO, refundPercentage);
            /** todo 4. 更新支付单表,  支付单中的金额是多次退款的金额，  退款中金额，回调后更新退款中金额为0，添加到已退款金额中， 退款中金额加已退款金额不能大于订单金额 */
            PayBill updatedPayBill =  new PayBill();
            updatedPayBill.setStatus(PayBillStatus.REFUNDING.getStatus());
            PayBillQuery billQuery = new PayBillQuery();
            billQuery.setPayId(dto.getPayId());
            billQuery.setStatus(PayBillStatus.PAY_SUCCESS.getStatus());
            int i = paymentService.updateByQuery(updatedPayBill,billQuery);
            ServiceUtil.assertion(i != 1, "修改支付订单状态失败");

            // 增加退款中金额
            int addRefunding = paymentService.addRefundingAmount(dto.getPayId(), dto.getAmount());
            ServiceUtil.assertion(addRefunding != 1, "记录退款中的金额失败");

            /** 5. 记录订单履历 */
            UserVO merchantVO =  userService.selectUserById(getUserId());
            String desc = "退款金额:"+dto.getAmount()+"元";
            boolean b = orderOperService.recordOrderHistory(orderVO.getOrderNo(), ServiceConstants.ORDER_OPERATION_REFUND,
                    orderVO.getStatus(), orderVO.getStatus(), orderVO.getPayFee(), orderVO.getPayFee(),merchantVO.getUserId() , merchantVO.getPhonenumber(), desc);
            ServiceUtil.assertion(!b, "记录订单履历失败");

            /** 6. 发起退款 */
            try {
                wxPayService.refund(orderVO, "商家退款金额", dto.getAmount(), refNo,payBill.getPayNo());
            }catch (com.wechat.pay.java.core.exception.ServiceException ex){
                logger.error("退款失败:{}",ex.getErrorMessage());
                throw new ServiceException("退款失败:"+ex.getErrorMessage());
            }
            return Boolean.TRUE;
        });
        if(Boolean.FALSE.equals(execute))throw new ServiceException("【订单退款】失败");
        return 1;
    }

    @Override
    public String saveRefund(RefundDTO dto, Long userId, OrderVO orderVO) {
        Refund refund = new Refund();
        String refundNo = IdUtils.getOrderNo(ServiceConstants.ORDER_TYPE_REFUND_PREFIX);
        refund.setCreateTime(DateUtils.getNowDate());
        refund.setAmount(dto.getAmount());
        refund.setReason(dto.getReason());
        if(userId != null){
            refund.setUserId(userId);
            UserVO userVO = userService.selectUserById(userId);
            refund.setUserName(userVO.getUserName());
            refund.setUserType(userVO.getUserType());
        }
        refund.setPayId(dto.getPayId());
        refund.setRefundNo(refundNo);
        refund.setMerchantId(orderVO.getMerchantId());
        int i = refundMapper.insertEtRefund(refund);
        ServiceUtil.assertion(i == 0,"保存退款对象失败");
        return refundNo;
    }

    /**
     * 修改退款订单
     *
     * @param rlRefund 退款订单
     * @return 结果
     */
    @Override
    public int updateEtRefund(Refund rlRefund)
    {
        return refundMapper.updateEtRefund(rlRefund);
    }

    /**
     * 批量删除退款订单
     *
     * @param ids 需要删除的退款订单主键
     * @return 结果
     */
    @Override
    public int deleteEtRefundByIds(Long[] ids)
    {
        return refundMapper.deleteEtRefundByIds(ids);
    }

    /**
     * 删除退款订单信息
     *
     * @param id 退款订单主键
     * @return 结果
     */
    @Override
    public int deleteEtRefundById(Long id)
    {
        return refundMapper.deleteEtRefundById(id);
    }

    /**
     * 根据退款单号修改退款订单
     * @param rlRefund
     * @return
     */
    @Override
    public int updateEtRefundByRefundNo(Refund rlRefund) {
        return refundMapper.updateEtRefundByRefundNo(rlRefund);
    }

    /**
     * 判断是否用户是否还有未退款的押金
     *
     * @param userId 用户id
     * @return 结果
     */
    @Override
    public List<Order> checkUserDeposit(Long userId) {
       return refundMapper.checkUserDeposit(userId);
    }

    /**
     * 统计已退款金额
     *
     * @param query 条件
     * @return 结果
     */
    @Override
    public BigDecimal getRefundedAmount(RefundQuery query) {
        return refundMapper.getRefundedAmount(query);
    }

    /**
     * 同意退款
     */
    @Override
    public int passRefund(String refundNo) {
        Refund refund = refundMapper.selectEtRefundByRefundNo(refundNo);
        ServiceUtil.assertion(refund == null, "退款申请单不存在");
//        ServiceUtil.assertion(!ServiceConstants.REFUND_STATUS_PENDING.equals(refund.getStatus()), "退款申请单已处理,请勿操作！");

        // TODO 根据支付渠道 调用退款方法  2. 更新退款单状态为 REFUND_STATUS_SUCCESS
        Refund refund1 = new Refund();
        refund1.setRefundNo(refundNo);
        refund1.setStatus(REFUND_STATUS_SUCCESS);
        int i = refundMapper.updateEtRefundByRefundNo(refund);
        ServiceUtil.assertion(i == 0, "退款申请单更新失败");

        return 1;
    }

    /**
     * 拒绝退款
     */
    @Override
    public int reject(String refundNo) {
        Refund refund = refundMapper.selectEtRefundByRefundNo(refundNo);
        ServiceUtil.assertion(refund == null, "退款申请单不存在");
//        ServiceUtil.assertion(!ServiceConstants.REFUND_STATUS_PENDING.equals(refund.getStatus()), "退款申请单已处理,请勿操作！");
        Refund update  = new Refund();
        update.setRefundNo(refundNo);
//        update.setStatus(ServiceConstants.REFUND_STATUS_REJECTED);
        int i = refundMapper.updateEtRefundByRefundNo(update);
        ServiceUtil.assertion(i == 0, "退款申请单更新失败");
        return i;
    }

}
