package com.cpop.mall.business.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.cpop.core.base.entity.PageDomain;
import com.cpop.core.base.enums.OrderSource;
import com.cpop.core.base.exception.ServiceException;
import com.cpop.core.utils.SecurityUtils;
import com.cpop.core.utils.SpringUtils;
import com.cpop.core.utils.sql.SqlUtils;
import com.cpop.mall.business.bo.OrderRefundPageBo;
import com.cpop.mall.business.bo.OrderRejectRefundBo;
import com.cpop.mall.business.dto.UserPointDto;
import com.cpop.mall.business.entity.Order;
import com.cpop.mall.business.entity.OrderRefund;
import com.cpop.mall.business.mapper.OrderRefundMapper;
import com.cpop.mall.business.service.OrderRefundService;
import com.cpop.mall.business.service.OrderService;
import com.cpop.mall.business.vo.OrderRefundPageVo;
import com.cpop.pay.framewok.config.wxPay.WxPayProperties;
import com.cpop.pay.framewok.handler.wxPay.WxPayHandler;
import com.cpop.system.business.entity.ProfitSharing;
import com.cpop.system.business.service.ProfitSharingService;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result;
import com.github.binarywang.wxpay.bean.profitsharing.request.ProfitSharingReturnRequest;
import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.core.datasource.DataSourceKey;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.row.DbChain;
import com.mybatisflex.core.row.RowKey;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;

import static com.cpop.core.base.table.table.SysUserTableDef.SYS_USER;
import static com.cpop.mall.business.entity.table.OrderRefundTableDef.ORDER_REFUND;
import static com.cpop.mall.business.entity.table.OrderTableDef.ORDER;
import static com.cpop.system.business.entity.table.BrandTableDef.BRAND;
import static com.cpop.system.business.entity.table.ProfitSharingTableDef.PROFIT_SHARING;

/**
 * 商城订单退款记录表 服务层实现。
 *
 * @author DB
 * @since 2023-10-24
 */
@Service("orderRefundService")
public class OrderRefundServiceImpl extends ServiceImpl<OrderRefundMapper, OrderRefund> implements OrderRefundService {

    @Autowired
    private WxPayHandler wxPayHandler;

    @Autowired
    private WxPayProperties wxPayProperties;

    /**
     * @descriptions 同意退款
     * @author DB
     * @date 2023/10/24 16:10
     * @param id 订单id
     * @return: void
     */
    @Override
    public void agreeRefund(String id) {
        OrderRefund orderRefund = this.queryChain().where(ORDER_REFUND.ID.eq(id)).one();
        Order order = SpringUtils.getBean(OrderService.class).queryChain().where(ORDER.ID.eq(orderRefund.getOrderId())).one();
        //微信支付
        if (order.getPayType() == 0) {
            try {
                WxPayService wxPayService = wxPayHandler.getWxPayService();
                //分账记录
                ProfitSharingService profitSharingService = SpringUtils.getBean(ProfitSharingService.class);
                ProfitSharing profitSharing = profitSharingService.queryChain().where(PROFIT_SHARING.ORDER_ID.eq(order.getId()))
                        .and(PROFIT_SHARING.ORDER_SOURCE.eq(OrderSource.MALL.toString()))
                        .and(PROFIT_SHARING.PROFIT_SHARING_STATUS.eq(1))
                        .one();
                if (null != profitSharing) {
                    ProfitSharingReturnRequest profitSharingReturnRequest = new ProfitSharingReturnRequest();
                    profitSharingReturnRequest.setOrderId(profitSharing.getOutProfitSharingId());
                    profitSharingReturnRequest.setOutReturnNo(profitSharing.getId());
                    profitSharingReturnRequest.setDescription("分账退款");
                    profitSharingReturnRequest.setSubMchId(profitSharing.getPayAccount());
                    profitSharingReturnRequest.setReturnAccount(wxPayProperties.getSharingAccount());
                    profitSharingReturnRequest.setReturnAccountType("MERCHANT_ID");
                    profitSharingReturnRequest.setReturnAmount(profitSharing.getAmount().intValue());
                    wxPayService.getProfitSharingService().profitSharingReturn(profitSharingReturnRequest);
                    //分账退款记录设置退款
                    profitSharingService.updateChain()
                            .set(PROFIT_SHARING.PROFIT_SHARING_STATUS, 2)
                            .where(PROFIT_SHARING.ID.eq(profitSharing.getId()))
                            .update();
                }
                WxPayRefundV3Request request = new WxPayRefundV3Request();
                WxPayRefundV3Request.Amount amount = new WxPayRefundV3Request.Amount();
                //退款金额(单位分)
                int refund = order.getTotalAmount().scaleByPowerOfTen(2).intValue();
                amount.setRefund(refund)
                        .setTotal(refund)
                        .setCurrency("CNY");
                request.setTransactionId(order.getOutOrderNo())
                        .setSubMchid(wxPayService.getConfig().getSubMchId())
                        .setNotifyUrl(wxPayProperties.getNotifyRefund())
                        .setOutRefundNo(id)
                        .setReason(orderRefund.getRefundReason())
                        .setAmount(amount);
                wxPayService.refundV3(request);
            } catch (WxPayException e) {
                throw new ServiceException(e.getMessage());
            }
        } else if (order.getPayType() == 1){
            //扣除积分
            JSONObject loginUserInfo = new JSONObject();
            loginUserInfo.put("openId",orderRefund.getOpenId());
            JSONObject loginStaffInfo = SecurityUtils.getInstance().getLoginUserInfo();
            loginUserInfo.put("brandId",loginStaffInfo.getString("brandId"));
            UserPointDto userPoint = SpringUtils.getBean(OrderService.class).getUserPoint(loginUserInfo);
            try {
                LocalDateTime now = LocalDateTime.now();
                DataSourceKey.use("jambox");
                DbChain.table("t_point_record")
                        .setId(RowKey.of("record_id", KeyType.Auto))
                        .set("user_id", userPoint.getOldUserId())
                        .set("brand_id", userPoint.getBrandCloudId())
                        .set("point_behaviour", 9)
                        .set("behaviour_record", "商城退款商品返回积分")
                        .set("point_change", order.getTotalPoint())
                        .set("point_time", now)
                        .set("cumulative", userPoint.getTotalPoint() + order.getTotalPoint())
                        .set("creation_by", userPoint.getOldUserId())
                        .set("last_modified", userPoint.getOldUserId())
                        .set("creation_time", now)
                        .save();
            } finally {
                DataSourceKey.clear();
            }
        }

    }

