package com.jeequan.jeepay.mgr.ctrl.order;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Pair;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.MchInfo;
import com.jeequan.jeepay.core.entity.SysUser;
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.ApiRes;
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.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.mgr.ctrl.CommonCtrl;
import com.jeequan.jeepay.service.impl.ChannelAccountCashoutRecordService;
import com.jeequan.jeepay.service.impl.PayInterfaceConfigService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/***
* 渠道提现记录管理类
*
* @author zx
* 
* @date 2022/3/23 16:45
*/
@RestController
@RequestMapping("/api/channel/cashouts")
@Slf4j
public class ChannelCashoutRecordController extends CommonCtrl {

    @Autowired private ChannelAccountCashoutRecordService channelAccountCashoutRecordService;
    @Autowired private IConfigContextQueryService configContextQueryService;
    @Autowired private PayInterfaceConfigService payInterfaceConfigService;

    @PreAuthorize("hasAuthority('ENT_MCH_CHANNEL_ACCOUNT')")
    @RequestMapping(value="", method = RequestMethod.GET)
    public ApiRes list() {

        String appId = getValStringRequired("appId");
        String ifCode = getValString("ifCode");
        String channelMchId = getValString("channelMchId");

        // 查询条件：启用状态 && 支持提现 && 开启提现
        List<PayInterfaceDefine> ifDefines = payInterfaceDefineService.list(PayInterfaceDefine.gw()
                .eq(PayInterfaceDefine::getState, CS.YES)
                .eq(PayInterfaceDefine::getIsSupportCashout, CS.YES)
                .eq(PayInterfaceDefine::getIsOpenCashout, CS.YES)
        );
        if (CollUtil.isEmpty(ifDefines)) {
            return ApiRes.page(new Page(getPageIndex(), getPageSize(), 0));
        }
        List<String> ifCodeList = ifDefines.stream().map(PayInterfaceDefine::getIfCode).collect(Collectors.toList());

        IPage<PayInterfaceConfig> payInterfaceConfigIPage = payInterfaceConfigService.page(getIPage(), PayInterfaceConfig.gw()
                .eq(PayInterfaceConfig::getInfoId, appId)
                .eq(PayInterfaceConfig::getInfoType, CS.SYS_ROLE_TYPE.MCH_APP)
                .eq(StringUtils.isNotBlank(ifCode), PayInterfaceConfig::getIfCode, ifCode)
                .eq(PayInterfaceConfig::getState, CS.YES)
                .isNotNull(PayInterfaceConfig::getIfParams)
                .in(PayInterfaceConfig::getIfCode, ifCodeList)
        );
        if (CollUtil.isEmpty(payInterfaceConfigIPage.getRecords())) {
            return ApiRes.page(payInterfaceConfigIPage);
        }

        MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfo(appId);

        Long id = null;
        Pair<String, Long> pair = null;
        for (PayInterfaceConfig payInterfaceConfig : payInterfaceConfigIPage.getRecords()) {
            // 查询结算接口是否存在
            IChannelAccountService channelAccountService =
                    SpringBeansUtil.getBean(JeepayKit.getIfCodeOrigin(payInterfaceConfig.getIfCode()) + "ChannelAccountService", IChannelAccountService.class);

            // 提现接口实现不存在
            if(channelAccountService == null){
                throw new BizException(payInterfaceConfig.getIfCode() + "不支持提现");
            }

            try {
                pair = channelAccountService.queryBalanceAmount(mchAppConfigContext, payInterfaceConfig.getIfCode());
                if (StringUtils.equals(channelMchId, pair.getKey())) {
                    id = payInterfaceConfig.getId();
                }
                payInterfaceConfig.addExt("currentBalance", pair.getValue());
                payInterfaceConfig.addExt("channelMchId", pair.getKey());
            }catch (Exception e) {
                log.error("查询三方账户余额失败，ifCode={}，mchNo={}，appId={}", payInterfaceConfig.getIfCode(), mchAppConfigContext.getMchNo(), appId);
            }
        }

        if (StringUtils.isNotBlank(channelMchId) && id == null) {
            return ApiRes.page(new Page(getPageIndex(), getPageSize(), 0));
        }

        // 根据渠道子商户号搜索的情况
        if (id != null) {
            PayInterfaceConfig config = payInterfaceConfigService.getById(id);
            if (pair != null) {
                config.addExt("currentBalance", pair.getValue());
                config.addExt("channelMchId", pair.getKey());
            }
            Page page = new Page(getPageIndex(), getPageSize(), 1);
            page.setRecords(Collections.singletonList(config));
            return ApiRes.page(page);
        }

        setIfName(payInterfaceConfigIPage.getRecords());

        return ApiRes.page(payInterfaceConfigIPage);
    }

