package com.yn.bftl.thirdparty.modules.chinaums.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yn.bftl.common.common.exception.YnceError;
import com.yn.bftl.common.common.util.RSAEncryptorUtil;
import com.yn.bftl.common.modules.configuration.entity.ThirdPartyPaySet;
import com.yn.bftl.thirdparty.common.exception.YnceErrorException;
import com.yn.bftl.thirdparty.common.util.ByxHttpClientUtil;
import com.yn.bftl.thirdparty.common.util.CryptoUtils;
import com.yn.bftl.thirdparty.modules.business.util.ThirdPartyConfigUtil;
import com.yn.bftl.thirdparty.modules.log.service.LoggerService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.time.LocalDateTime;
import java.util.*;

@Component
@Slf4j
public class ByxChinaumsUtil {
    /**
     * 银联接口域名
     */
    private static String DOMAIN_NAME = null;

    /**
     * 银联公钥
     */
    private static String PUBLIC_KEY = null;

    /**
     * 银联私钥
     */
    private static String PRIVATE_KEY = null;

    /**
     * 交易证书
     */
    private static String TRANSACTION_PRIVATE_KEY = null;

    /**
     * 银联商户ID
     */
    private static String MERCHANT_ID = null;

    /**
     * 银联银行编码
     */
    private static String INS_ID_CD = null;

    /**
     * 银联费用电子账簿ID
     */
    private static String CHINAUMS_FEE_BALANCE_ACCT_ID = null;

    /**
     * 配置更新时间
     */
    public static LocalDateTime updatedOn;

    /**
     * 默认支付密码
     */
    private static final String DEFAULT_PASSWORD = "123456";

    @Resource
    private ThirdPartyConfigUtil thirdPartyConfigUtil;
    @Resource
    private LoggerService loggerService;
    @Value("${spring.profiles.active}")
    private String active;

    /** appId (请替换成实际appId) */
    private final static String APP_ID = "5ed70f9c2c07873f0aa9e455560bc553";

    /** 签名算法 */
    private final static String SIGN_ALG = "SHA1withRSA";

    private PrivateKey privateKey = null;

    private PublicKey publicKey = null;

    private PrivateKey transactionPrivateKey = null;

    private final static String MCT_WALLET_TRANS_CERT_PWD = "LianTiao135!";

    private final static String MCT_WALLET_CERT_PWD = "123456";

    /**
     * 配置初始化
     *
     * @return 第三方支付配置
     * @description
     * @author huabiao
     * @create 2022/9/1  15:29
     */
    public void configInit() {
        ThirdPartyPaySet thirdPartyPaySet = thirdPartyConfigUtil.getPlatform();
        if (thirdPartyPaySet == null) {
            throw new YnceErrorException(YnceError.YNCE_218001, "未配置参数");
        }
        if (StringUtils.isBlank(thirdPartyPaySet.getChinaumsDomainName())
                || StringUtils.isBlank(thirdPartyPaySet.getChinaumsPublicKey())
                || StringUtils.isBlank(thirdPartyPaySet.getChinaumsPrivateKey())
                || StringUtils.isBlank(thirdPartyPaySet.getChinaumsInsIdCd())
                || StringUtils.isBlank(thirdPartyPaySet.getChinaumsMerchantId())
                || StringUtils.isBlank(thirdPartyPaySet.getChinaumsFeeBalanceAcctId())) {
            throw new YnceErrorException(YnceError.YNCE_218001, "未配置银联参数");
        }
        if (thirdPartyPaySet.getUpdatedOn().equals(updatedOn)) {
            return;
        }
        // 更新第三方配置
        updatedOn = thirdPartyPaySet.getUpdatedOn();
        DOMAIN_NAME = thirdPartyPaySet.getChinaumsDomainName();
        PUBLIC_KEY = thirdPartyPaySet.getChinaumsPublicKey();
        PRIVATE_KEY = thirdPartyPaySet.getChinaumsPrivateKey();
        TRANSACTION_PRIVATE_KEY = thirdPartyPaySet.getChinaumsTransactionPrivateKey();

        try {
            publicKey = CryptoUtils.loadPublicKeyFromBase64(PUBLIC_KEY);
            privateKey = CryptoUtils.initPrivateKeyFromByteArray(Base64.getDecoder().decode(PRIVATE_KEY.getBytes()), MCT_WALLET_CERT_PWD);
            transactionPrivateKey = CryptoUtils.initPrivateKeyFromByteArray(Base64.getDecoder().decode(TRANSACTION_PRIVATE_KEY.getBytes()), MCT_WALLET_TRANS_CERT_PWD);
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new RuntimeException(e);
        }
        MERCHANT_ID = thirdPartyPaySet.getChinaumsMerchantId();
        INS_ID_CD = thirdPartyPaySet.getChinaumsInsIdCd();
        CHINAUMS_FEE_BALANCE_ACCT_ID = thirdPartyPaySet.getChinaumsFeeBalanceAcctId();
    }

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public <T> T execute(String method, JSONObject requestJson, Class<T> voClass) {
        return this.execute(method, requestJson, voClass, false);
    }

