package com.clx.performance.service.impl.thirdparty.nbbank;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.clx.performance.param.OwnerLoanPayNotifyMqParam;
import com.clx.performance.component.IdGenerateSnowFlake;
import com.clx.performance.constant.RabbitKeyConstants;
import com.clx.performance.dao.loan.BankTradeDao;
import com.clx.performance.enums.PerformanceResultEnum;
import com.clx.performance.enums.loan.BankTradeEnum;
import com.clx.performance.enums.nbbank.NbBankStatusEnum;
import com.clx.performance.model.loan.BankTrade;
import com.clx.performance.service.thirdparty.nbbank.NbBankService;
import com.clx.performance.service.thirdparty.nbbank.NbBankThirdpartyService;
import com.clx.performance.utils.LocalDateTimeUtils;
import com.clx.performance.vo.pc.nbbank.NbBankOrderPayResultVO;
import com.clx.performance.vo.pc.nbbank.NbBankOrderResultVO;
import com.msl.common.exception.ServiceSystemException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Objects;

/**
 * 易付通
 */
@Slf4j
@Service
public class NbBankServiceImpl implements NbBankService {

    @Autowired
    private BankTradeDao bankTradeDao;

    @Autowired
    private NbBankThirdpartyService bankThirdpartyService;

    @Autowired
    private IdGenerateSnowFlake idGenerateSnowFlake;

    @Autowired
    private RabbitTemplate rabbitTemplate;


    /**
     * 转账支付
     * @param amount 金额分
     */
    @Override
    public NbBankOrderPayResultVO orderTransferPay(Integer amount) {
        String merSeqNo = idGenerateSnowFlake.nextIdToString(3L);

        String merDtTm = LocalDateTimeUtils.formatTime();
        String closeDtTm = LocalDateTimeUtils.formatTime(LocalDateTimeUtils.parseTime(merDtTm).plusDays(30));
        JSONObject data = bankThirdpartyService.directBankTransferOrder(merSeqNo, merDtTm, closeDtTm, amount);

        String transSeqNo = data.getString("transSeqNo");
        String cardNo = data.getString("cardNo");
        String signNo = getSignNo(data.getString("signNo"), cardNo);

        NbBankOrderPayResultVO result = new NbBankOrderPayResultVO();
        result.setAmount(amount);
        result.setMerDtTm(merDtTm);
        result.setMerSeqNo(merSeqNo);
        result.setTransSeqNo(transSeqNo);
        result.setSignNo(signNo);
        result.setCloseDtTm(closeDtTm);

        return result;
    }

    /**
     * 订单支付
     * @param loanNo 借款单号
     * @param amount 金额分
     * @param payAcctOpenBankId 开户行号
     * @param payAcctNo 付款账号
     * @param payAcctNm 付款户名
     */
    @Override
    public NbBankOrderPayResultVO orderDirectPay(Long loanNo, Integer amount,
                                                 String payAcctOpenBankId, String payAcctNo, String payAcctNm) {
        String merSeqNo = idGenerateSnowFlake.nextIdToString(1L);

        String merDtTm = LocalDateTimeUtils.formatTime();
        String closeDtTm = LocalDateTimeUtils.formatTime(LocalDateTimeUtils.parseTime(merDtTm).plusDays(30));
        JSONObject data = bankThirdpartyService.unionPayDirectOrder(merSeqNo, merDtTm, amount,
                payAcctOpenBankId,payAcctNo, payAcctNm);

        String transSeqNo = data.getString("transSeqNo");
        if (StringUtils.isBlank(transSeqNo)) {
            log.info("宁波银行订单支付失败,借款单号{},返回结果:{}", loanNo, data);
            throw new ServiceSystemException(PerformanceResultEnum.NB_BANK_ORDER_PAY_ERROR);
        }
        NbBankOrderPayResultVO result = new NbBankOrderPayResultVO();
        result.setAmount(amount);
        result.setMerDtTm(merDtTm);
        result.setMerSeqNo(merSeqNo);
        result.setTransSeqNo(transSeqNo);
        result.setCloseDtTm(closeDtTm);

        return result;
    }

    /**
     * 获取交易结果
     * @param orgMerSeqNo 商户流水号 （原）
     */
    @Override
    public NbBankOrderResultVO getResult(String orgMerSeqNo) {
        JSONObject data = bankThirdpartyService.queryOrder(orgMerSeqNo);

        NbBankOrderResultVO result = new NbBankOrderResultVO();

        String transStatus = data.getString("transStatus");
        BigDecimal trxAmt = data.getBigDecimal("trxAmt");
        BigDecimal realTrxAmt = data.getBigDecimal("realTrxAmt");
        String transSeqNo = data.getString("transSeqNo");
        String signNo = data.getString("signNo");

        result.setStatus(getStatus(transStatus));
        result.setAmount(trxAmt==null? null : trxAmt.movePointLeft(2).intValue());
        result.setRealAmount(realTrxAmt==null? null : realTrxAmt.movePointLeft(2).intValue());
        result.setMerSeqNo(orgMerSeqNo);
        result.setTransSeqNo(transSeqNo);
        result.setSignNo(signNo);

        return result;
    }

    /**
     * 调账
     * @param orgMerSeqNo 商户流水号 （原）
     * @param amount 金额分
     */
    @Override
    public NbBankOrderResultVO adjust(String orgMerSeqNo, Integer amount) {

        String merDtTm = LocalDateTimeUtils.formatTime();
        JSONObject data = bankThirdpartyService.fundTransferOrder(orgMerSeqNo, merDtTm, amount,
                null, null);

        NbBankOrderResultVO result = new NbBankOrderResultVO();

        String transStatus = data.getString("transStatus");
        String transSeqNo = data.getString("transSeqNo");

        result.setStatus(getStatus(transStatus));
        result.setAmount(amount);
        result.setRealAmount(amount);
        result.setMerDtTm(merDtTm);
        result.setMerSeqNo(orgMerSeqNo);
        result.setTransSeqNo(transSeqNo);

        return result;
    }

