package ink.wgink.module.wechat.utils.pay;

import ink.wgink.interfaces.consts.ISystemConstant;
import org.springframework.web.bind.annotation.RequestMethod;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;

/**
 * @ClassName: PaySignAuthorizationUtil
 * @Description: 支付签名和认证工具
 * @Author: wanggeng
 * @Date: 2021/8/18 5:45 下午
 * @Version: 1.0
 */
public class PaySignAuthorizationUtil {

    /**
     * @param requestMethod 请求方法
     * @param url           请求接口
     * @param timestamp     时间戳
     * @param nonceStr      随机字符串
     * @param body          请求主体
     * @return
     */
    public static String getSign(RequestMethod requestMethod, String url, long timestamp, String nonceStr, String body) {
        return new StringBuilder()
                .append(requestMethod.toString())
                .append("\n")
                .append(url)
                .append("\n")
                .append(timestamp)
                .append("\n")
                .append(nonceStr)
                .append("\n")
                .append(body)
                .append("\n")
                .toString();
    }

    /**
     * @param method    方法
     * @param urlSuffix 可通过 WxApiType 来获取，URL挂载参数需要自行拼接
     * @param mchId     商户Id
     * @param serialNo  商户 API 证书序列号
     * @param keyPath   key.pem 证书路径
     * @param body      接口请求参数
     * @param nonceStr  随机字符库
     * @param timestamp 时间戳
     * @param authType  认证类型
     * @return
     * @throws Exception
     */
    public static String buildAuthorization(RequestMethod method, String urlSuffix, String mchId,
                                            String serialNo, String keyPath, String body, String nonceStr,
                                            long timestamp, String authType) throws Exception {
        // 构建签名参数
        String buildSignMessage = getSign(method, urlSuffix, timestamp, nonceStr, body);
        // 获取商户私钥
        String key = PayPrivateKeyUtil.getPrivateKey(keyPath);
        // 生成签名
        String signature = PayPrivateKeyUtil.encryptByPrivateKey(buildSignMessage, key);
        // 根据平台规则生成请求头 authorization
        return getAuthorization(mchId, serialNo, nonceStr, String.valueOf(timestamp), signature, authType);
    }

    /**
     * @param mchId     方法
     * @param serialNo  商户 API 证书序列号
     * @param nonceStr  随机字符库
     * @param timestamp 时间戳
     * @param signature 签名值
     * @param authType  认证类型
     * @return
     */
    private static String getAuthorization(String mchId, String serialNo, String nonceStr, String timestamp, String signature, String authType) throws UnsupportedEncodingException {
        Map<String, String> params = new HashMap<>(5);
        params.put("mchid", mchId);
        params.put("serial_no", serialNo);
        params.put("nonce_str", nonceStr);
        params.put("timestamp", timestamp);
        params.put("signature", signature);
        return authType.concat(" ").concat(createLinkString(params, ",", false, true));
    }

    /**
     * 链接字符串
     *
     * @param params  map
     * @param connStr 链接值
     * @param encode  是否编码
     * @param quotes  是否加引号
     * @return
     */
    private static String createLinkString(Map<String, String> params, String connStr, boolean encode, boolean quotes) throws UnsupportedEncodingException {
        List<String> keys = new ArrayList<>(params.keySet());
        Collections.sort(keys);
        StringBuilder content = new StringBuilder();
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);
            // 拼接时，不包括最后一个&字符
            if (i == keys.size() - 1) {
                if (quotes) {
                    content.append(key).append("=").append('\"').append(encode ? URLEncoder.encode(value, ISystemConstant.CHARSET_UTF8) : value).append('\"');
                } else {
                    content.append(key).append("=").append(encode ? URLEncoder.encode(value, ISystemConstant.CHARSET_UTF8) : value);
                }
            } else {
                if (quotes) {
                    content.append(key).append("=").append('\"').append(encode ? URLEncoder.encode(value, ISystemConstant.CHARSET_UTF8) : value).append('\"').append(connStr);
                } else {
                    content.append(key).append("=").append(encode ? URLEncoder.encode(value, ISystemConstant.CHARSET_UTF8) : value).append(connStr);
                }
            }
        }
        return content.toString();
    }

}
