package com.jeequan.jeepay.pay.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.jeequan.jeepay.components.mq.model.PayOrderDivisionMQ;
import com.jeequan.jeepay.components.mq.vender.IMQSender;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.IConfigContextQueryService;
import com.jeequan.jeepay.core.interfaces.paychannel.IDivisionService;
import com.jeequan.jeepay.core.model.mchconfig.MchDivisionConfig;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.utils.AmountUtil;
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.db.entity.*;
import com.jeequan.jeepay.service.impl.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 业务： 支付订单分账处理逻辑
 * @author terrfly
 *
 * @date 2021/8/27 9:43
 *
 *
 *   订单支付成功后（自动分账）
 *   mqSender.send(PayOrderDivisionMQ.build(payOrder.getPayOrderId(), CS.YES,null), 80); //80s 后执行
 *      PayOrderDivisionMQReceiver.receive (MQ接收消息)
 *          payOrderDivisionProcessService.processPayOrderDivision()
 *   //:  插入分账用户表、 调起接口、 更新状态。
 *
 *
 *
 *   重试接口
 *   mqSender.send(PayOrderDivisionMQ.build(record.getPayOrderId(), null, null, true));
 *      PayOrderDivisionMQReceiver.receive (MQ接收消息)
 *          payOrderDivisionProcessService.processPayOrderDivision()
 *   // 查询分账用户表、 调起接口、 更新状态。
 *
 *
 *   商户API接口：
 *   PayOrderDivisionExecController.exec()
 *      payOrderDivisionProcessService.processPayOrderDivision()
 *   // 插入分账、 调起接口， 更新状态。
 *
 *
 */
@Slf4j
@Component
public class PayOrderDivisionProcessService {

    @Autowired
    private PayOrderService payOrderService;
    @Autowired
    private MchDivisionReceiverService mchDivisionReceiverService;
    @Autowired
    private MchDivisionReceiverGroupService mchDivisionReceiverGroupService;
    @Autowired
    private PayOrderDivisionRecordService payOrderDivisionRecordService;
    @Autowired
    private IConfigContextQueryService configContextQueryService;

    @Autowired
    private MchConfigService mchConfigService;

    @Autowired
    private IMQSender mqSender;

    @Autowired
    private MchApplymentService mchApplymentService;

    @Autowired
    private SysConfigService sysConfigService;

    @Autowired
    private RedPacketRuleService redPacketRuleService;

    @Autowired
    private DivisionTaskService divisionTaskService;

    @Autowired
    private DivisionSubjectService divisionSubjectService;

    @Autowired
    private DivisionConnService divisionConnService;

