package com.trs.project.util;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.log4j.Log4j2;

import java.security.MessageDigest;
import java.util.*;

/**
 * MD5加密工具类
 *
 * @Name MD5Utils
 * @Author adai
 * @Date 2021/4/14 4:34 下午
 * @Version V1.0.0
 **/
@Log4j2
public class MD5Utils {

    private static final char MD5_STRING[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            'A', 'B', 'C', 'D', 'E', 'F'};

    public final static String MD5(String pwd) {
        try {
            //使用平台的默认字符集将此 String 编码为 byte序列，并将结果存储到一个新的 byte数组中
            byte[] btInput = pwd.getBytes();
            //信息摘要是安全的单向哈希函数，它接收任意大小的数据，并输出固定长度的哈希值。
            MessageDigest mdInst = MessageDigest.getInstance("MD5");
            //MessageDigest对象通过使用 update方法处理数据， 使用指定的byte数组更新摘要
            mdInst.update(btInput);
            // 摘要更新之后，通过调用digest（）执行哈希计算，获得密文
            byte[] md = mdInst.digest();
            // 把密文转换成十六进制的字符串形式
            int j = md.length;
            char str[] = new char[j * 2];
            int k = 0;
            for (int i = 0; i < j; i++) {   //  i = 0
                byte byte0 = md[i];  //95
                str[k++] = MD5_STRING[byte0 >>> 4 & 0xf];    //    5
                str[k++] = MD5_STRING[byte0 & 0xf];   //   F
            }
            //返回经过加密后的字符串
            return new String(str);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 计算数据签名
     *
     * @param appId      应用APPID
     * @param data       请求数据
     * @param timeStamp  时间戳
     * @param signSecret appId对应的签名秘钥
     * @return
     */
    public static String getRequestSign(String appId, String data, String timeStamp, String signSecret) {
        StringBuilder sb = new StringBuilder();
        sb.append("appId=").append(appId);
        sb.append("&data=").append(data);
        sb.append("&timeStamp=").append(timeStamp);
        sb.append("&secret=").append(signSecret);
        return MD5Utils.MD5(sb.toString());
    }

    /**
     * 计算数据签名
     *
     * @param data      请求数据
     * @param appSecret appId对应的秘钥
     * @return
     */
    public static String getRequestSign(JSONObject data, String appSecret) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, Object> entry : getSortJson(data).entrySet()) {
            if (entry.getKey().equals("sign")) {
                continue;
            }
            if (entry.getValue() instanceof JSONObject && "file".equals(((JSONObject) entry.getValue()).getString("type"))) {
                continue;
            }
            if (sb.length() > 0) {
                sb.append("&");
            }
            sb.append(entry.getKey()).append("=").append(entry.getValue().toString());
        }
        sb.append("&secret=").append(appSecret);
        String str = sb.toString();
        log.info("MD5待加密串:[{}]", str);
        return MD5Utils.MD5(str);
    }

    /**
     * json 按照key进行字典排序
     *
     * @param json
     * @return
     */
    public static JSONObject getSortJson(JSONObject json) {
        if (Objects.isNull(json)) {
            return new JSONObject();
        }
        Set<String> keySet = json.keySet();
        SortedMap<String, Object> map = new TreeMap<>();
        for (String key : keySet) {
            Object value = json.get(key);
            if (Objects.nonNull(value) && (value instanceof JSONArray || value instanceof List)) {
                JSONArray array = json.getJSONArray(key);
                JSONArray jsonArray = new JSONArray(new LinkedList<>());
                for (int i = 0; i < array.size(); i++) {
                    JSONObject sortJson = getSortJson(array.getJSONObject(i));
                    jsonArray.add(sortJson);
                }
                map.put(key, jsonArray);
            } else if (Objects.nonNull(value) && (value instanceof JSONObject || value instanceof Map)) {
                JSONObject sortJson = getSortJson(json.getJSONObject(key));
                map.put(key, sortJson);
            } else {
                map.put(key, value);
            }
        }
        return new JSONObject(map);
    }
}