    /**
     * 退款
     * @Param type: 1普通退款（忽略） 2银行转账退款（支付成功） 3银联订单支付撤单（未支付）
     * @param orgMerSeqNo 商户流水号（原）
     * @param amount 金额分
     * @param orgTransSeqNo 第三方流水号 (原)
     */
    @Override
    public NbBankOrderResultVO refund(Integer type, String orgMerSeqNo, Integer amount, String orgTransSeqNo) {
        String merSeqNo = idGenerateSnowFlake.nextIdToString(3L);

        String merDtTm = LocalDateTimeUtils.formatTime();
        JSONObject data = bankThirdpartyService.refund(type, merSeqNo, merDtTm, amount,
                orgMerSeqNo, orgTransSeqNo);

        NbBankOrderResultVO result = new NbBankOrderResultVO();

        String transStatus = data.getString("transStatus");
        String transSeqNo = data.getString("transSeqNo");

        result.setStatus(getStatus(transStatus));
        result.setAmount(amount);
        result.setRealAmount(amount);
        result.setMerDtTm(merDtTm);
        result.setMerSeqNo(merSeqNo);
        result.setTransSeqNo(transSeqNo);

        return result;
    }

    /**
     * 银行回调
     * @param merSeqNo 商户流水号
     * @param transSeqNo 第三方流水号
     * @param trxAmt 订单金额分
     * @param realTrxAmt 实收金额分
     * @param transStatus 交易状态
     * @param signNo 转账编码
     */
    @Override
    public void orderPayCallback(String merSeqNo, String transSeqNo,
                                 Integer trxAmt, Integer realTrxAmt,
                                 String transStatus,
                                 String signNo) {
        log.info("宁波银行支付回调, merSeqNo:{}, transSeqNo:{}, trxAmt:{}, realTrxAmt:{}, transSeqNo:{}, signNo:{}",
                merSeqNo, transSeqNo, trxAmt, realTrxAmt, transStatus, signNo);

        NbBankOrderResultVO result = new NbBankOrderResultVO();
        result.setStatus(getStatus(transStatus));

        orderPayCallback(merSeqNo, transSeqNo, trxAmt, realTrxAmt, result.getStatus(), signNo);
    }

    /**
     * 银行回调
     * @param merSeqNo 商户流水号
     * @param transSeqNo 第三方流水号
     * @param trxAmt 订单金额分
     * @param realTrxAmt 实收金额分
     * @param transStatus 交易状态
     * @param signNo 转账编码
     */
    @Override
    public void orderPayCallback(String merSeqNo, String transSeqNo,
                                 Integer trxAmt, Integer realTrxAmt,
                                 Integer transStatus,
                                 String signNo) {
        log.info("宁波银行支付回调, merSeqNo:{}, transSeqNo:{}, trxAmt:{}, realTrxAmt:{}, transSeqNo:{}, signNo:{}",
                merSeqNo, transSeqNo, trxAmt, realTrxAmt, transStatus, signNo);

        BankTrade bankTrade = bankTradeDao.selectByMerchantRunningWaterNo(merSeqNo).orElseThrow(PerformanceResultEnum.DATA_NOT_FIND);
        bankTrade.setStatus(transStatus);

        // 更新状态
        bankTradeDao.updateStatus(bankTrade);
        // 货主借款支付回调
        if (Objects.equals(bankTrade.getOrderType(), BankTradeEnum.OrderType.OWNER_LOAN_RECORD.getCode())){
            OwnerLoanPayNotifyMqParam param = new OwnerLoanPayNotifyMqParam();
            param.setMerSeqNo(merSeqNo);
            param.setPayStatus(transStatus);
            Message message = MessageBuilder.withBody(JSONUtil.parse(param).toString().getBytes()).build();

            //执行解绑自有车辆处理逻辑，将绑定记录正常改为取消
            rabbitTemplate.send(
                    RabbitKeyConstants.OWNER_LOAN_EXCHANGE, RabbitKeyConstants.OWNER_LOAN_PAY_ROUTE_KEY, message
            );

        }

    }

    /**
     * 获取转账编码，默认银行卡后7位
     */
    private String getSignNo(String signNo, String cardNo){
        return StringUtils.isBlank(signNo)? StringUtils.right(cardNo,7) : signNo;
    }

    /**
     * 获取订单状态
     */
    private Integer getStatus(String transStatus){

        if (StringUtils.equals(transStatus, "00")){return NbBankStatusEnum.Status.SUCCESS.getCode();}           //交易成功
        else if (StringUtils.equals(transStatus, "01")){return NbBankStatusEnum.Status.FAIL.getCode();}         //交易失败
        else if (StringUtils.equals(transStatus, "02")){return NbBankStatusEnum.Status.ACCEPT.getCode();}       //交易已受理
        else if (StringUtils.equals(transStatus, "80")){return NbBankStatusEnum.Status.INIT.getCode();}         //订单初始状态
        else if (StringUtils.equals(transStatus, "90")){return NbBankStatusEnum.Status.NOT_FOUND.getCode();}    //交易不存在
        else if (StringUtils.equals(transStatus, "99")){return NbBankStatusEnum.Status.TIMEOUT.getCode();}      //交易超时,须发起交易结果查询

        return NbBankStatusEnum.Status.TEMP.getCode();
    }

}