    /***
    * 处理分账，
     * 1. 向外抛异常： 系统检查没有通过 / 系统级别异常
     * 2 若正常调起接口将返回渠道侧响应结果
    *
    * @author terrfly
    *
    * @date 2021/8/27 9:44
    */
    public ChannelRetMsg processPayOrderDivision(String payOrderId, Byte useSysAutoDivisionReceivers, List<PayOrderDivisionMQ.CustomerDivisionReceiver> receiverList,
                                                 Boolean isResend, Boolean isResendAndRecalAmount) {

        // 是否重发分账接口（ 当分账失败， 列表允许再次发送请求 ）
        if(isResend == null){
            isResend = false;
        }

        // 是否重新计算分账金额，比如盛付通第二天再发起分账并且产出了退款的情况（仅当isResend=true时生效）
        if(isResendAndRecalAmount == null){
            isResendAndRecalAmount = false;
        }

        if(isResend){

            // 更新订单状态
            payOrderDivisionRecordService.updateResendState(payOrderId);
        }


        String logPrefix = "订单["+payOrderId+"]执行分账";

        //查询订单信息
        PayOrder payOrder = payOrderService.getById(payOrderId);

        if(payOrder == null){
            log.error("{}，订单不存在", logPrefix);
            throw new BizException("订单不存在");
        }

        // 分账状态不正确
        if(payOrder.getDivisionState() != PayOrder.DIVISION_STATE_WAIT_TASK && payOrder.getDivisionState() != PayOrder.DIVISION_STATE_UNHAPPEN){
            log.error("{}, 分账状态不正确", logPrefix);
            throw new BizException("分账状态不正确");
        }

        //更新订单为： 分账任务处理中
        boolean updPayOrder = payOrderService.update(new LambdaUpdateWrapper<PayOrder>()
                .set(PayOrder::getDivisionState, PayOrder.DIVISION_STATE_ING)
                .eq(PayOrder::getPayOrderId, payOrderId)
                .eq(PayOrder::getDivisionState, payOrder.getDivisionState()));
        if(!updPayOrder){
            log.error("{}, 更新支付订单为分账处理中异常！", logPrefix);
            throw new BizException("更新支付订单为分账处理中异常");
        }
        // 所有的分账列表
        List<PayOrderDivisionRecord> recordList = null;
        // 重发通知，可直接查库
        if(isResend){
            recordList = payOrderDivisionRecordService.list(PayOrderDivisionRecord.gw().eq(PayOrderDivisionRecord::getPayOrderId, payOrderId));
            // 重发通知 & 重新计算金额
            if(isResendAndRecalAmount) {
                this.resendAndRecalAmount(payOrder, recordList);
            }
        }else{
            List<DivisionSubject> allReceiver = this.queryReceiver(useSysAutoDivisionReceivers, payOrder, receiverList);;

            //得到全部分账比例 (所有待分账账号的分账比例总和)
            BigDecimal allDivisionProfit = BigDecimal.ZERO;
            for (DivisionSubject receiver : allReceiver) {
                allDivisionProfit = allDivisionProfit.add(receiver.getDivisionValue());
            }
            //计算分账金额 = 商家实际入账金额
            MchDivisionConfig mchDivisionConfig = mchConfigService.queryMchDivisionConfigByDefault(payOrder.getMchNo());
            String calBaseAmountType = (mchDivisionConfig != null ? mchDivisionConfig.getCalBaseAmountType(): null);
            Long payOrderDivisionAmount = payOrderService.calMchIncomeAmount(payOrder, calBaseAmountType);

            //剩余待分账金额 (用作最后一个分账账号的 计算， 避免出现分账金额超出最大) [结果向下取整 ， 避免出现金额溢出的情况。 ]
            Long subDivisionAmount = AmountUtil.calPercentageFee(payOrderDivisionAmount, allDivisionProfit, BigDecimal.ROUND_FLOOR);

            recordList = new ArrayList<>();

            //计算订单分账金额, 并插入到记录表

            String batchOrderId = SeqKit.genDivisionBatchId();

            //如果是银盛的分账则 这里需要设置剩下分账给自己
            if(CS.IF_CODE.YSPAY.equals(payOrder.getIfCode()) && allReceiver.isEmpty()){
                PayOrderDivisionRecord record = genPaymentRecord(batchOrderId,payOrder);
                //入库保存
                payOrderDivisionRecordService.save(record);
                recordList.add(record);
            }else{
                for (DivisionSubject receiver : allReceiver) {

                    PayOrderDivisionRecord record = genRecord(batchOrderId, payOrder, receiver, payOrderDivisionAmount, subDivisionAmount, calBaseAmountType);
                    //剩余金额
                    subDivisionAmount = subDivisionAmount - record.getCalDivisionAmount();
                    //入库保存
                    payOrderDivisionRecordService.save(record);
                    recordList.add(record);
                }
            }
        }

        ChannelRetMsg channelRetMsg = null;

        try{

            //调用渠道侧分账接口
            IDivisionService divisionService = SpringBeansUtil.getBean(JeepayKit.getIfCodeOrigin(payOrder.getIfCode()) + "DivisionService", IDivisionService.class);
            if(divisionService == null){
                throw new BizException("通道无此分账接口");
            }


            List<com.jeequan.jeepay.core.entity.PayOrderDivisionRecord> recordList2 = new ArrayList<>();
            recordList2.addAll(recordList);

            channelRetMsg = divisionService.singleDivision(payOrder, recordList2, configContextQueryService.queryMchInfoAndAppInfoV2(payOrder.getMchNo(), payOrder.getAppId(),payOrder.getMchExtNo()));

            // 确认分账成功
            if(channelRetMsg.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS) {

                payOrderDivisionRecordService.updateRecordSuccessOrFail(recordList, PayOrderDivisionRecord.STATE_SUCCESS,
                        channelRetMsg.getChannelOrderId(), channelRetMsg.getChannelOriginResponse());

            }else if(channelRetMsg.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_FAIL){ // 分账失败（明确失败）

                payOrderDivisionRecordService.updateRecordSuccessOrFail(recordList, PayOrderDivisionRecord.STATE_FAIL,
                        channelRetMsg.getChannelOrderId(), channelRetMsg.getChannelErrMsg());

            }else if(channelRetMsg.getChannelState() == ChannelRetMsg.ChannelState.WAITING){ // 分账已受理（上游受理）

                payOrderDivisionRecordService.updateRecordSuccessOrFail(recordList, PayOrderDivisionRecord.STATE_ACCEPT,
                        channelRetMsg.getChannelOrderId(), channelRetMsg.getChannelErrMsg());

            }else if(channelRetMsg.getChannelState() == ChannelRetMsg.ChannelState.UNKNOWN){ // 分账处理中（一般为渠道侧或定时任务）

                payOrderDivisionRecordService.updateRecordSuccessOrFail(recordList, PayOrderDivisionRecord.STATE_ING,
                        channelRetMsg.getChannelOrderId(), channelRetMsg.getChannelErrMsg());

                // 等待MQ的下一次触发
                if(StringUtils.isNotBlank(channelRetMsg.getChannelAttach())){
                    JSONObject jsonObject = JSON.parseObject(channelRetMsg.getChannelAttach());
                    if(jsonObject.getInteger("mqDelayTime") != null){
                        mqSender.send(PayOrderDivisionMQ.build(payOrderId, null, null, true, true), jsonObject.getInteger("mqDelayTime"));
                    }
                }
            }

        } catch (Exception e) {
            log.error("{}, 调用分账接口异常", logPrefix, e);
            payOrderDivisionRecordService.updateRecordSuccessOrFail(recordList, PayOrderDivisionRecord.STATE_FAIL,
                    null, "系统异常：" + e.getMessage());

            channelRetMsg = ChannelRetMsg.confirmFail(null, null, e.getMessage());
        }

        //更新 支付订单主表状态  分账任务已结束。
        // 分账处理中时， 此时订单的分账状态：仍然是： 分账处理中 ing状态
        if(channelRetMsg.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS || channelRetMsg.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_FAIL){
            payOrderService.update(new LambdaUpdateWrapper<PayOrder>()
                    .set(PayOrder::getDivisionState, PayOrder.DIVISION_STATE_FINISH)
                    .set(PayOrder::getDivisionLastTime, new Date())
                    .eq(PayOrder::getPayOrderId, payOrderId)
                    .eq(PayOrder::getDivisionState, PayOrder.DIVISION_STATE_ING)
            );
        }

        return channelRetMsg;
    }

//    /**
//     * 构建收款方分账数据
//     * @param batchOrderId
//     * @param payOrder
//     * @return
//     */
    private PayOrderDivisionRecord genPaymentRecord(String batchOrderId, PayOrder payOrder) {
        PayOrderDivisionRecord record = new PayOrderDivisionRecord();
        record.setMchNo(payOrder.getMchNo());
        record.setIsvNo(payOrder.getIsvNo());
        record.setAppId(payOrder.getAppId());
        record.setMchName(payOrder.getMchName());
        record.setMchType(payOrder.getMchType());
        record.setIfCode(payOrder.getIfCode());
        record.setPayOrderId(payOrder.getPayOrderId());
        record.setPayOrderChannelOrderNo(payOrder.getChannelOrderNo()); //支付订单渠道订单号
        record.setPayOrderAmount(payOrder.getAmount()); //订单金额
        record.setPayOrderDivisionAmount(payOrder.getFindAmt()); // 订单计算分账金额
        record.setBatchOrderId(batchOrderId); //系统分账批次号
        record.setState(PayOrderDivisionRecord.STATE_WAIT); //状态: 待分账
        record.setReceiverAlias("收款方分账");
        record.setAccNo(payOrder.getMchExtNo());
        record.setAccName(payOrder.getMchName());
        record.setChannelAccNo(payOrder.getChannelMchNo());
        record.setAccType((byte)1);
        record.setRelationType("PAYMENT");
        record.setRelationTypeName("收款方分账");
        record.setDivisionProfit(BigDecimal.ONE);
        record.setCalBaseAmountType("INCOME_AMOUNT"); // 分账基数类型
        record.setCalDivisionAmount(payOrder.getFindAmt());
        record.setMchExtNo(payOrder.getMchExtNo());
        return record;
    }