    /**
     * @descriptions 拒绝退款
     * @author DB
     * @date 2023/10/24 16:13
     * @param bo 请求参数
     * @return: void
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rejectRefund(OrderRejectRefundBo bo) {
        //获取订单
        OrderRefund refund = this.queryChain().where(ORDER_REFUND.ID.eq(bo.getId())).one();
        this.updateChain().set(ORDER_REFUND.REFUND_STATUS, 2)
                .set(ORDER_REFUND.REFUND_REASON, bo.getRejectReason())
                .where(ORDER_REFUND.ID.eq(bo.getId()))
                .update();
        //更改订单为原状态
        SpringUtils.getBean(OrderService.class).updateChain().set(ORDER.ORDER_STATUS,ORDER.PREVIOUS_STATUS)
                .where(ORDER.ID.eq(refund.getOrderId())).update();
    }

    /**
     * @descriptions 商城退款列表
     * @author DB
     * @date 2023/10/27 17:34
     * @param bo 请求参数
     * @return: com.mybatisflex.core.paginate.Page<com.cpop.mall.business.vo.OrderRefundPageVo>
     */
    @Override
    public Page<OrderRefundPageVo> getOrderRefundPage(OrderRefundPageBo bo) {
        PageDomain pageDomain = SqlUtils.getInstance().getPageDomain();
        QueryWrapper queryWrapper = QueryWrapper.create();
        if (null != bo.getRefundStatus()) {
            if (-1 == bo.getRefundStatus()) {
                queryWrapper.and(ORDER_REFUND.REFUND_STATUS.in(1, 2));
            } else {
                queryWrapper.and(ORDER_REFUND.REFUND_STATUS.eq(bo.getRefundStatus()));
            }
        }
        return this.mapper.paginateAs(pageDomain.getPageNum(),pageDomain.getPageSize(),
                queryWrapper.select(ORDER_REFUND.REFUND_STATUS,ORDER_REFUND.REFUND_REASON,ORDER_REFUND.ID,ORDER_REFUND.ORDER_ID,ORDER_REFUND.CREATE_TIME)
                        //订单相关参数
                        .select(ORDER.ORDER_STATUS,ORDER.OUT_ORDER_NO,ORDER.PAY_USER_ID,ORDER.PRODUCT_NAMES,ORDER.TOTAL_AMOUNT,ORDER.PAY_USER_NAME,ORDER.BRAND_ID,ORDER.RECEIVE_NAME,
                                ORDER.RECEIVE_PHONE,ORDER.RECEIVE_ADDRESS,ORDER.LOGISTICS_ORDER,ORDER.REMARKS)
                        //品牌/店铺或校区
                        .select(BRAND.BRAND_NAME)
                        .from(ORDER_REFUND)
                        .leftJoin(ORDER).on(ORDER.ID.eq(ORDER_REFUND.ORDER_ID))
                        .leftJoin(SYS_USER).on(SYS_USER.ID.eq(ORDER.PAY_USER_ID))
                        .leftJoin(BRAND).on(BRAND.ID.eq(ORDER.BRAND_ID))
                        .and(ORDER.PRODUCT_NAMES.like(bo.getProductName()))
                        .and(ORDER.PAY_USER_NAME.like(bo.getPayUserName()))
                        .and(ORDER.RECEIVE_PHONE.like(bo.getReceivePhone()))
                        .orderBy(ORDER_REFUND.CREATE_TIME.desc()),
                OrderRefundPageVo.class);
    }

    /**
     * @descriptions 退款回调通知处理
     * @author DB
     * @date 2023/10/27 18:22
     * @param xmlData 请求数据
     * @return: void
     */
    @Override
    public void wxPayNotifyRefund(String xmlData) {
        try {
            WxPayRefundNotifyV3Result result = wxPayHandler.getWxPayService().parseRefundNotifyV3Result(xmlData, null);
            WxPayRefundNotifyV3Result.DecryptNotifyResult reqInfo = result.getResult();
            //获取退款单号
            this.updateChain().set(ORDER_REFUND.REFUND_STATUS, 1)
                    .set(ORDER_REFUND.OUT_REFUND_ID, reqInfo.getRefundId())
                    .where(ORDER_REFUND.ID.eq(reqInfo.getOutRefundNo()))
                    .update();
        } catch (WxPayException e) {
            throw new ServiceException(e.getMessage());
        }
    }
}
