package com.jeequan.jeepay.pay.ctrl.refund;

import cn.hutool.core.date.DateUtil;
import com.jeequan.jeepay.core.aop.MethodLog;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.MchApp;
import com.jeequan.jeepay.core.entity.MchInfo;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.exception.ChannelException;
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
import com.jeequan.jeepay.core.interfaces.paychannel.IDivisionService;
import com.jeequan.jeepay.core.interfaces.paychannel.IRefundService;
import com.jeequan.jeepay.core.model.ApiRes;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.model.rqrs.refund.RefundOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.refund.RefundOrderRS;
import com.jeequan.jeepay.core.utils.JeepayKit;
import com.jeequan.jeepay.core.utils.SeqKit;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.core.utils.StringKit;
import com.jeequan.jeepay.db.entity.PayOrder;
import com.jeequan.jeepay.db.entity.RefundOrder;
import com.jeequan.jeepay.pay.ctrl.ApiController;
import com.jeequan.jeepay.pay.ctrl.payorder.AbstractRefundOrderController;
import com.jeequan.jeepay.pay.service.PayMchNotifyService;
import com.jeequan.jeepay.pay.service.PayOrderDivisionRefundOrderCommonService;
import com.jeequan.jeepay.pay.util.ApiResKit;
import com.jeequan.jeepay.service.impl.PayOrderService;
import com.jeequan.jeepay.service.impl.RefundOrderService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;

/*
* 商户发起退款 controller
*
* @author terrfly
*
* @date 2021/6/16 15:54
*/
@Slf4j
@RestController
public class RefundOrderController extends AbstractRefundOrderController {

    @Autowired private PayOrderService payOrderService;
    @Autowired private RefundOrderService refundOrderService;
    @Autowired private IConfigContextQueryService configContextQueryService;
    @Autowired private PayOrderDivisionRefundOrderCommonService payOrderDivisionRefundOrderCommonService;


    /** 申请退款 **/
    @MethodLog(remark = "发起支付退款API")
    @PostMapping("/api/refund/refundOrder")
    public ApiRes refundOrder(){

        //获取参数 & 验签
        RefundOrderRQ rq = getRQByWithMchSign(RefundOrderRQ.class);

        return this.refund(rq);
    }