    /** 生成对象信息 **/
    private PayOrderDivisionRecord genRecord(String batchOrderId, PayOrder payOrder, DivisionSubject receiver,
                                             Long payOrderDivisionAmount, Long subDivisionAmount, String calBaseAmountType){


        // customerDivisionAmount: 接入传入的 自定义金额
        Long customerDivisionAmount = receiver.extv().getLong("divisionAmount");

        PayOrderDivisionRecord record = new PayOrderDivisionRecord();
        record.setMchNo(payOrder.getMchNo());
        record.setIsvNo(payOrder.getIsvNo());
        record.setAppId(payOrder.getAppId());
        record.setMchName(payOrder.getMchName());
        record.setMchType(payOrder.getMchType());
        record.setIfCode(payOrder.getIfCode());
        record.setPayOrderId(payOrder.getPayOrderId());
        record.setPayOrderChannelOrderNo(payOrder.getChannelOrderNo()); //支付订单渠道订单号
        record.setPayOrderAmount(payOrder.getAmount()); //订单金额
        record.setPayOrderDivisionAmount(payOrderDivisionAmount); // 订单计算分账金额
        record.setBatchOrderId(batchOrderId); //系统分账批次号
        record.setState(PayOrderDivisionRecord.STATE_WAIT); //状态: 待分账
        record.setReceiverId(receiver.getDivisionNo());
        record.setTaskId(receiver.getTaskId());
        record.setReceiverAlias(receiver.getName());
        record.setAccType(receiver.getUserType());
        record.setAccNo(receiver.getDivisionNo());
        record.setAccName(receiver.getInfo().getIdCardName());
        record.setChannelAccNo(receiver.getChannelDivisionNo());
        record.setRelationType(receiver.getRemark());
        record.setRelationTypeName(receiver.getRemark());
        record.setDivisionProfit(receiver.getDivisionValue());
        record.setCalBaseAmountType(calBaseAmountType); // 分账基数类型

        // 接口直接传入分账金额
        if(customerDivisionAmount != null){
            record.setCalDivisionAmount(customerDivisionAmount);
            if(customerDivisionAmount <= 0){
                record.setDivisionProfit(BigDecimal.ZERO);
            }else{
                // 计算实际的百分比
                record.setDivisionProfit(new BigDecimal(customerDivisionAmount).divide(new BigDecimal(payOrderDivisionAmount), 4, BigDecimal.ROUND_HALF_UP));
            }

        }else{
            // 原逻辑：  根据比例计算分账金额
            // 计算订单的分佣金额
            if( subDivisionAmount <= 0 ) {
                record.setCalDivisionAmount(0L);
            }else{
                //计算的分账金额
                record.setCalDivisionAmount(AmountUtil.calPercentageFee(record.getPayOrderDivisionAmount(), record.getDivisionProfit()));
                if(record.getCalDivisionAmount() > subDivisionAmount){ // 分账金额超过剩余总金额时： 将按照剩余金额进行分账。
                    record.setCalDivisionAmount(subDivisionAmount);
                }
            }
        }
        record.setMchExtNo(payOrder.getMchExtNo());
        return record;
    }


