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

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.ctrls.AbstractCtrl;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.exception.ResponseException;
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
import com.jeequan.jeepay.core.interfaces.paychannel.IChannelNoticeService;
import com.jeequan.jeepay.core.interfaces.paychannel.IIsvmchStoreApplymentNotifyService;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.db.entity.MchApplyment;
import com.jeequan.jeepay.db.entity.MchStore;
import com.jeequan.jeepay.db.entity.PayOrder;
import com.jeequan.jeepay.db.entity.RouteManage;
import com.jeequan.jeepay.pay.service.PayMchNotifyService;
import com.jeequan.jeepay.pay.service.PayOrderMarketReissueService;
import com.jeequan.jeepay.pay.service.PayOrderProcessService;
import com.jeequan.jeepay.service.impl.*;
import com.jeequan.jeepay.thirdparty.channel.ryxpay.model.ReqMethod;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
* 渠道侧的通知入口Controller 【分为同步跳转（doReturn）和异步回调(doNotify) 】
*
* @author terrfly
*
* @date 2021/6/8 17:26
*/
@Slf4j
@Controller
public class ChannelNoticeController extends AbstractCtrl {

    @Autowired private PayOrderService payOrderService;
    @Autowired private IConfigContextQueryService configContextQueryService;
    @Autowired private PayMchNotifyService payMchNotifyService;
    @Autowired private PayOrderProcessService payOrderProcessService;
    @Autowired private MchApplymentService mchApplymentService;
    @Autowired private MchStoreService mchStoreService;
    @Autowired private SettleInfoService settleInfoService;
    @Autowired private PayOrderMarketReissueService payOrderMarketReissueService;
    @Autowired private RouteManageService routeManageService;