    public ApiRes refund(RefundOrderRQ rq){
        RefundOrder refundOrder = null;

        try {

            if(StringUtils.isAllEmpty(rq.getMchOrderNo(), rq.getPayOrderId())){
                throw new BizException("mchOrderNo 和 payOrderId不能同时为空");
            }

            PayOrder payOrder = payOrderService.queryMchOrder(rq.getMchNo(), rq.getPayOrderId(), rq.getMchOrderNo());
            if(payOrder == null){
                throw new BizException("退款订单不存在");
            }

            if(payOrder.getRefundState() == PayOrder.REFUND_STATE_ALL || payOrder.getRefundAmount() >= payOrder.getAmount()){
                throw new BizException("订单已全额退款，本次申请失败");
            }

            if(payOrder.getState() != PayOrder.STATE_SUCCESS && PayOrder.REFUND_STATE_ALL == payOrder.getRefundState()){
                throw new BizException("当前订单已退款完成或订单状态不正确");
            }

            if(payOrder.getRefundAmount() + rq.getRefundAmount() > payOrder.getAmount()){
                throw new BizException("申请金额超出订单可退款余额，请检查退款金额");
            }

            if(refundOrderService.count(RefundOrder.gw().eq(RefundOrder::getPayOrderId, payOrder.getPayOrderId()).eq(RefundOrder::getState, RefundOrder.STATE_ING)) > 0){
                throw new BizException("支付订单具有在途退款申请，请稍后再试");
            }

            //全部退款金额 （退款订单表）
            Long sumSuccessRefundAmount = refundOrderService.getBaseMapper().sumSuccessRefundAmount(payOrder.getPayOrderId());
            if(sumSuccessRefundAmount >= payOrder.getAmount()){
                throw new BizException("退款单已完成全部订单退款，本次申请失败");
            }

            if(sumSuccessRefundAmount + rq.getRefundAmount() > payOrder.getAmount()){
                throw new BizException("申请金额超出订单可退款余额，请检查退款金额");
            }

            String mchNo = rq.getMchNo();
            String appId = rq.getAppId();

            // 校验退款单号是否重复
            if(refundOrderService.count(RefundOrder.gw().eq(RefundOrder::getMchNo, mchNo).eq(RefundOrder::getMchRefundNo, rq.getMchRefundNo())) > 0){
                throw new BizException("商户退款订单号["+rq.getMchRefundNo()+"]已存在");
            }

            if(StringUtils.isNotEmpty(rq.getNotifyUrl()) && !StringKit.isAvailableUrl(rq.getNotifyUrl())){
                throw new BizException("异步通知地址协议仅支持http:// 或 https:// !");
            }

            //获取支付参数 (缓存数据) 和 商户信息
            MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(mchNo, appId,payOrder.getMchExtNo());
            if(mchAppConfigContext == null){
                throw new BizException("获取商户应用信息失败");
            }

            MchInfo mchInfo = mchAppConfigContext.getMchInfo();
            MchApp mchApp = mchAppConfigContext.getMchApp();

            // 是否有退款权限
            if (!mchInfo.getRefundMode().contains(MchInfo.REFUND_MODEL_API)) {
                throw new BizException("无退款权限！");
            }

            //获取退款接口
            IRefundService refundService = SpringBeansUtil.getBean(JeepayKit.getIfCodeOrigin(payOrder.getIfCode()) + "RefundService", IRefundService.class);
            if(refundService == null){
                throw new BizException("当前通道不支持退款！");
            }
            refundOrder = genRefundOrder(rq, payOrder, mchInfo, mchApp);

            refundService.preCheck(rq,refundOrder,payOrder);

            //退款单入库 退款单状态：生成状态  此时没有和任何上游渠道产生交互。
            refundOrderService.save(refundOrder);

            IDivisionService divisionService = getDivisionService(payOrder);
            // 退款业务发生事前 分账。
            if(divisionService != null && divisionService.isSupport() && !divisionService.divisionRefundIsOrderRefundAfterProc()){
                // 处理分账回退逻辑
                payOrderDivisionRefundOrderCommonService.processDivisionRefund(refundOrder, mchAppConfigContext);
            }
            // 调起退款接口
            ChannelRetMsg channelRetMsg = refundService.refund(rq, refundOrder, payOrder, mchAppConfigContext);

            //处理退款单状态
            this.processChannelMsg(channelRetMsg, refundOrder);

            // 退款业务发生 事后 分账。 明确成功再退分。
            if(divisionService != null && divisionService.isSupport() && divisionService.divisionRefundIsOrderRefundAfterProc()
                    && channelRetMsg.getChannelState() != null && channelRetMsg.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS){
                // 处理分账回退逻辑
                payOrderDivisionRefundOrderCommonService.processDivisionRefundByAfter(refundOrder);
            }

            RefundOrderRS bizRes = RefundOrderRS.buildByRefundOrder(refundOrder);

            return ApiResKit.okWithSign(bizRes, configContextQueryService.queryMchApp(rq.getMchNo(), rq.getAppId()), rq.getSignType());


        } catch (BizException e) {
            return ApiRes.customFail(e.getMessage());

        } catch (ChannelException e) {

            //处理上游返回数据
            this.processChannelMsg(e.getChannelRetMsg(), refundOrder);

            if(e.getChannelRetMsg().getChannelState() == ChannelRetMsg.ChannelState.SYS_ERROR ){
                return ApiRes.customFail(e.getMessage());
            }

            RefundOrderRS bizRes = RefundOrderRS.buildByRefundOrder(refundOrder);
            return ApiResKit.okWithSign(bizRes, configContextQueryService.queryMchApp(rq.getMchNo(), rq.getAppId()), rq.getSignType());

        } catch (Exception e) {
            log.error("系统异常：{}", e);
            return ApiRes.customFail("系统异常");
        }

    }



    private IDivisionService getDivisionService(PayOrder payOrder){
        if(PayOrder.DIVISION_STATE_UNHAPPEN == payOrder.getDivisionMode()){
            return null;
        }
        IDivisionService divisionService = SpringBeansUtil.getBean(JeepayKit.getIfCodeOrigin(payOrder.getIfCode()) + "DivisionService", IDivisionService.class);
        return divisionService;
    }

}
