package com.jeequan.jeepay.thirdparty.channel.dgpay;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.*;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.exception.ChannelException;
import com.jeequan.jeepay.core.model.applyment.DgpayApplymentInfo;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.params.dgpay.DgpayIsvsubMchParams;
import com.jeequan.jeepay.core.model.rqrs.msg.ChannelRetMsg;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.core.utils.DateKit;
import com.jeequan.jeepay.core.utils.JsonKit;
import com.jeequan.jeepay.core.utils.SeqKit;
import com.jeequan.jeepay.thirdparty.channel.AbstractDivisionService;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
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;
import java.util.List;

/**
 * 分账接口
 *
 * @author xiaoyu
 * 
 * @date 2022/8/1 15:39
 */
@Slf4j
@Service
public class DgpayDivisionService extends AbstractDivisionService {

    @Autowired private ConfigContextQueryService configContextQueryService;
    @Autowired private DgpayMchApplymentService dgpayMchApplymentService;

    @Override
    public String getIfCode() {
        return CS.IF_CODE.DGPAY;
    }

    @Override
    public boolean isSupport() {
        return false;
    }

    @Override
    public ChannelRetMsg bind(MchDivisionReceiver mchDivisionReceiver, MchAppConfigContext mchAppConfigContext) {

        try {

            String logPrefix = "[斗拱添加分账]";
            // 新创建的分账接收者的mchId
            String newDivisionReceiverSubMchId = null;

            // 实际使用ifCode，用于查询支付参数
            String ifCode = mchDivisionReceiver.getIfCode();

            // 当channelExtInfo 传入： {} ， 说明是subMchId直接绑定模式
            JSONObject channelExtInfoJOSN = JSON.parseObject(mchDivisionReceiver.getChannelExtInfo());
            if(channelExtInfoJOSN.isEmpty()){
                throw new BizException("分账配置信息错误");
            }
            newDivisionReceiverSubMchId = mchDivisionReceiver.getAccNo();

            Boolean isHasAccount = (Boolean) channelExtInfoJOSN.getOrDefault("isHasAccount", false);

            // 新增的分账接收方：调起进件接口，并需要返回一个 subMchId
            if(!isHasAccount){
                // 1.基本信息开户
                MchApplyment mchApplyment = new MchApplyment(); // 构造进件相关请求参数
                mchApplyment.setApplyId(SeqKit.genMchApplyNo());
                mchApplyment.setIsvNo(mchAppConfigContext.getMchApplyment().getIsvNo());
                mchApplyment.setApplyDetailInfo(mchDivisionReceiver.getChannelExtInfo());
                mchApplyment.setIfCode(ifCode);
                // 进件参数
                JSONObject reqParams = dgpayMchApplymentService.getApplymentParams(mchApplyment);
                // 开户参数
                dgpayMchApplymentService.setAccountInfo(mchApplyment, reqParams);

                // 银行卡参数
                JSONObject cardInfo = reqParams.getJSONObject("card_info");
                reqParams.remove("card_info");

                DgpayApplymentInfo applymentInfo = JSONObject.parseObject(mchApplyment.getApplyDetailInfo(), DgpayApplymentInfo.class);

                JSONArray fileArr = new JSONArray();
                // 门头照
                String storeOuterImg = DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F49", applymentInfo.getStoreOuterImg(), ifCode);
                if (StringUtils.isNotEmpty(storeOuterImg)) {
                    JSONObject json = JsonKit.newJson("file_type", "F49");
                    json.put("file_id", storeOuterImg);
                    fileArr.add(json);
                }
                // 内景照
                String storeInnerImg = DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F50", applymentInfo.getStoreInnerImg(), ifCode);
                if (StringUtils.isNotEmpty(storeInnerImg)) {
                    JSONObject json = JsonKit.newJson("file_type", "F50");
                    json.put("file_id", storeInnerImg);
                    fileArr.add(json);
                }
                // 收银台照
                String storeCashierImg = DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F105", applymentInfo.getStoreCashierImg(), ifCode);
                if (StringUtils.isNotEmpty(storeCashierImg)) {
                    JSONObject json = JsonKit.newJson("file_type", "F105");
                    json.put("file_id", storeCashierImg);
                    fileArr.add(json);
                }
                // 法人身份证人像面照片
                String idcard1Img = DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F02", applymentInfo.getIdcard1Img(), ifCode);
                if (StringUtils.isNotEmpty(idcard1Img)) {
                    JSONObject json = JsonKit.newJson("file_type", "F02");
                    json.put("file_id", idcard1Img);
                    fileArr.add(json);
                }
                // 法人身份证国徽面照片
                String idcard2Img = DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F03", applymentInfo.getIdcard2Img(), ifCode);
                if (StringUtils.isNotEmpty(idcard2Img)) {
                    JSONObject json = JsonKit.newJson("file_type", "F03");
                    json.put("file_id", idcard2Img);
                    fileArr.add(json);
                }
                // 营业执照
                String licenseImg = DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F07", applymentInfo.getLicenseImg(), ifCode);
                if (StringUtils.isNotEmpty(licenseImg)) {
                    JSONObject json = JsonKit.newJson("file_type", "F07");
                    json.put("file_id", licenseImg);
                    fileArr.add(json);
                }
                // 商务协议
                String baPicImg = DgPayKit.uploadFile(mchApplyment.getIsvNo(), "F116", applymentInfo.getBankPic(), ifCode);
                if (StringUtils.isNotEmpty(baPicImg)) {
                    JSONObject json = JsonKit.newJson("file_type", "F116");
                    json.put("file_id", baPicImg);
                    fileArr.add(json);
                }

                reqParams.put("file_list", fileArr.toJSONString());

                // 步骤1 请求地址 企业开户和个人开户地址
                String step1Url = applymentInfo.getMerchantType() == 1?"/user/basicdata/indv":"/user/basicdata/ent";
                // 发起开户请求
                JSONObject responseStep1 = DgPayKit.request(mchAppConfigContext, reqParams, step1Url, ifCode);
                JSONObject dataJson1 = responseStep1.getJSONObject("data");
                // 响应失败
                if (!DgPayKit.STATE_SUCCESS.equals(dataJson1.getString("resp_code"))) {
                    ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
                    channelRetMsg.setChannelErrCode(dataJson1.getString("resp_code"));
                    channelRetMsg.setChannelErrMsg(dataJson1.getString("resp_desc"));
                    return channelRetMsg;
                }
                // 获取汇付ID号
                String huifuId = dataJson1.getString("huifu_id");

                // 2.业务入驻
                DgpayIsvsubMchParams mchParams = (DgpayIsvsubMchParams)configContextQueryService.queryIsvsubMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), ifCode);
                // 汇付ID
                reqParams.put("huifu_id", huifuId);
                // 请求流水号
                reqParams.put("req_seq_id", SeqKit.genMchApplyNo());
                // 商户编号
                reqParams.put("upper_huifu_id", mchParams.getHuifuId());
                // 延迟入账开关
                reqParams.put("delay_flag", "Y");
                // 银行卡参数
                reqParams.put("card_info", cardInfo);
                reqParams.remove("noMchId");
                // 步骤2 请求地址 用户业务入驻
                String step2Url = "/user/busi/open";
                // 发起开户请求
                JSONObject responseStep2 = DgPayKit.request(mchAppConfigContext, reqParams, step2Url, ifCode);
                JSONObject dataJson2 = responseStep2.getJSONObject("data");
                // 响应失败
                if (!DgPayKit.STATE_SUCCESS.equals(dataJson2.getString("resp_code"))) {
                    ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
                    // 状态失败
                    DgPayKit.channelMsgError(dataJson2, channelRetMsg);
                    return channelRetMsg;
                }
                JSONObject paramsJson = new JSONObject();
                String tokenNo = dataJson2.getString("token_no");
                paramsJson.put("tokenNo", tokenNo);
                paramsJson.put("huifuId", huifuId);
                newDivisionReceiverSubMchId = paramsJson.toJSONString();
            }else {
                JSONObject paramsJson = JsonKit.newJson("huifuId", newDivisionReceiverSubMchId);
                // 取现卡序列号
                if (StringUtils.isNotEmpty(mchDivisionReceiver.getChannelExtInfo())) {
                    String tokenNo = channelExtInfoJOSN.getString("tokenNo");
                    paramsJson.put("tokenNo", tokenNo);
                }

                newDivisionReceiverSubMchId = paramsJson.toJSONString();
            }