    /** 同步通知入口
     *
     * payOrderId动态参数， 可能是固定格式： 规则如下：
     * JEEPAYISV_V1000001 (CS.PAY_RETURNURL_FIX_ISV_PREFIX)
     * JEEPAYMCHAPP_M1000001
     * 如果是以上格式， 那么需要通道内自行解析，  &   不校验 urlOrderId 是合法性。
     *
     * payOrderId 前缀为 ONLYJUMP_，表示直接跳转
     *
     * **/
    @RequestMapping(value= {"/api/pay/return/{ifCode}", "/api/pay/return/{ifCode}/{payOrderId}"})
    public String doReturn(HttpServletRequest request, @PathVariable("ifCode") String ifCode, @PathVariable(value = "payOrderId", required = false) String urlOrderId){

        String payOrderId = null;
        String logPrefix = "进入[" +ifCode+ "]支付同步跳转：urlOrderId：["+ StringUtils.defaultIfEmpty(urlOrderId, "") + "] ";
        log.info("===== {} =====" , logPrefix);

        try {

            // 参数有误
            if(StringUtils.isEmpty(ifCode)){
                return this.toReturnPage("ifCode is empty");
            }

            //查询支付接口是否存在
            IChannelNoticeService payNotifyService = SpringBeansUtil.getBean(ifCode + "ChannelNoticeService", IChannelNoticeService.class);

            // 支付通道接口实现不存在
            if(payNotifyService == null){
                log.error("{}, interface not exists ", logPrefix);
                return this.toReturnPage("[" + ifCode + "] interface not exists");
            }

            // 仅做跳转，直接跳转订单的returnUrl
            onlyJump(urlOrderId, logPrefix);

            // 解析订单号 和 请求参数
            MutablePair<String, Object> mutablePair = payNotifyService.parseParams(request, urlOrderId, IChannelNoticeService.NoticeTypeEnum.DO_RETURN);
            if(mutablePair == null){ // 解析数据失败， 响应已处理
                log.error("{}, mutablePair is null ", logPrefix);
                throw new BizException("解析数据异常！"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
            }

            //解析到订单号
            payOrderId = mutablePair.left;
            log.info("{}, 解析数据为：payOrderId:{}, params:{}", logPrefix, payOrderId, mutablePair.getRight());

            // 判断url订单号是否合法
            this.judgeUrlOrderId(urlOrderId, payOrderId, logPrefix);

            //获取订单号 和 订单数据
            PayOrder payOrder = payOrderService.getById(payOrderId);

            // 订单不存在
            if(payOrder == null){
                log.error("{}, 订单不存在. payOrderId={} ", logPrefix, payOrderId);
                return this.toReturnPage("支付订单不存在");
            }

            //查询出商户应用的配置信息
            MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(payOrder.getMchNo(), payOrder.getAppId(),payOrder.getMchExtNo());

            //调起接口的回调判断
            ChannelRetMsg notifyResult = payNotifyService.doNotice(request, mutablePair.getRight(), payOrder, mchAppConfigContext, IChannelNoticeService.NoticeTypeEnum.DO_RETURN);

            // 返回null 表明出现异常， 无需处理通知下游等操作。
            if(notifyResult == null || notifyResult.getChannelState() == null || notifyResult.getResponseEntity() == null){
                log.error("{}, 处理回调事件异常  notifyResult data error, notifyResult ={} ",logPrefix, notifyResult);
                throw new BizException("处理回调事件异常！"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
            }

            //判断订单状态
            if(notifyResult.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS) {
                payOrder.setState(PayOrder.STATE_SUCCESS);
            }else if(notifyResult.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_FAIL) {
                payOrder.setState(PayOrder.STATE_FAIL);
            }

            boolean hasReturnUrl = StringUtils.isNotBlank(payOrder.getReturnUrl());
            log.info("===== {}, 订单通知完成。 payOrderId={}, parseState = {}, hasReturnUrl={} =====", logPrefix, payOrderId, notifyResult.getChannelState(), hasReturnUrl);

            //包含通知地址时
            if(hasReturnUrl){
                // 重定向
                response.sendRedirect(payMchNotifyService.createReturnUrl(payOrder, mchAppConfigContext.getMchApp().getAppSecret()));
                return null;
            }else{

                //跳转到支付成功页面
                return this.toReturnPage(null);
            }

        } catch (BizException e) {
            log.error("{}, payOrderId={}, BizException", logPrefix, payOrderId, e);
            return this.toReturnPage(e.getMessage());

        } catch (ResponseException e) {
            log.error("{}, payOrderId={}, ResponseException", logPrefix, payOrderId, e);
            return this.toReturnPage(e.getMessage());

        } catch (Exception e) {
            log.error("{}, payOrderId={}, 系统异常", logPrefix, payOrderId, e);
            return this.toReturnPage(e.getMessage());
        }
    }


    /** 异步回调入口 **/
    @ResponseBody
    @RequestMapping(value= {"/api/pay/notify/{ifCode}", "/api/pay/notify/{ifCode}/{payOrderId}"})
    public ResponseEntity doNotify(HttpServletRequest request,HttpServletResponse response, @PathVariable("ifCode") String ifCode, @PathVariable(value = "payOrderId", required = false) String urlOrderId){

        String payOrderId = null;
        String logPrefix = "进入[" +ifCode+ "]支付回调：urlOrderId：["+ StringUtils.defaultIfEmpty(urlOrderId, "") + "] ";
        log.info("===== {} =====" , logPrefix);
        try {

            // 参数有误
            if(StringUtils.isEmpty(ifCode)){
                return ResponseEntity.badRequest().body("ifCode is empty");
            }

            //查询支付接口是否存在
            IChannelNoticeService payNotifyService = SpringBeansUtil.getBean(ifCode + "ChannelNoticeService", IChannelNoticeService.class);

            // 支付通道接口实现不存在
            if(payNotifyService == null){
                log.error("{}, interface not exists ", logPrefix);
                return ResponseEntity.badRequest().body("[" + ifCode + "] interface not exists");
            }

            // 解析订单号 和 请求参数
            MutablePair<String, Object> mutablePair = payNotifyService.parseParams(request, urlOrderId, IChannelNoticeService.NoticeTypeEnum.DO_NOTIFY);
            if(mutablePair == null){ // 解析数据失败， 响应已处理
                log.error("{}, mutablePair is null ", logPrefix);
                throw new BizException("解析数据异常！"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
            }

            //解析到订单号
            payOrderId = mutablePair.left;
            log.info("{}, 解析数据为：payOrderId:{}, params:{}", logPrefix, payOrderId, mutablePair.getRight());

            // 判断url订单号是否合法
            this.judgeUrlOrderId(urlOrderId, payOrderId, logPrefix);

            //获取订单号 和 订单数据
            PayOrder payOrder = payOrderService.getById(payOrderId);

            // 订单不存在
            if(payOrder == null){
                log.error("{}, 订单不存在. payOrderId={} ", logPrefix, payOrderId);
                //退款异步通知 由于瑞银信的特殊性 需要转发到退款回调去处理
                JSONObject right = (JSONObject) mutablePair.right;
                if(CS.IF_CODE.RYXPAY.equals(ifCode) && ReqMethod.ServiceCode.SMZF004.getValue().equals(right.getString("tranCode"))){
                    request.getRequestDispatcher("/api/refund/notify/" + ifCode).forward(request, response);
                }
                return payNotifyService.doNotifyOrderNotExists(request);
            }

            //查询出商户应用的配置信息
            MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(payOrder.getMchNo(), payOrder.getAppId(),payOrder.getMchExtNo());
            //调起接口的回调判断
            ChannelRetMsg notifyResult = payNotifyService.doNotice(request, mutablePair.getRight(), payOrder, mchAppConfigContext, IChannelNoticeService.NoticeTypeEnum.DO_NOTIFY);

            // 返回null 表明出现异常， 无需处理通知下游等操作。
            if(notifyResult == null || notifyResult.getChannelState() == null || notifyResult.getResponseEntity() == null){
                log.error("{}, 处理回调事件异常  notifyResult data error, notifyResult ={} ",logPrefix, notifyResult);
                throw new BizException("处理回调事件异常！"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
            }

            boolean updateOrderSuccess = true; //默认更新成功
            // 订单是 【支付中状态】
            if(payOrder.getState() == PayOrder.STATE_ING || payOrder.getState() == PayOrder.STATE_INIT) {
                // 渠道订单号
                payOrder.setChannelOrderNo(notifyResult.getChannelOrderId());
                //明确成功
                if(ChannelRetMsg.ChannelState.CONFIRM_SUCCESS == notifyResult.getChannelState()) {

                    updateOrderSuccess = payOrderService.updateIng2Success(payOrderId, notifyResult);

                    //订单支付成功 其他业务逻辑
                    if(updateOrderSuccess){
                        payOrderProcessService.confirmSuccess(payOrder, mchAppConfigContext);
                    }
                    //TODO 初始化D0的结算数据
                    log.info("处理D0交易初始化，订单号：{}",payOrder.getPayOrderId());
                    settleInfoService.initD0Settle(payOrder);
                    //营销活动相关处理
                    payOrderMarketReissueService.reissue(payOrder);
                    if(StringUtils.isNotBlank(payOrder.getRouteId())){
                        routeManageService.upStoreRouteData(payOrder.getFindAmt(),payOrder.getStoreId(),payOrder.getRouteId(),true);
                    }
                    //明确失败
                }else if(ChannelRetMsg.ChannelState.CONFIRM_FAIL == notifyResult.getChannelState()) {

                    updateOrderSuccess = payOrderService.updateIng2Fail(payOrderId,  notifyResult);
                }
            }

            // 更新订单 异常
            if(!updateOrderSuccess){
                log.error("{}, updateOrderSuccess = {} ",logPrefix, updateOrderSuccess);
                return payNotifyService.doNotifyOrderStateUpdateFail(request);
            }

            log.info("===== {}, 订单通知完成。 payOrderId={}, parseState = {} =====", logPrefix, payOrderId, notifyResult.getChannelState());

            return notifyResult.getResponseEntity();
        } catch (BizException e) {
            log.error("{}, payOrderId={}, BizException", logPrefix, payOrderId, e);
            return ResponseEntity.badRequest().body(e.getMessage());

        } catch (ResponseException e) {
            log.error("{}, payOrderId={}, ResponseException", logPrefix, payOrderId, e);
            return e.getResponseEntity();

        } catch (Exception e) {
            log.error("{}, payOrderId={}, 系统异常", logPrefix, payOrderId, e);
            return ResponseEntity.badRequest().body(e.getMessage());
        }
    }


    /** 异步回调入口 **/
    @ResponseBody
    @RequestMapping(value= {"/api/mchStoreApplyment/notify/{storeId}/{applyId}"})
    public ResponseEntity doNotifyStore(HttpServletRequest request, @PathVariable(value = "storeId") String storeId, @PathVariable(value = "applyId") String applyId){

        String logPrefix = "进入[" +applyId+ "]门店入驻回调：storeId：["+ StringUtils.defaultIfEmpty(storeId, "") + "] ";
        log.info("===== {} =====" , logPrefix);
        try {

            // 参数有误
            if(StringUtils.isEmpty(applyId)){
                return ResponseEntity.badRequest().body("applyId is empty");
            }
            // 参数有误
            if(StringUtils.isEmpty(storeId)){
                return ResponseEntity.badRequest().body("storeId is empty");
            }

            // 进件记录查询
            MchApplyment tbMchApplyment = mchApplymentService.getById(applyId);
            if (tbMchApplyment == null) {
                return ResponseEntity.badRequest().body("进件记录不存在");
            }
            // 门店进件信息
            if (StringUtils.isEmpty(tbMchApplyment.getStoreSuccResParameter())) {
                return ResponseEntity.badRequest().body("门店入驻信息不存在");
            }
            // 支付接口代码
            String ifCode = tbMchApplyment.getIfCode();
            // 参数有误
            if(StringUtils.isEmpty(ifCode)){
                return ResponseEntity.badRequest().body("ifCode is empty");
            }
            //查询回调接口是否存在
            IIsvmchStoreApplymentNotifyService notifyService = SpringBeansUtil.getBean(ifCode + "MchStoreApplymentNoticeService", IIsvmchStoreApplymentNotifyService.class);

            // 门店回调接口实现不存在
            if(notifyService == null){
                log.error("{}, interface not exists ", logPrefix);
                return ResponseEntity.badRequest().body("[" + ifCode + "] interface not exists");
            }
            // 门店信息是否存在
            MchStore mchStore = mchStoreService.getById(storeId);
            if (mchStore == null) {
                return ResponseEntity.badRequest().body("入驻门店不存在");
            }

            // 获取当前入驻门店信息
            JSONArray jsonArray = JSONArray.parseArray(tbMchApplyment.getStoreSuccResParameter());
            JSONObject dbStoreApplyment = null;
            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject json = (JSONObject) jsonArray.get(i);
                if (storeId.equals(json.getString("storeId"))) {
                    dbStoreApplyment = json;
                    jsonArray.remove(i);
                }
            }

            if(dbStoreApplyment == null){
                throw new BizException("处理回调事件异常！"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
            }

            String storeNo = dbStoreApplyment.getString("store_no");

            //调起接口的回调判断
            ChannelRetMsg notifyResult = notifyService.doNotice(request, storeNo, tbMchApplyment.getIsvNo(), IIsvmchStoreApplymentNotifyService.NoticeTypeEnum.DO_NOTIFY);

            // 返回null 表明出现异常， 无需处理通知下游等操作。
            if(notifyResult == null || notifyResult.getChannelState() == null || notifyResult.getResponseEntity() == null){
                log.error("{}, 处理回调事件异常  notifyResult data error, notifyResult ={} ",logPrefix, notifyResult);
                throw new BizException("处理回调事件异常！"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
            }

            if (notifyResult.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS) {
                // 门店审核通过
                dbStoreApplyment.put("state", MchApplyment.STATE_SUCCESS);
                jsonArray.add(dbStoreApplyment);
            }else if (notifyResult.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_FAIL) {
                // 审核失败
                dbStoreApplyment.put("state", MchApplyment.STATE_REJECT_WAIT_MODIFY);
                dbStoreApplyment.put("msg", notifyResult.getChannelErrMsg());
                jsonArray.add(dbStoreApplyment);
            }else {
                return notifyResult.getResponseEntity();
            }

            // 更新入驻信息
            MchApplyment updateApplyment = new MchApplyment();
            updateApplyment.setApplyId(applyId);
            updateApplyment.setStoreSuccResParameter(jsonArray.toJSONString());
            boolean update = mchApplymentService.updateById(updateApplyment);

            // 更新入驻信息 异常
            if(!update){
                log.error("{}, update = {} ",logPrefix, update);
            }

            log.info("===== {}, 门店入驻通知完成。 storeId={}, parseState = {} =====", logPrefix, storeId, notifyResult.getChannelState());

            return notifyResult.getResponseEntity();

        } catch (BizException e) {
            log.error("{}, storeId={}, BizException", logPrefix, storeId, e);
            return ResponseEntity.badRequest().body(e.getMessage());

        } catch (ResponseException e) {
            log.error("{}, storeId={}, ResponseException", logPrefix, storeId, e);
            return e.getResponseEntity();

        } catch (Exception e) {
            log.error("{}, storeId={}, 系统异常", logPrefix, storeId, e);
            return ResponseEntity.badRequest().body(e.getMessage());
        }
    }

    /*  跳转到支付成功页面 **/
    private String toReturnPage(String errInfo){


        return "cashier/returnPage";
    }


    /** 比较URL是否相等 */
    private void judgeUrlOrderId(String urlOrderId, String payOrderId, String logPrefix){

        // URL参数中的 orderId为空
        if(StringUtils.isEmpty(urlOrderId)){
            return ;
        }

        // 存在固定URL
        if(CS.parsePayReturnUrlFix(urlOrderId, CS.PAY_RETURNURL_FIX_MCHAPP_PREFIX) != null || CS.parsePayReturnUrlFix(urlOrderId, CS.PAY_RETURNURL_FIX_ISV_PREFIX) != null){
            return ;
        }

        // 比较URL参数和解析的是否相等
        if(!urlOrderId.equals(payOrderId)){
            log.error("{}, 订单号不匹配. urlOrderId={}, payOrderId={} ", logPrefix, urlOrderId, payOrderId);
            throw new BizException("订单号不匹配！");
        }
    }


    private void onlyJump(String urlOrderId, String logPrefix) throws IOException {

        if (StringUtils.isNotBlank(urlOrderId) && urlOrderId.startsWith(CS.PAY_RETURNURL_FIX_ONLY_JUMP_PREFIX)) {

            String payOrderId = urlOrderId.substring(CS.PAY_RETURNURL_FIX_ONLY_JUMP_PREFIX.length());

            //获取订单号 和 订单数据
            PayOrder payOrder = payOrderService.getById(payOrderId);

            // 订单不存在
            if(payOrder == null){
                log.error("{}, 订单不存在. payOrderId={} ", logPrefix, payOrderId);
                this.toReturnPage("支付订单不存在");
            }

            //查询出商户应用的配置信息
            MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(payOrder.getMchNo(), payOrder.getAppId(),payOrder.getMchExtNo());

            if (StringUtils.isBlank(payOrder.getReturnUrl())) {
                this.toReturnPage(null);
            }
            response.sendRedirect(payMchNotifyService.createReturnUrl(payOrder, mchAppConfigContext.getMchApp().getAppSecret()));
        }
    }

}
