
package com.jeequan.jeepay.pay.service;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Pair;
import com.alibaba.fastjson.JSON;
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.IChannelAccountService;
import com.jeequan.jeepay.core.model.cashout.CashoutParams;
import com.jeequan.jeepay.core.model.cashout.CashoutRetMsg;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
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.ChannelAccountCashoutRecord;
import com.jeequan.jeepay.db.entity.PayInterfaceConfig;
import com.jeequan.jeepay.db.entity.PayInterfaceDefine;
import com.jeequan.jeepay.db.entity.PayOrder;
import com.jeequan.jeepay.service.impl.ChannelAccountCashoutRecordService;
import com.jeequan.jeepay.service.impl.PayInterfaceConfigService;
import com.jeequan.jeepay.service.impl.PayInterfaceDefineService;
import com.jeequan.jeepay.service.impl.PayOrderService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;

/*
 * 提现 api接口
 *
 * @author zx
 * 
 * @date 2021/6/8 17:20
 */

@Service
@Slf4j
public class MchChannelCashoutService {

    @Autowired private ChannelAccountCashoutRecordService cashoutRecordService;
    @Autowired private PayOrderService payOrderService;
    @Autowired private IConfigContextQueryService configContextQueryService;
    @Autowired private PayInterfaceDefineService payInterfaceDefineService;
    @Autowired private PayInterfaceConfigService payInterfaceConfigService;

    /** 调起提现接口 **/
    public void cashout(String payOrderId){

        PayOrder payOrder = payOrderService.getById(payOrderId);
        if(payOrder == null || payOrder.getState() != PayOrder.STATE_SUCCESS){
            log.info("订单自动提现失败，当前订单非成功状态，payOrderId={}", payOrderId);
            return ;
        }

        try {
            // 查询结算接口是否存在
            IChannelAccountService channelAccountService = SpringBeansUtil.getBean(JeepayKit.getIfCodeOrigin(payOrder.getIfCode()) + "ChannelAccountService", IChannelAccountService.class);
            // 提现接口实现不存在
            if(channelAccountService == null){
                log.error("订单自动提现失败，提现接口实现不存在，payOrderId={}，ifCode={}", payOrder.getPayOrderId(), payOrder.getIfCode());
            }

            // 校验提现配置
            CashoutParams cashoutParams = checkCashoutJudge(payOrder.getIfCode(), payOrder.getIsvNo(), payOrder.getAppId(), ChannelAccountCashoutRecord.CASHOUT_FROM_ORDER);

            MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfoV2(payOrder.getMchNo(), payOrder.getAppId(),payOrder.getMchExtNo());

            // 查询三方账户余额
            Pair<String, Long> pair = channelAccountService.queryBalanceAmount(mchAppConfigContext, payOrder.getIfCode());
            if(pair.getValue() <= 0){
                log.error("订单自动提现失败，商户可用余额不足，payOrderId={}，余额={}，提现金额={}", payOrder.getPayOrderId(), pair.getValue(), payOrder.getAmount());
                return;
            }

            // 校验提现金额
            Long cashoutAmount = checkCashoutAmount(pair.getValue(), cashoutParams, mchAppConfigContext.getAppId());

            // 提现单入库
            ChannelAccountCashoutRecord cashoutRecord = cashoutRecordService.initRecord(SeqKit.genCashoutRecordId(), cashoutAmount, pair.getValue(), payOrder.getMchNo(), payOrder.getMchName(), payOrder.getAppId(), payOrder.getIsvNo(),
                    payOrder.getIfCode(), pair.getKey(), payOrder.getPayOrderId(), null, null);

            // 发起提现
            CashoutRetMsg cashoutRetMsg = channelAccountService.cashout(cashoutRecord, mchAppConfigContext, payOrder.getIfCode());
            processChannelMsg(cashoutRetMsg, cashoutRecord.getRid());

        } catch (BizException e) {
            log.error("订单自动提现失败：payOrderId={}，errMsg={}", payOrderId, e.getMessage());
        }
    }

