package com.yiqi.common.config;

import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.yiqi.common.enums.SystemEnv;
import com.yiqi.common.utils.EnumUtils;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.annotation.Bean;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;

/**
 * Created by liuyangkly on 15/6/27.
 */
public class WxPayConfigs {

    public static final String DOMAIN_API = "https://api.mch.weixin.qq.com";

    //app下单
    public static final String PAY_TRANSACTIONS_APP = DOMAIN_API + "/v3/pay/transactions/app";

    //付款码下单
    public static final String PAY_FACE_TO_FACE = DOMAIN_API + "/pay/micropay";

    //小程序支付下单
    public static final String PAY_TRANSACTIONS_WXAPP = DOMAIN_API + "/v3/pay/transactions/jsapi";

    //申请退款
    public static final String REFUND_DOMESTIC_REFUNDS = "/refund/domestic/refunds";

    //关闭订单
    public static final String PAY_TRANSACTIONS_OUT_TRADE_NO = "/pay/transactions/out-trade-no/{}/close";

    private static Log log = LogFactory.getLog(WxPayConfigs.class);
    private static Configuration configs;


    private static String mchSerialNo; //平台证书序列号

    private static String appId;
    //商户id
    private static String mch_ID;

    // API V3密钥
    private static String apiV3Key;

    // 商户API V3私钥证书路径
    private static String privateKeyPath;

    private static String payNotifyUrl;

    private static String refundNotifyUrl;
    
    private static Config config;

    private static Verifier verifier;
    private WxPayConfigs() {
        // No Constructor
    }

    // 根据文件名读取配置文件，文件后缀名必须为.properties
    public synchronized static void init() {
        String filePath;
        switch (EnumUtils.getEnumByType(SystemEnvConfig.env, SystemEnv.class)){
            case LOCAL:
            {
                filePath = "wxpayinfo-local.properties";
                break;
            }
            case DEV:
            {
                filePath = "wxpayinfo-test.properties";
                break;
            }
            case PROD:
            {
                filePath = "wxpayinfo.properties";
                break;
            }
            default:
                return;
        }

        if (configs != null) {
            return;
        }

        try {
            configs = new PropertiesConfiguration(filePath);
        } catch (ConfigurationException e) {
            e.printStackTrace();
        }

        if (configs == null) {
            throw new IllegalStateException("can`t find file by path:" + filePath);
        }

        appId = configs.getString("app_id");
        mchSerialNo = configs.getString("mch_serial_no");
        mch_ID = configs.getString("mch_id");
        apiV3Key = configs.getString("api_v3_key");
        privateKeyPath = configs.getString("private_key_path");
        payNotifyUrl = configs.getString("pay_notify_url");
        refundNotifyUrl = configs.getString("refund_notify_url");

        log.info("配置文件名: " + filePath);
        log.info(description());
    }

    public static String description() {
        StringBuilder sb = new StringBuilder("微信支付Configs{");
        sb.append("微信支付成功回调: ").append(payNotifyUrl).append("\n");
        sb.append(", 微信退款成功回调: ").append(refundNotifyUrl).append("\n");
        sb.append(", app ID: ").append(appId).append("\n");
        sb.append(", 商户ID: ").append(mch_ID).append("\n");
        sb.append(", V3密钥: ").append(apiV3Key).append("\n");
        sb.append(", V3私钥证书路径: ").append(privateKeyPath).append("\n");
        sb.append("}");
        return sb.toString();
    }

    private static String getKeyDescription(String key) {
        int showLength = 6;
        if (StringUtils.isNotEmpty(key) &&
                key.length() > showLength) {
            return new StringBuilder(key.substring(0, showLength))
                    .append("******")
                    .append(key.substring(key.length() - showLength))
                    .toString();
        }
        return null;
    }

    /**
     * 获取商户的私钥文件
     *
     * @param keyPemPath
     * @return
     */
    public static PrivateKey getPrivateKey() {
        String certPath = WxPayConfigs.getPrivateKeyPath();
        File file = new File(certPath);
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return PemUtil.loadPrivateKey(inputStream);
    }

    /**
     * 获取证书管理器实例
     *
     * @return
     */
    public synchronized static Verifier getVerifier() {
        if(verifier == null){
            try {
                log.info("获取证书管理器实例");
                //获取商户私钥
                PrivateKey privateKey = getPrivateKey();

                //私钥签名对象
                PrivateKeySigner privateKeySigner = new PrivateKeySigner(getMchSerialNo(), privateKey);

                //身份认证对象
                WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(getMch_ID(), privateKeySigner);

                // 使用定时更新的签名验证器，不需要传入证书
                CertificatesManager certificatesManager = CertificatesManager.getInstance();
                certificatesManager.putMerchant(getMch_ID(), wechatPay2Credentials, apiV3Key.getBytes(StandardCharsets.UTF_8));
                verifier = certificatesManager.getVerifier(getMch_ID());
            }catch (Exception e){
                e.printStackTrace();
            }
        }

        return verifier;
    }

    public static Configuration getConfigs() {
        return configs;
    }


    public static void setConfigs(Configuration configs) {
        WxPayConfigs.configs = configs;
    }

    public static String getMchSerialNo() {
        return mchSerialNo;
    }

    public static void setMchSerialNo(String mchSerialNo) {
        WxPayConfigs.mchSerialNo = mchSerialNo;
    }

    public static String getMch_ID() {
        return mch_ID;
    }

    public static void setMch_ID(String mch_ID) {
        WxPayConfigs.mch_ID = mch_ID;
    }

    public static String getApiV3Key() {
        return apiV3Key;
    }

    public static void setApiV3Key(String apiV3Key) {
        WxPayConfigs.apiV3Key = apiV3Key;
    }

    public static String getPrivateKeyPath() {
        return privateKeyPath;
    }

    public static void setPrivateKeyPath(String privateKeyPath) {
        WxPayConfigs.privateKeyPath = privateKeyPath;
    }

    public static String getPayNotifyUrl() {
        return payNotifyUrl;
    }

    public static void setPayNotifyUrl(String payNotifyUrl) {
        WxPayConfigs.payNotifyUrl = payNotifyUrl;
    }

    public static String getRefundNotifyUrl() {
        return refundNotifyUrl;
    }

    public static void setRefundNotifyUrl(String refundNotifyUrl) {
        WxPayConfigs.refundNotifyUrl = refundNotifyUrl;
    }

    public static String getAppId() {
        return appId;
    }

    public static void setAppId(String appId) {
        WxPayConfigs.appId = appId;
    }

    public synchronized static Config getConfig() {
        if(config == null){
            config = new RSAAutoCertificateConfig.Builder()
                    .merchantId(WxPayConfigs.getMch_ID())
                    .privateKeyFromPath(WxPayConfigs.getPrivateKeyPath())
                    .merchantSerialNumber(WxPayConfigs.getMchSerialNo())
                    .apiV3Key(WxPayConfigs.getApiV3Key())
                    .build();
        }
        return config;
    }

    public static void setConfig(Config config) {
        WxPayConfigs.config = config;
    }
}