    /**
     * @param method:请求方法名
     * @param requestJson:请求参数
     * @param voClass          响应体类型
     * @return ChinaumsResponseVO
     * @author huabiao
     * @create 2022/9/6  11:22
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public <T> T execute(String method, JSONObject requestJson, Class<T> voClass, boolean isH5Api) {
        if (voClass == null) {
            return null;
        }
        String requestBody = null;
        configInit();
        log.info("发送请求至银联接口：'" + method + "', 参数->" + requestJson.toJSONString());
        try {
            // 清除null数据
            Iterator<Map.Entry<String, Object>> it = requestJson.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry<String, Object> next = it.next();
                if (next.getValue() == null || StringUtils.isBlank(next.getValue().toString())) {
                    it.remove();
                }
            }
            String params = "";
            if (isH5Api) {
                params = requestJson.toJSONString();
            } else {
                params = this.initBizContent(requestJson);
            }
            requestBody = buildRequestBodyAndSign(method, params);
            // 调用请求接口
            JSONObject jsonObject = ByxHttpClientUtil.getInstance(SIGN_ALG, publicKey).sendPost(DOMAIN_NAME, requestBody);//.setHeaderMap(buildRequestHead(requestJson)).sendPost(DOMAIN_NAME + url, mainJsonObject);
//            if (active.equals("test") || active.equals("local")) {
                log.info("银联请求:" + method + ", 返回数据：" + jsonObject.toJSONString());
//            }
//            log.info("银联请求:" + method + ", 返回数据：" + jsonObject.toJSONString());
//            T result = jsonObject.toJavaObject(voClass);
//            if (result instanceof JSONObject) {
//            JSONObject resultJsonObject = (JSONObject) result;
            String rspCode = jsonObject.get("rspCode").toString();
            loggerService.unionpay(jsonObject.toJSONString(), requestBody, ByxChinaumsUtil.class.getName(), method, "chinaums");
            if (rspCode.equals("00000") || rspCode.equals("10004")) {
                if (voClass.getName().equals(JSONObject.class.getName())) {
                    if (isH5Api) {
                        return (T) jsonObject;
                    } else {
                        return (T) jsonObject.getJSONObject("msgBody");
                    }
                } else {
                    if (isH5Api) {
                        return jsonObject.toJavaObject(voClass);
                    } else {
                        return jsonObject.getJSONObject("msgBody").toJavaObject(voClass);
                    }

                }
            }
            else {
                throw new RuntimeException(jsonObject.get("rspResult").toString());
            }
//            ByxBaseVO baseVO = (ByxBaseVO) result;
//            if (baseVO.getRspCode().equals("00000")) {
//                loggerService.unionpay(jsonObject.toJSONString(), requestBody, ByxChinaumsUtil.class.getName(), method, "chinaums");
//                if (voClass == null) {
//                    return null;
//                } else {
//                    return result;
//                }
//            } else {
//                throw new RuntimeException(baseVO.getRspMsg());
//            }
        } catch (final Exception e) {
            loggerService.unionpay(e.getMessage(), requestBody, ByxChinaumsUtil.class.getName(), method, "chinaums");
            throw new YnceErrorException(YnceError.YNCE_218001, e.getMessage());
        }
    }

    private String initBizContent(JSONObject requestJson) throws UnsupportedEncodingException {
        Map<String, String> packParams = new HashMap<>();
        packParams.put("reqSn", UUID.randomUUID().toString().replace("-",""));
        packParams.put("sndDate", DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
        packParams.put("msgBody", requestJson.toJSONString());
        return URLEncoder.encode(JSON.toJSONString(packParams), "UTF-8");
    }

    private String buildRequestBodyAndSign(String method, String bizContent) {
        return sign(buildRequestBody(method, bizContent));
    }

    private String buildRequestBody(String method, String bizContent) {
        String timestamp = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
        return "app_id=" + APP_ID +
                "&timestamp=" + timestamp +
                "&method=" + method +
                "&v=1.0.1" +
                "&biz_content=" + bizContent +
                "&sign_alg=0";
    }

    private String sign(String requestBody) {
        String signature = CryptoUtils.sign(SIGN_ALG, requestBody, privateKey, "UTF-8");
        return requestBody + "&sign=" + signature;
    }

    /**
     * 拼接请求报文头
     *
     * @param jsonObject:请求参数集合
     * @return 请求体
     * @author huabiao
     * @create 2022/9/7  10:20
     */
    private static Map<String, String> buildRequestHead(JSONObject jsonObject) throws Exception {
        Map<String, String> headerMap = new HashMap<>();
        headerMap.put("Accept", "application/json");
        headerMap.put("X-OPEN-APP-ID", MERCHANT_ID);
        // 获取请求时间
        String timestamp = String.valueOf(System.currentTimeMillis());
        headerMap.put("X-OPEN-TS", timestamp);
        // 待签名串
        StringBuilder toBeSignStr = new StringBuilder();
        toBeSignStr.append("body=[")
                .append(jsonObject)
                .append("]&X-OPEN-TS=[")
                .append(timestamp)
                .append("]");
        headerMap.put("X-OPEN-SIGN", RSAEncryptorUtil.sign(toBeSignStr.toString(), RSAEncryptorUtil.getPrivateKey(PRIVATE_KEY)));
        return headerMap;
    }