    /** 处理返回的渠道信息，并更新状态 **/
    public void processChannelMsg(CashoutRetMsg cashoutRetMsg, String recordId){

        //对象为空 || 上游返回状态为空， 则无需操作
        if(cashoutRetMsg == null || cashoutRetMsg.getChannelState() == null){
            throw new BizException("上游返回状态为空");
        }

        if(cashoutRetMsg.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_SUCCESS) {
            updateStatusInit2Other(recordId, ChannelAccountCashoutRecord.CASHOUT_STATE_SUCCESS, cashoutRetMsg.getChannelOrderId(), cashoutRetMsg.getBankName(), cashoutRetMsg.getBankAccount(), cashoutRetMsg.getBankAccountName(), null);
        }else if(cashoutRetMsg.getChannelState() == ChannelRetMsg.ChannelState.CONFIRM_FAIL) {

            //更新为失败状态
            String applyMsg = String.format("failInfo=[code=%s, msg=%s]", cashoutRetMsg.getChannelErrCode(), cashoutRetMsg.getChannelErrMsg());
            updateStatusInit2Other(recordId, ChannelAccountCashoutRecord.CASHOUT_STATE_FAIL, null, null, null, null, applyMsg);
        }else {
            updateStatusInit2Other(recordId, ChannelAccountCashoutRecord.CASHOUT_STATE_ING, cashoutRetMsg.getChannelOrderId(), null, null, null, null);
        }
    }

    private void updateStatusInit2Other(String recordId, byte state, String channelRid, String bankName, String bankAccount, String bankAccountName, String channelErrMsg) {

        boolean isSuccess;
        if(state == ChannelAccountCashoutRecord.CASHOUT_STATE_SUCCESS){
            isSuccess = cashoutRecordService.updateIng2Success(recordId, channelRid, bankName, bankAccount, bankAccountName);
            if(!isSuccess){
                throw new BizException("更新提现单异常!");
            }
        }else if(state == ChannelAccountCashoutRecord.CASHOUT_STATE_FAIL){
            isSuccess = cashoutRecordService.updateIng2Fail(recordId, channelErrMsg);
            if(!isSuccess){
                throw new BizException("更新提现单异常!");
            }
        }else {
            isSuccess = cashoutRecordService.updateInit2Ing(recordId, channelRid);
            if(!isSuccess){
                throw new BizException("更新提现单异常!");
            }
        }
    }