    private List<DivisionSubject> queryReceiver(Byte useSysAutoDivisionReceivers, PayOrder payOrder, List<PayOrderDivisionMQ.CustomerDivisionReceiver> customerDivisionReceiverList){

        //红包分账组
        if(PayOrder.MAKET_RED_PACKET_MANUAL == payOrder.getDivisionMode()){
            if(payOrder.getExtDivAmt() == BigDecimal.ZERO.longValue()){
                return new ArrayList<>();
            }
            String divMchNo = sysConfigService.getDBMarketingConfig().getMchNo();
            MchApplyment applyment = mchApplymentService.getById(divMchNo);
            MchDivisionReceiver receiver = new MchDivisionReceiver();
            receiver.setReceiverAlias("平台红包账户");
            receiver.setReceiverGroupName("红包");
            receiver.setMchNo(applyment.getMchNo());
            receiver.setIsvNo(applyment.getIsvNo());
            receiver.setAppId(applyment.getAutoConfigMchAppId());
            receiver.setIfCode(applyment.getIfCode());
            //商户类型
            receiver.setAccType((byte)1);
            receiver.setAccNo(applyment.getApplyId());
            receiver.setAccName(applyment.getMchShortName());
            receiver.setChannelAccNo(applyment.getChannelMchNo());
            receiver.setRelationType("HEADQUARTER");
            receiver.setRelationTypeName("总部");
            receiver.setState(CS.YES);
            receiver.setIsExtDivAmt(Boolean.TRUE);
            //则采用商户设置的门店折扣来进行分账
            RedPacketRule rule = redPacketRuleService.getRuleByMchStrore(payOrder.getMchNo(), payOrder.getStoreId());
            BigDecimal scale = rule.getMarketScale().compareTo(BigDecimal.TEN) >= 0 ? rule.getMarketScale() : rule.getMarketScale().multiply(BigDecimal.TEN);
            BigDecimal calcScale = BigDecimal.ONE.subtract(scale.divide(BigDecimal.valueOf(100)));
            receiver.setDivisionProfit(calcScale);
            List<MchDivisionReceiver> list = new ArrayList<>(1);
            list.add(receiver);
            return null;
        }else{
            // 如果是自动分账组
            if(useSysAutoDivisionReceivers == CS.YES){
                DivisionTask autoTask = divisionTaskService.getAutoTask(payOrder.getIfCode());
                if(autoTask == null){
                    log.info("当前分账为自动分账，未获取该通道下配置自动分账任务,订单号：{}",payOrder.getPayOrderId());
                    return null;
                }
                List<DivisionSubject> receiverList = divisionSubjectService.listByTaskAndDivisionNo(autoTask.getTaskId(),payOrder.getStoreId());
                if(receiverList.isEmpty()){
                    log.info("当前分账为自动分账，未获取到当前分账分出方绑定分账接收方,订单号：{}",payOrder.getPayOrderId());
                }
                return receiverList;
            }else{

            }
//            LambdaQueryWrapper<MchDivisionReceiver> queryWrapper = MchDivisionReceiver.gw();
//
//            queryWrapper.eq(MchDivisionReceiver::getMchNo, payOrder.getMchNo()); //mchNo
//            queryWrapper.eq(MchDivisionReceiver::getAppId, payOrder.getAppId()); //appId
//            queryWrapper.eq(MchDivisionReceiver::getIfCode, payOrder.getIfCode()); //ifCode
//            queryWrapper.eq(MchDivisionReceiver::getState, CS.PUB_USABLE); // 可用状态的账号
//
//            // 自动分账组的账号
//            if(useSysAutoDivisionReceivers == CS.YES) {
//
//                List<MchDivisionReceiverGroup> groups = mchDivisionReceiverGroupService.list(
//                        MchDivisionReceiverGroup.gw().eq(MchDivisionReceiverGroup::getMchNo, payOrder.getMchNo())
//                                .eq(MchDivisionReceiverGroup::getAutoDivisionFlag, CS.YES));
//
//                if(groups.isEmpty()){
//                    return new ArrayList<>();
//                }
//
//                queryWrapper.eq(MchDivisionReceiver::getReceiverGroupId, groups.get(0).getReceiverGroupId());
//            }
//            //全部分账账号
//            List<MchDivisionReceiver> allMchReceiver = mchDivisionReceiverService.list(queryWrapper);
//            if(allMchReceiver.isEmpty()){
//                return allMchReceiver;
//            }
//
//            //自动分账组
//            if(useSysAutoDivisionReceivers == CS.YES){
//                return allMchReceiver;
//            }
//
//            // 自定义列表未定义
//            if(customerDivisionReceiverList == null || customerDivisionReceiverList.isEmpty()){
//                return new ArrayList<>();
//            }
//
//            // 过滤账号
//            List<MchDivisionReceiver> filterMchReceiver = new ArrayList<>();
//
//            for (MchDivisionReceiver mchDivisionReceiver : allMchReceiver) {
//                for (PayOrderDivisionMQ.CustomerDivisionReceiver customerDivisionReceiver : customerDivisionReceiverList) {
//
//                    // 查询匹配相同的项目
//                    if( mchDivisionReceiver.getReceiverId().equals(customerDivisionReceiver.getReceiverId()) ||
//                            mchDivisionReceiver.getReceiverGroupId().equals(customerDivisionReceiver.getReceiverGroupId())
//                    ){
//
//                        // 重新对分账比例赋值
//                        if(customerDivisionReceiver.getDivisionProfit() != null){
//                            mchDivisionReceiver.setDivisionProfit(customerDivisionReceiver.getDivisionProfit());
//                        }
//
//                        // 分账金额
//                        if(customerDivisionReceiver.getDivisionAmount() != null){
//                            mchDivisionReceiver.addExt("divisionAmount", customerDivisionReceiver.getDivisionAmount());
//                        }
//                        filterMchReceiver.add(mchDivisionReceiver);
//                    }
//                }
//            }

            return null;
        }
    }