    /**
     * 获取银联公钥
     *
     * @return 银联公钥
     * @author huabiao
     * @create 2022/12/6  17:09
     */
    public String getPublicKey() {
        configInit();
        return PUBLIC_KEY;
    }

    /**
     * 获取平台分账银联电子账簿ID
     * @param companyId 企业ID
     * @return 分账电子账簿ID
     * @description
     * @author huabiao
     * @create 2022/12/12  16:46
     */
    public String getBalanceAcctId(Long companyId) {
        ThirdPartyPaySet thirdPartyPaySet = thirdPartyConfigUtil.getCompany(companyId);
        return thirdPartyPaySet != null ? thirdPartyPaySet.getChinaumsBalanceAcctId() : null;
    }

    /**
     * 获取平台费用银联电子账簿ID
     *
     * @return 费用电子账簿ID
     * @description
     * @author huabiao
     * @create 2022/12/12  16:46
     */
    public String getFeeBalanceAcctId() {
        configInit();
        return CHINAUMS_FEE_BALANCE_ACCT_ID;
    }

    /**
     * 获取企业授信还款电子账簿ID
     *
     * @param companyId 企业ID
     * @return 企业授信还款电子账簿ID
     * @description
     * @author huabiao
     * @create 2023/7/8  17:20
     */
    public String getChinaumsCreditBalanceAcctId(Long companyId) {
        ThirdPartyPaySet thirdPartyPaySet = thirdPartyConfigUtil.getCompany(companyId);
        return thirdPartyPaySet != null ? thirdPartyPaySet.getChinaumsCreditBalanceAcctId() : null;
    }


    public String getDefaultPassword() {
        return DEFAULT_PASSWORD;
    }

    public PrivateKey getPrivateKey() {
        return privateKey;
    }

    public PrivateKey getTransactionPrivateKey() {
        return transactionPrivateKey;
    }
}