    // 校验提现权限
    public CashoutParams checkCashoutJudge(String ifCode, String isvNo, String appId, String cashoutFrom) {

        // 支付接口应支持且开启提现
        PayInterfaceDefine ifDefine = payInterfaceDefineService.getOne(PayInterfaceDefine.gw()
                .eq(PayInterfaceDefine::getIfCode, ifCode)
                .eq(PayInterfaceDefine::getIsSupportCashout, CS.YES)
                .eq(PayInterfaceDefine::getIsOpenCashout, CS.YES)
        );
        if (ifDefine == null) {
            throw new BizException("支付接口：" + ifCode + "，未开启提现");
        }

        // 服务商提现配置
        if (StringUtils.isNotBlank(isvNo)) {
            PayInterfaceConfig isvIfConfig = payInterfaceConfigService.getOne(PayInterfaceConfig.gw()
                    .eq(PayInterfaceConfig::getIfCode, ifCode)
                    .eq(PayInterfaceConfig::getInfoType, CS.SYS_ROLE_TYPE.ISV)
                    .eq(PayInterfaceConfig::getInfoId, isvNo)
            );
            if (isvIfConfig == null || StringUtils.isBlank(isvIfConfig.getCashoutParams())) {
                throw new BizException("服务商：" + isvNo + "，未开启提现");
            }

            // 服务商需开启提现
            CashoutParams isvCashoutParams = JSON.parseObject(isvIfConfig.getCashoutParams(), CashoutParams.class);
            if (isvCashoutParams == null || isvCashoutParams.getIsOpenIsvCashout() == null || isvCashoutParams.getIsOpenIsvCashout() != CS.YES) {
                throw new BizException("服务商：" + isvNo + "，未开启提现");
            }
        }

        // 商户应用提现配置
        PayInterfaceConfig mchAppIfConfig = payInterfaceConfigService.getOne(PayInterfaceConfig.gw()
                .eq(PayInterfaceConfig::getIfCode, ifCode)
                .eq(PayInterfaceConfig::getInfoType, CS.SYS_ROLE_TYPE.MCH_APP)
                .eq(PayInterfaceConfig::getInfoId, appId)
        );
        if (mchAppIfConfig == null || StringUtils.isBlank(mchAppIfConfig.getCashoutParams())) {
            throw new BizException("商户应用：" + appId + "，未开启提现");
        }
        CashoutParams appCashoutParams = JSON.parseObject(mchAppIfConfig.getCashoutParams(), CashoutParams.class);
        if (appCashoutParams == null) {
            throw new BizException("商户应用：" + appId + "，未开启提现");
        }

        // 订单支付成功  自动提现
        if (ChannelAccountCashoutRecord.CASHOUT_FROM_ORDER.equals(cashoutFrom)
            && (appCashoutParams.getIsOpenMchOrderCashout() == null || appCashoutParams.getIsOpenMchOrderCashout() != CS.YES)) {
            throw new BizException("商户应用：" + appId + "，未开启订单支付成功自动提现");
        }

        // 定时任务  自动提现
        if (ChannelAccountCashoutRecord.CASHOUT_FROM_TASK.equals(cashoutFrom)
                && (appCashoutParams.getIsOpenMchTaskCashout() == null || appCashoutParams.getIsOpenMchTaskCashout() != CS.YES)) {
            throw new BizException("商户应用：" + appId + "，未开启定时任务自动提现");
        }

        // 商户应用配置的提现时间区间
        Date nowDate = DateUtil.date();
        String todayDateStr = DateUtil.formatDate(nowDate) + " ";
        if (StringUtils.isNotBlank(appCashoutParams.getStartTime())
                && DateUtil.compare(nowDate, DateUtil.parseDateTime(todayDateStr + appCashoutParams.getStartTime())) < 0) {
            throw new BizException("商户应用：" + appId + "，不在配置的提现开始时间内：" + appCashoutParams.getStartTime());
        }
        if (StringUtils.isNotBlank(appCashoutParams.getEndTime())
                && DateUtil.compare(nowDate, DateUtil.parseDateTime(todayDateStr + appCashoutParams.getEndTime())) > 0) {
            throw new BizException("商户应用：" + appId + "，不在配置的提现结束时间内：" + appCashoutParams.getEndTime());
        }

        return appCashoutParams;
    }

    // 校验提现金额
    public Long checkCashoutAmount(Long amount, CashoutParams appCashoutParams, String appId) {

        if (StringUtils.isNotBlank(appCashoutParams.getMinCashoutAmount()) && AmountUtil.convertDollar2CentLong(appCashoutParams.getMinCashoutAmount()) > amount) {
            throw new BizException(String.format("商户应用：%s，待提现金额：%s(分)，小于商户应用配置的最小提现金额：%s(分)", appId, amount, AmountUtil.convertDollar2CentLong(appCashoutParams.getMinCashoutAmount())));
        }
        if (StringUtils.isNotBlank(appCashoutParams.getMaxCashoutAmount()) && amount > AmountUtil.convertDollar2CentLong(appCashoutParams.getMaxCashoutAmount())) {
            log.info("商户应用：{}，待提现金额：{}(分)，大于商户应用配置的最大提现金额：{}(分)，将以最大金额发起提现", appId, amount, AmountUtil.convertDollar2CentLong(appCashoutParams.getMaxCashoutAmount()));
            return AmountUtil.convertDollar2CentLong(appCashoutParams.getMaxCashoutAmount());
        }
        return amount;
    }

}