    /** 重新计算分账金额  && 更新  **/
    private void resendAndRecalAmount(PayOrder dbPayOrder, List<PayOrderDivisionRecord> recordList){

        if(recordList.isEmpty()){
            return ;
        }

        //重新获取： 计算分账金额 = 商家实际入账金额
        Long payOrderDivisionAmount = payOrderService.calMchIncomeAmount(dbPayOrder, recordList.get(0).getCalBaseAmountType());

        // 金额相同， 无需重新计算
        if(recordList.get(0).getPayOrderDivisionAmount().equals(payOrderDivisionAmount)){
            return ;
        }


        //得到全部分账比例 (所有待分账账号的分账比例总和)
        BigDecimal allDivisionProfit = BigDecimal.ZERO;

        for (PayOrderDivisionRecord record : recordList) {
            allDivisionProfit = allDivisionProfit.add(record.getDivisionProfit());
        }

        //剩余待分账金额 (用作最后一个分账账号的 计算， 避免出现分账金额超出最大) [结果向下取整 ， 避免出现金额溢出的情况。 ]
        Long subDivisionAmount = AmountUtil.calPercentageFee(payOrderDivisionAmount, allDivisionProfit, BigDecimal.ROUND_FLOOR);

        for (PayOrderDivisionRecord record : recordList) {

            DivisionSubject receiver = new DivisionSubject();
            receiver.setDivisionValue(record.getDivisionProfit());
            // 重新计算订单金额 &&
            PayOrderDivisionRecord recordNew = genRecord(null, dbPayOrder, receiver, payOrderDivisionAmount, subDivisionAmount, record.getCalBaseAmountType());
            record.setPayOrderDivisionAmount(payOrderDivisionAmount); // 订单分账计算基数
            record.setCalDivisionAmount(recordNew.getCalDivisionAmount()); // 分账金额
            //剩余金额
            subDivisionAmount = subDivisionAmount - recordNew.getCalDivisionAmount();
        }
        // 批量更新 计算后的分账金额
        payOrderDivisionRecordService.batchUpdateRecordDivisionAmount(recordList);
    }

}