    @PreAuthorize("hasAuthority('ENT_CHANNEL_CASHOUT_LIST')")
    @RequestMapping(value="/records", method = RequestMethod.GET)
    public ApiRes records() {
        ChannelAccountCashoutRecord queryRecord = getObject(ChannelAccountCashoutRecord.class);

        LambdaQueryWrapper<ChannelAccountCashoutRecord> wrapper = ChannelAccountCashoutRecord.gw();
        // 单号
        wrapper.eq(StringUtils.isNotEmpty(queryRecord.getRid()), ChannelAccountCashoutRecord::getRid, queryRecord.getRid());
        // infoId
        wrapper.eq( StringUtils.isNotEmpty(queryRecord.getAppId() ), ChannelAccountCashoutRecord::getAppId, queryRecord.getAppId());
        // 状态查询
        wrapper.eq(queryRecord.getState() != null, ChannelAccountCashoutRecord::getState, queryRecord.getState());
        // 商户号
        wrapper.eq(StringUtils.isNotEmpty(queryRecord.getMchNo()), ChannelAccountCashoutRecord::getMchNo, queryRecord.getMchNo());
        // 支付接口
        wrapper.eq(StringUtils.isNotEmpty(queryRecord.getIfCode()), ChannelAccountCashoutRecord::getIfCode, queryRecord.getIfCode());
        // 渠道子商户号
        wrapper.eq(StringUtils.isNotEmpty(queryRecord.getChannelSubMchId()), ChannelAccountCashoutRecord::getChannelSubMchId, queryRecord.getChannelSubMchId());
        // 时间范围条件
        Date[] dateRange = queryRecord.buildQueryDateRange();
        wrapper.ge(dateRange[0] != null, ChannelAccountCashoutRecord::getCreatedAt, dateRange[0]);
        wrapper.le(dateRange[1] != null, ChannelAccountCashoutRecord::getCreatedAt, dateRange[1]);

        wrapper.orderByDesc(ChannelAccountCashoutRecord::getCreatedAt);

        IPage<ChannelAccountCashoutRecord> pages = channelAccountCashoutRecordService.page(getIPage(), wrapper);

        setIfName(pages.getRecords());

        return ApiRes.page(pages);
    }


    @PreAuthorize("hasAuthority('ENT_CHANNEL_CASHOUT_RECORD_VIEW')")
    @RequestMapping(value="/{rid}", method = RequestMethod.GET)
    public ApiRes detail(@PathVariable("rid") String rid) {
        return ApiRes.ok(channelAccountCashoutRecordService.getById(rid));
    }

    /** 发起提现 **/
    @PreAuthorize("hasAuthority('ENT_MCH_CHANNEL_CASHOUT')")
    @PostMapping
    public ApiRes cashout() {

        String ifCode = getValStringRequired("ifCode");
        String isvNo = getValStringRequired("isvNo");
        String appId = getValStringRequired("appId");
        Long amount = getRequiredAmountL("amount");
        if (amount <= 0) {
            throw new BizException("提现金额应大于0");
        }

        // 查询结算接口是否存在
        IChannelAccountService channelAccountService = SpringBeansUtil.getBean(JeepayKit.getIfCodeOrigin(ifCode) + "ChannelAccountService", IChannelAccountService.class);
        // 提现接口实现不存在

        MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfo(appId);
        MchInfo mchInfo = mchAppConfigContext.getMchInfo();

        // 校验提现权限
        if (!checkCashoutJudge(ifCode, isvNo)) {
            throw new BizException("暂不支持发起提现，请检查是否开启提现");
        }

        Pair<String, Long> pair = channelAccountService.queryBalanceAmount(mchAppConfigContext, ifCode);
        Long currentBalance = pair.getValue();
        if(currentBalance < amount){
            throw new BizException("商户可用余额不足");
        }

        // 提现记录入库
        SysUser sysUser = getCurrentUser().getSysUser();
        ChannelAccountCashoutRecord cashoutRecord = channelAccountCashoutRecordService.initRecord(SeqKit.genCashoutRecordId(), amount, currentBalance, mchAppConfigContext.getMchNo(), mchAppConfigContext.getMchInfo().getMchName(),
                mchAppConfigContext.getAppId(), isvNo, ifCode, pair.getKey(), null, sysUser.getSysUserId(), sysUser.getRealname());

        // 提现
        CashoutRetMsg cashoutRS = channelAccountService.cashout(cashoutRecord, mchAppConfigContext, ifCode);
        processChannelMsg(cashoutRS, cashoutRecord.getRid());

        return ApiRes.ok(channelAccountCashoutRecordService.getById(cashoutRecord.getRid()));
    }

    /** 处理返回的渠道信息，并更新状态 **/
    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 = channelAccountCashoutRecordService.updateInit2Ing(recordId, channelRid);
        if(!isSuccess){
            throw new BizException("更新提现单异常!");
        }

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

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

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

        // 服务商提现配置
        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())) {
                log.info("服务商：{}，未开启提现", isvNo);
                return false;
            }

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

        return true;
    }

}