            //在channelAttach数据中，写入 渠道用户编号
            return ChannelRetMsg.confirmSuccess(null).setChannelAttach(newDivisionReceiverSubMchId);
        } catch (ChannelException e) {

            ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
            channelRetMsg.setChannelErrCode(e.getChannelRetMsg().getChannelErrCode());
            channelRetMsg.setChannelErrMsg(e.getChannelRetMsg().getChannelErrMsg());
            return channelRetMsg;

        } catch (Exception e) {
            log.error("添加斗拱分账异常", e);
            ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
            channelRetMsg.setChannelErrMsg(e.getMessage());
            return channelRetMsg;
        }
    }

    @Override
    public ChannelRetMsg singleDivision(PayOrder payOrder, List<PayOrderDivisionRecord> recordList, MchAppConfigContext mchAppConfigContext) {

        try {
            // 分账批次号
            String batchOrderId = recordList.get(0).getBatchOrderId();

            if(recordList.isEmpty()){ // 当无分账用户时，直接响应成功即可。
                return ChannelRetMsg.confirmSuccess(null);
            }

            String logPrefix = "【斗拱分账】";

            DgpayIsvsubMchParams isvsubMchParams = (DgpayIsvsubMchParams) configContextQueryService.queryIsvsubMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), payOrder.getIfCode());
            String mchId = isvsubMchParams.getHuifuId();

            JSONObject reqParams = new JSONObject();
            // 请求日期
            reqParams.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
            // 请求流水号
            reqParams.put("req_seq_id", batchOrderId);
            // 商户号
            reqParams.put("huifu_id", mchId);

            String orgReqSeqId = payOrder.getPayOrderId();
            if (StringUtils.equals(CS.PAY_WAY_CODE.AUTO_POS, payOrder.getWayCode())) {
                orgReqSeqId = payOrder.getChannelOrderNo();

                if (payOrder.getChannelBizData() != null && StringUtils.isNotBlank(payOrder.getChannelBizData())) {
                    JSONObject channelBizData = JSONObject.parseObject(payOrder.getChannelBizData());

                    if (StringUtils.isNotBlank(channelBizData.getString("reqSeqId"))) {
                        orgReqSeqId = channelBizData.getString("reqSeqId");
                    }
                }
            }
            // 原交易请求流水号
            reqParams.put("org_req_seq_id",  orgReqSeqId);
            // 下单日期
            reqParams.put("org_req_date", DateUtil.format(payOrder.getSuccessTime(), DatePattern.PURE_DATE_PATTERN));

            JSONArray receivers = new JSONArray();
            for (int i = 0; i < recordList.size(); i++) {
                PayOrderDivisionRecord record = recordList.get(i);
                if(record.getCalDivisionAmount() <= 0){ //金额为 0 不参与分账处理
                    continue;
                }
                if(StringUtils.isEmpty(record.getChannelAccNo())){ // 判断是否都包含渠道号码
                    throw new BizException(record.getAccName() + "账号没有分账渠道号， 本次分账失败");
                }

                JSONObject paramsJson = JSONObject.parseObject(record.getChannelAccNo());
                JSONObject receiver = new JSONObject();
                receiver.put("huifu_id", paramsJson.getString("huifuId"));
                receiver.put("div_amt", AmountUtil.convertCent2Dollar(record.getCalDivisionAmount()));
                receivers.add(receiver);
            }

            if(receivers.isEmpty()){
                return ChannelRetMsg.confirmSuccess(null);
            }

            // 分账对象
            JSONObject receiverJson = new JSONObject();
            receiverJson.put("acct_infos", receivers);
            reqParams.put("acct_split_bunch", receiverJson.toJSONString());

            log.info("{}分账处理分账接收方", logPrefix);
            // 发起分账请求
            JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, "/trade/payment/delaytrans/confirm", payOrder.getIfCode());
            JSONObject dataJson = response.getJSONObject("data");
            // 响应失败
            if (!DgPayKit.STATE_SUCCESS.equals(dataJson.getString("resp_code"))) {
                ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
                // 状态失败
                DgPayKit.channelMsgError(dataJson, channelRetMsg);
                return channelRetMsg;
            }

            // 业务处理成功, 响应成功 交易状态为成功
            if(DgPayKit.STATE_SUCCESS.equals(dataJson.getString("resp_code")) && "S".equals(dataJson.getString("trans_stat"))) {

                return ChannelRetMsg.confirmSuccess(dataJson.getString("req_seq_id"));
            }

            // 订单处理中： 需要延迟到第二天5点进行结算
            if("P".equals(dataJson.getString("trans_stat"))){

                // 第二天的5点钟
                Date nextDay5 = DateUtil.offset(DateKit.getBegin(DateUtil.offsetDay(new Date(), 1)), DateField.HOUR, 5);

                // 触发MQ的时间， 单位： 秒
                Long delayTime = (nextDay5.getTime() - DateKit.currentTimeMillis()) / 1000;

                return ChannelRetMsg.unknown().setChannelAttach(JsonKit.newJson("mqDelayTime", delayTime).toJSONString());

            }else{ //其他情况认为： 失败
                return ChannelRetMsg.confirmFail().setChannelErrCode(dataJson.getString("resp_code")).setChannelErrMsg(dataJson.getString("resp_desc"));
            }

        } catch (ChannelException e) {

            ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
            channelRetMsg.setChannelErrCode(e.getChannelRetMsg().getChannelErrCode());
            channelRetMsg.setChannelErrMsg(e.getChannelRetMsg().getChannelErrMsg());
            return channelRetMsg;

        } catch (Exception e) {
            log.error("斗拱分账异常", e);
            ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
            channelRetMsg.setChannelErrMsg(e.getMessage());
            return channelRetMsg;
        }
    }

    @Override
    public ChannelRetMsg divisionRefund(PayOrderDivisionRecord payOrderDivisionRecord, PayOrderDivisionRefundRecord payOrderDivisionRefundRecord, RefundOrder refundOrder, PayOrder payOrder, MchAppConfigContext mchAppConfigContext) {
        String logPrefix = "【斗拱分账回退】";

        try {
            // 防止接口限流
            Thread.sleep(1000);

            DgpayIsvsubMchParams isvsubMchParams = (DgpayIsvsubMchParams) configContextQueryService.queryIsvsubMchParams(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), payOrderDivisionRecord.getIfCode());

            JSONObject reqParams = new JSONObject();
            // 请求日期
            reqParams.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
            // 请求流水号
            reqParams.put("req_seq_id", payOrderDivisionRefundRecord.getDivisionRefundId());
            // 商户号
            reqParams.put("huifu_id", isvsubMchParams.getHuifuId());
            // 原交易请求日期
            reqParams.put("org_req_date", DateUtil.format(payOrderDivisionRecord.getCreatedAt(), DatePattern.PURE_DATE_PATTERN));
            // 原交易请求流水号
            reqParams.put("org_req_seq_id", payOrderDivisionRecord.getChannelBatchOrderId());
            JSONObject paramsJson = JSONObject.parseObject(payOrderDivisionRecord.getChannelAccNo());
            JSONArray refundArr = new JSONArray();
            JSONObject refundJson = new JSONObject();
            refundJson.put("huifu_id", paramsJson.getString("huifuId"));
            refundJson.put("div_amt", AmountUtil.convertCent2Dollar(payOrderDivisionRefundRecord.getDivisionRefundAmount()));
            refundArr.add(refundJson);

            JSONObject receiverJson = new JSONObject();
            receiverJson.put("acct_infos", refundArr);
            reqParams.put("acct_split_bunch", receiverJson.toJSONString());

            log.info(logPrefix);
            JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, "/trade/payment/delaytrans/confirmrefund", payOrderDivisionRecord.getIfCode());
            JSONObject jsonData = response.getJSONObject("data");
            // 响应失败
            if (!DgPayKit.STATE_SUCCESS.equals(jsonData.getString("resp_code"))) {
                ChannelRetMsg channelRetMsg = ChannelRetMsg.confirmFail();
                // 状态失败
                DgPayKit.channelMsgError(jsonData, channelRetMsg);
                return channelRetMsg;
            }
            return ChannelRetMsg.confirmSuccess(jsonData.getString("hf_seq_id"));

        } catch (Exception e) {
            throw new BizException(e.getMessage());
        }
    }

    @Override
    public Long queryBalanceAmount(MchDivisionReceiver mchDivisionReceiver, MchAppConfigContext mchAppConfigContext) {
        String logPrefix = "【斗拱分账余额查询】";
        try {
            JSONObject paramsJson = JSONObject.parseObject(mchDivisionReceiver.getChannelAccNo());

            JSONObject reqParams = new JSONObject();
            // 请求日期
            reqParams.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
            // 请求流水号
            reqParams.put("req_seq_id", DateKit.currentTimeMillis());
            // 商户号
            reqParams.put("huifu_id", paramsJson.getString("huifuId"));

            log.info(logPrefix);
            JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, "/trade/acctpayment/balance/query", mchDivisionReceiver.getIfCode());
            JSONObject jsonData = response.getJSONObject("data");

            Long balanceAmt = 0L;
            // 响应成功
            if (DgPayKit.STATE_SUCCESS.equals(jsonData.getString("resp_code"))) {
                // 账户信息列表
                JSONArray infoList = jsonData.getJSONArray("acctInfo_list");
                for (int i = 0; i < infoList.size(); i++) {
                    JSONObject infoJson = JSONObject.parseObject(infoList.get(i).toString());
                    // 查询基本账户类型
                    if ("01".equals(infoJson.getString("acct_type"))) {
                        balanceAmt = Long.parseLong(AmountUtil.convertDollar2Cent(infoJson.getString("balance_amt")));
                    }
                }
            }
            return balanceAmt;
        } catch (Exception e) {
            throw new BizException(e.getMessage());
        }
    }

    @Override
    public ChannelRetMsg cashout(MchDivisionReceiver mchDivisionReceiver, Long amount, MchAppConfigContext mchAppConfigContext) {
        String logPrefix = "斗拱分账账号提现";
        try {
            Long thisBalanceAmount = queryBalanceAmount(mchDivisionReceiver, mchAppConfigContext);
            if(thisBalanceAmount < amount){
                throw new BizException("商户可用余额不足");
            }
            JSONObject paramsJson = JSONObject.parseObject(mchDivisionReceiver.getChannelAccNo());

            JSONObject reqParams = new JSONObject();
            // 请求流水号
            reqParams.put("req_seq_id", DateKit.currentTimeMillis());
            // 请求日期
            reqParams.put("req_date", DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN));
            // 商户号
            reqParams.put("huifu_id", paramsJson.getString("huifuId"));

            // 取现卡序列号
            String tokenNo = paramsJson.getString("tokenNo");

            if (StringUtils.isEmpty(tokenNo)) {
                throw new BizException("取现卡序列号不能为空");
            }

            // 请求流水号
            reqParams.put("req_seq_id", DateKit.currentTimeMillis() +"_"+ 1);
            // 到账类型
            reqParams.put("into_acct_date_type", "T0");
            // 取现卡序列号
            reqParams.put("token_no", tokenNo);
            // 取现金额
            reqParams.put("cash_amt", AmountUtil.convertCent2Dollar(amount));

            log.info(logPrefix);
            JSONObject response = DgPayKit.request(mchAppConfigContext, reqParams, "/trade/settlement/enchashment", mchDivisionReceiver.getIfCode());
            JSONObject jsonData = response.getJSONObject("data");

            // 响应成功
            if (DgPayKit.STATE_SUCCESS.equals(jsonData.getString("resp_code"))) {
                // 交易状态
                String transStat = jsonData.getString("trans_stat");
                if (DgPayKit.STATE_ING_P.equals(transStat)) {
                    String channelNo = jsonData.getString("hf_seq_id");
                    return ChannelRetMsg.confirmSuccess(channelNo);
                }else if (DgPayKit.STATE_FAIL_F.equals(transStat)) {
                    return ChannelRetMsg.confirmFail(jsonData.getString("resp_code"), jsonData.getString("resp_desc"));
                }
            }
            return ChannelRetMsg.waiting();
        } catch (Exception e) {
            throw new BizException(e.getMessage());
        }
    }

}
