package com.fkzy.warn.common.util;

import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Iterator;
import java.util.Objects;
import java.util.TreeMap;

/**
 * MD5工具类
 */
@Slf4j
public class MD5Util {

    /**
     * 生成签名串
     *
     * @param params 升序的参数
     * @param slat   加盐：应用系统的apiKey，从控制台应用系统中获取
     * @return 16进制签名串
     */
    public static String md5Sign(String params, String slat) {
        return digestToHex(params + slat);
    }

    /**
     * 验签
     *
     * @param params 升序的参数
     * @param sign   签名串
     * @param slat   加盐：应用系统的apiKey，从控制台应用系统中获取
     * @return true 表示验签通过
     */
    public static boolean checkSign(String params, String sign, String slat) {
        try {
            String signData = digestToHex(params + slat);
            log.info("生成的签名：{}", signData);
            return StringUtils.equals(signData, sign);
        } catch (Exception e) {
            log.error("验签异常:", e);
            return false;
        }
    }

    /**
     * json参数排序 <br/>
     * <p>
     * post请求为json传参时，调用该方法，按json中的key升序排列
     * </p>
     * @param obj json参数
     * @return key升序的json字符串
     */
    public static String sortJsonToStr(Object obj) {
        if (Objects.isNull(obj) || "".equals(obj)) {
            return "";
        }
        if (obj instanceof Integer
            || obj instanceof Short
            || obj instanceof Long
            || obj instanceof Float
            || obj instanceof Double
            || obj instanceof Boolean
            || obj instanceof Character
            || obj instanceof Byte) {
            return String.valueOf(obj);
        }
        if (obj instanceof String) {
            return JSONObject.toJSONString(JSONObject.parseObject(String.valueOf(obj)), JSONWriter.Feature.MapSortField);
        }
        try {
            return JSONObject.toJSONString(obj, JSONWriter.Feature.MapSortField);
        } catch (Exception e) {
            return String.valueOf(obj);
        }
    }

    /**
     * url参数排序
     * <br/>
     * 该方法适用范围：
     * <ul>
     *     <li>get请求：url参数传参</li>
     *     <li>post请求：form-data传参</li>
     * </ul>
     * <p>
     *     比如: a=1&c=3&b=2  排序后  a=1&b=2&c=3
     * </p>
     *
     * @param paraMap TreeMap类型，将参数都放入其中
     * @return 按顺序拼接好的参数串
     */
    public static String getUrlParamsSign(TreeMap<String, Object> paraMap, String slat) {
        if (Objects.isNull(paraMap) || paraMap.isEmpty()) {
            return md5Sign("", slat);
        }
        Iterator<String> it = paraMap.keySet().iterator();
        StringBuilder paramStr = new StringBuilder();
        while (it.hasNext()) {
            String key = it.next();
            Object o = paraMap.get(key);
            if (Objects.isNull(o)) {
                paramStr.append("&").append(key).append("=");
            } else {
                paramStr.append("&").append(key).append("=").append(o);
            }
        }
        String urlParams = paramStr.substring(1);
        return md5Sign(urlParams, slat);
    }

    private static String digestToHex(String s) {
        try {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            byte[] b = digest.digest(s.getBytes(StandardCharsets.UTF_8));
            return toHexString(b);
        } catch (NoSuchAlgorithmException e) {
            log.info("MD5异常,", e);
            return null;
        }
    }

    private static final char[] HEX_CHARS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    private static String toHexString(byte[] b) {
        return b == null ? null : toHexString(b, 0, b.length);
    }

    private static String toHexString(byte[] b, int offset, int length) {
        if (b == null) {
            return null;
        } else {
            int end = offset + length;
            StringBuilder sb = new StringBuilder(length * 2);
            for (int i = offset; i < end; ++i) {
                int c = b[i] >> 4 & 15;
                sb.append(HEX_CHARS[c]);
                c = b[i] & 15;
                sb.append(HEX_CHARS[c]);
            }
            return sb.toString();
        }
    }

    public static void main(String[] args) {
        // 签名的盐值，控制台应用系统中的apiKey
        String apiKey = "123456";

        // url传参方式签名 比如a=1&b=2&c=3
        TreeMap<String, Object> paramMap = new TreeMap<>();
        paramMap.put("a", 1);
        paramMap.put("b", 2);
        paramMap.put("c", 3);
        String urlParamsSign = getUrlParamsSign(paramMap, apiKey);
        System.out.println("url参数签名(getUrlParamsSign方法): " + urlParamsSign);

        String urlParamsSign2 = md5Sign("a=1&b=2&c=3", apiKey);
        System.out.println("url参数签名(md5Sign方法): " + urlParamsSign2);

        // json传参方式签名
        String obj = "{\"a\":\"0\",\"c\":\"\",\"d\":0,\"b\":\"0\",\"c1\":\"\",\"a1\":\"0\"}";

        String sortedJsonStr = sortJsonToStr(obj);
        System.out.println("json字符串排序结果:" + sortedJsonStr);
        String jsonStrSign = md5Sign(sortedJsonStr, apiKey);
        System.out.println("json字符串签名结果:" + jsonStrSign);

        JSONObject jsonObject = JSONObject.parseObject(obj);
        String sortedJsonStr2 = sortJsonToStr(jsonObject);
        System.out.println("json对象排序结果:" + sortedJsonStr2);
        String jsonSign = md5Sign(sortedJsonStr2, apiKey);
        System.out.println("json对象签名结果:" + jsonSign);

    }
}