package com.friday.custody.api.util;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.NetworkInterface;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.util.*;

public class GJDFSignGenerator {
    private static Logger logger = LoggerFactory.getLogger(GJDFSignGenerator.class);

    private static String percentEncode(String src) {
        if ((src != null) && (src.length() > 0)) {
            try {
                return URLEncoder.encode(src, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                return src;
            }
        }
        return src;
    }

    private static String getSignature(String data, String key) {
        return GJDFHmac.encryptHMAC(data, key);
    }

    public static String getSignature(String httpMethod, String target, String body, String CanonicalizedQueryString, String accessKeySecret) {
        String temp = sortParameter(CanonicalizedQueryString);
        StringBuffer sbb = new StringBuffer();
        String[] stss = body.split("&", 0);
        Arrays.sort(stss);
        for (int i = 0; i < stss.length; i++) {
            sbb.append(stss[i]);
            if (i < stss.length - 1){
                sbb.append("&");
            }
        }
        if (target.contains("?")) {
            String[] tars = target.split("\\?", 0);
            if ((tars.length > 0) && (tars[1].length() > 0)) {
                String[] tts = new StringBuilder().append(tars[1]).append("&").append(temp).toString().split("&", 0);
                Arrays.sort(tts);
                for (int i = 0; i < tts.length; i++) {
                    if (sbb.charAt(sbb.length() - 1) != '&'){
                        sbb.append("&");
                    }
                    sbb.append(tts[i]);
                }
            }

            target = tars[0];
            temp = sbb.toString();
        } else {
            temp = new StringBuilder().append(sbb).append("&").append(temp).toString();
        }

        return getSignature(httpMethod, target, temp, accessKeySecret);
    }

    public static String getSignature(String httpMethod, String target, String body, String CanonicalizedQueryString, String accessKeySecret, int k) {
        String temp = sortParameter(CanonicalizedQueryString);
        StringBuffer sbb = new StringBuffer();

        if (target.contains("?")) {
            String[] tars = target.split("\\?", 0);
            if ((tars.length > 0) && (tars[1].length() > 0)) {
                String[] tts = new StringBuilder().append(tars[1]).append("&").append(temp).toString().split("&", 0);
                Arrays.sort(tts);
                for (int i = 0; i < tts.length; i++) {
                    if (sbb.charAt(sbb.length() - 1) != '&')
                        sbb.append("&");
                    sbb.append(tts[i]);
                }
            }

            target = tars[0];
            temp = sbb.toString();
        } else if (sbb.toString().length() > 0) {
            temp = new StringBuilder().append(sbb).append("&").append(temp).toString();
        }

        return getSignature2(httpMethod, target, body, temp, accessKeySecret);
    }

    public static String getSignature(String httpMethod, String target, String CanonicalizedQueryString, String accessKeySecret) {
        StringBuffer strBuff = new StringBuffer();
        strBuff.append(httpMethod.toUpperCase());
        strBuff.append("&");
        strBuff.append(percentEncode(target));
        strBuff.append("&");
        strBuff.append(percentEncode(CanonicalizedQueryString));
        String result = getSignature(strBuff.toString(), new StringBuilder().append(accessKeySecret).append("&").toString());
        return result;
    }

    public static String getSignature2(String httpMethod, String target, String JSONbody, String CanonicalizedQueryString, String accessKeySecret) {
        StringBuffer strBuff = new StringBuffer();
        strBuff.append(httpMethod.toUpperCase());
        strBuff.append("&");
        strBuff.append(percentEncode(target));
        strBuff.append("&");
        strBuff.append(JSONbody);
        strBuff.append("&");
        strBuff.append(percentEncode(CanonicalizedQueryString));
        String result = getSignature(strBuff.toString(), new StringBuilder().append(accessKeySecret).append("&").toString());
        return result;
    }

    private static String sortParameter(String CanonicalizedQueryString) {
        if ((CanonicalizedQueryString != null) && (CanonicalizedQueryString.length() > 0)) {
            List list = Arrays.asList(CanonicalizedQueryString.split("\\&"));
            Collections.sort(list);
            StringBuffer sb = new StringBuffer();
            if ((list != null) && (list.size() > 0)) {
                for (int i = 0; i < list.size(); i++) {
                    sb.append(list.get(i));
                    if (i != list.size() - 1) {
                        sb.append("&");
                    }
                }
            }
            return sb.toString();
        }
        return "";
    }

    public static String getSignatureString(String target, String httpMethod, Map<String, String> args, String contentType, String body, boolean uppercaseParameters) {
        if (uppercaseParameters) {
            return getSignatureString(target, httpMethod, args, contentType, body);
        }
        if ("".equals(target)) {
            target = "/";
        }
        StringBuilder result = new StringBuilder();
        if (args == null) {
            args = new HashMap();
        }
        String accessKeySecret = args.get("AppSecret");
        args.remove("AppSecret");

        String signatureNonce = getSignatureNonce(10);
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        String timestamp = df.format(new Date());

        if (args.containsKey("timestamp")) {
            timestamp = args.get("timestamp");
        }

        String signatureVersion = "1.0";

        args.put("signature_nonce", signatureNonce);
        args.put("timestamp", timestamp);
        args.put("signature_version", signatureVersion);

        result.append(new StringBuilder().append("signature_nonce=").append(signatureNonce).append("&signature_version=").append(signatureVersion).append("&timestamp=").append(timestamp).append("&app_key=").append(args.get("app_key")));

        StringBuilder urlParameter = new StringBuilder();
        for (Map.Entry entry : args.entrySet()) {
            urlParameter.append("&").append((String) entry.getKey()).append("=").append((String) entry.getValue());
        }
        String sortUrlPar = sortStringByRegex(urlParameter.substring(1), "&");

        StringBuilder parameter = new StringBuilder();
        StringBuilder bodyParameter = new StringBuilder();
        if ((body != null) && (!"".equals(body)) &&
                ("POST".equalsIgnoreCase(httpMethod))) {
            if (!"application/json".equals(contentType)) {
                bodyParameter.append(sortStringByRegex(body, "&"));
                parameter.append(bodyParameter).append(percentEncode("&"));
            } else {
                bodyParameter.append(body);
                parameter.append(bodyParameter).append("&");
            }
        }

        parameter.append(sortUrlPar);

        StringBuffer strBuff = new StringBuffer();
        strBuff.append(httpMethod.toUpperCase());
        strBuff.append("&");
        strBuff.append(percentEncode(target));
        strBuff.append("&");
        strBuff.append(parameter);
        String signature = getSignature(strBuff.toString(), new StringBuilder().append(accessKeySecret).append("&").toString());

        result.append("&signature=").append(signature);
        return result.toString();
    }

    /**
     * 计算签名
     * @param target  请求路径
     * @param httpMethod 请求方法
     * @param args  保存app_key，AppSecret
     * @param contentType 格式：application/json
     * @param body 请求体：保存必填参数和分页参数
     * @param uppercaseParameters turn：选择另一种计算方式，false：不选择
     * @return
     */
    public static Map<String,String> getSignatureMap(String target, String httpMethod, Map<String, String> args, String contentType, String body, boolean uppercaseParameters) {
        Map<String, String> signatureMap = new HashMap<>();
        if (uppercaseParameters) {
            return getSignatureMap(target, httpMethod, args, contentType, body);
        }
        if ("".equals(target)) {
            target = "/";
        }
        StringBuilder result = new StringBuilder();
        if (args == null) {
            args = new HashMap();
        }
        String accessKeySecret = args.get("AppSecret");
        args.remove("AppSecret");

        String signatureNonce = getSignatureNonce(10);
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        String timestamp = df.format(new Date());

        if (args.containsKey("timestamp")) {
            timestamp = args.get("timestamp");
        }

        String signatureVersion = "1.0";

        args.put("signature_nonce", signatureNonce);
        args.put("timestamp", timestamp);
        args.put("signature_version", signatureVersion);

        signatureMap.put("signature_nonce",signatureNonce);
        signatureMap.put("signature_version",signatureVersion);
        signatureMap.put("timestamp",timestamp);
        signatureMap.put("app_key",args.get("app_key"));
        StringBuilder urlParameter = new StringBuilder();
        for (Map.Entry entry : args.entrySet()) {
            urlParameter.append("&").append((String) entry.getKey()).append("=").append((String) entry.getValue());
        }
        String sortUrlPar = sortStringByRegex(urlParameter.substring(1), "&");

        StringBuilder parameter = new StringBuilder();
        StringBuilder bodyParameter = new StringBuilder();
        if ((body != null) && (!"".equals(body)) &&
                ("POST".equalsIgnoreCase(httpMethod))) {
            if (!"application/json".equals(contentType)) {
                bodyParameter.append(sortStringByRegex(body, "&"));
                parameter.append(bodyParameter).append(percentEncode("&"));
            } else {
                bodyParameter.append(body);
                parameter.append(bodyParameter).append("&");
            }
        }

        parameter.append(sortUrlPar);

        StringBuffer strBuff = new StringBuffer();
        strBuff.append(httpMethod.toUpperCase());
        strBuff.append("&");
        strBuff.append(percentEncode(target));
        strBuff.append("&");
        strBuff.append(parameter);
        String signature = getSignature(strBuff.toString(), new StringBuilder().append(accessKeySecret).append("&").toString());
        signatureMap.put("signature",signature);

        return signatureMap;
    }


    public static String getSignatureString(String target, String httpMethod, Map<String, String> args, String contentType, String body) {
        if ("".equals(target)) {
            target = "/";
        }
        StringBuilder result = new StringBuilder();
        if (args == null) {
            args = new HashMap();
        }
        String accessKeySecret = args.get("AppSecret");
        args.remove("AppSecret");

        String signatureNonce = getSignatureNonce(10);
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        String timesTamp = df.format(new Date());

        String signatureVersion = "1.0";

        args.put("SignatureNonce", signatureNonce);
        args.put("TimeStamp", timesTamp);
        args.put("SignatureVersion", signatureVersion);

        result.append(new StringBuilder().append("SignatureNonce=").append(signatureNonce).append("&SignatureVersion=").append(signatureVersion).append("&TimeStamp=").append(timesTamp).append("&app_key=").append(args.get("app_key")));

        StringBuilder urlParameter = new StringBuilder();
        for (Map.Entry entry : args.entrySet()) {
            urlParameter.append("&").append((String) entry.getKey()).append("=").append((String) entry.getValue());
        }
        String sortUrlPar = sortStringByRegex(urlParameter.substring(1), "&");

        StringBuilder parameter = new StringBuilder();
        StringBuilder bodyParameter = new StringBuilder();
        if ((body != null) && (!"".equals(body)) &&
                ("POST".equalsIgnoreCase(httpMethod))) {
            if (!"application/json".equals(contentType)) {
                bodyParameter.append(sortStringByRegex(body, "&"));
                parameter.append(bodyParameter).append(percentEncode("&"));
            } else {
                bodyParameter.append(body);
                parameter.append(bodyParameter).append("&");
            }
        }

        parameter.append(sortUrlPar);

        StringBuffer strBuff = new StringBuffer();
        strBuff.append(httpMethod.toUpperCase());
        strBuff.append("&");
        strBuff.append(percentEncode(target));
        strBuff.append("&");
        strBuff.append(parameter);
        String signature = getSignature(strBuff.toString(), new StringBuilder().append(accessKeySecret).append("&").toString());

        result.append("&Signature=").append(signature);
        return result.toString();
    }
    public static Map<String, String> getSignatureMap(String target, String httpMethod, Map<String, String> args, String contentType, String body) {
        Map<String, String> signatureMap = new HashMap<>();
        if ("".equals(target)) {
            target = "/";
        }
        StringBuilder result = new StringBuilder();
        if (args == null) {
            args = new HashMap();
        }
        String accessKeySecret = args.get("AppSecret");
        args.remove("AppSecret");

        String signatureNonce = getSignatureNonce(10);
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        String timesTamp = df.format(new Date());

        String signatureVersion = "1.0";

        args.put("SignatureNonce", signatureNonce);
        args.put("TimeStamp", timesTamp);
        args.put("SignatureVersion", signatureVersion);
        signatureMap.put("signature_nonce",signatureNonce);
        signatureMap.put("signature_version",signatureVersion);
        signatureMap.put("timestamp",timesTamp);
        signatureMap.put("app_key",args.get("app_key"));
        StringBuilder urlParameter = new StringBuilder();
        for (Map.Entry entry : args.entrySet()) {
            urlParameter.append("&").append((String) entry.getKey()).append("=").append((String) entry.getValue());
        }
        String sortUrlPar = sortStringByRegex(urlParameter.substring(1), "&");

        StringBuilder parameter = new StringBuilder();
        StringBuilder bodyParameter = new StringBuilder();
        if ((body != null) && (!"".equals(body)) &&
                ("POST".equalsIgnoreCase(httpMethod))) {
            if (!"application/json".equals(contentType)) {
                bodyParameter.append(sortStringByRegex(body, "&"));
                parameter.append(bodyParameter).append(percentEncode("&"));
            } else {
                bodyParameter.append(body);
                parameter.append(bodyParameter).append("&");
            }
        }

        parameter.append(sortUrlPar);

        StringBuffer strBuff = new StringBuffer();
        strBuff.append(httpMethod.toUpperCase());
        strBuff.append("&");
        strBuff.append(percentEncode(target));
        strBuff.append("&");
        strBuff.append(parameter);
        String signature = getSignature(strBuff.toString(), new StringBuilder().append(accessKeySecret).append("&").toString());
        signatureMap.put("Signature",signature);
        return signatureMap;
    }

    private static boolean matches(String str, String regex) {
        if ((str.equals(regex)) && (str.indexOf(92) < 0)) {
            return true;
        }
        int rIdx = 0;
        int sIdx = 0;
        while ((rIdx < regex.length()) && (sIdx < str.length())) {
            char c = regex.charAt(rIdx);
            switch (c) {
                case '*':
                    String tempSource = str.substring(sIdx);
                    String tempRegex = regex.substring(rIdx + 1);
                    for (int j = 0; j <= tempSource.length(); j++) {
                        if (matches(tempSource.substring(j), tempRegex)) {
                            return true;
                        }
                    }
                    return false;
                case '?':
                    break;
                case '\\':
                    c = regex.charAt(++rIdx);
                default:
                    if (str.charAt(sIdx) != c){
                        return false;
                    }
                    break;
            }
            rIdx++;
            sIdx++;
        }

        return (str.length() == sIdx) && (
                (regex
                        .length() == rIdx) || ((regex.length() == rIdx + 1) && (regex.charAt(rIdx) == '*')));
    }

    public static String aesDecrypt(String text, String password) {
        try {
            new Base64();
            byte[] content = Base64.decodeBase64(text);
            KeyGenerator kgen = KeyGenerator.getInstance("GCM");
            SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
            random.setSeed(password.getBytes());
            kgen.init(128, random);
            SecretKey secretKey = kgen.generateKey();
            byte[] enCodeFormat = secretKey.getEncoded();
            SecretKeySpec key = new SecretKeySpec(enCodeFormat, "GCM");
            Cipher cipher = Cipher.getInstance("GCM");
            cipher.init(2, key);
            byte[] result = cipher.doFinal(content);
            return new String(result, StandardCharsets.UTF_8);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String aesEncrypt(String content, String password) {
        try {
            KeyGenerator kgen = KeyGenerator.getInstance("GCM");
            SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
            random.setSeed(password.getBytes());
            kgen.init(128, random);
            SecretKey secretKey = kgen.generateKey();
            byte[] enCodeFormat = secretKey.getEncoded();
            SecretKeySpec key = new SecretKeySpec(enCodeFormat, "GCM");
            Cipher cipher = Cipher.getInstance("GCM");
            byte[] byteContent = content.getBytes(StandardCharsets.UTF_8);
            cipher.init(1, key);
            byte[] result = cipher.doFinal(byteContent);

            return new Base64().encodeToString(result);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static String getCpuInfo() {
        try {
            Process process = Runtime.getRuntime().exec(new String[]{"wmic", "cpu", "get", "ProcessorId"});
            process.getOutputStream().close();
            Scanner sc = new Scanner(process.getInputStream());
            String property = sc.next();
            return sc.next();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    private static List<String> getMacInfo() {
        List data = new ArrayList();
        try {
            Enumeration enumeration = NetworkInterface.getNetworkInterfaces();
            while (enumeration.hasMoreElements()) {
                StringBuffer stringBuffer = new StringBuffer();
                NetworkInterface networkInterface = (NetworkInterface) enumeration.nextElement();
                if (networkInterface != null) {
                    byte[] bytes = networkInterface.getHardwareAddress();
                    if (bytes != null) {
                        for (int i = 0; i < bytes.length; i++) {
                            if (i != 0) {
                                stringBuffer.append("-");
                            }
                            int tmp = bytes[i] & 0xFF;
                            String str = Integer.toHexString(tmp);
                            if (str.length() == 1){
                                stringBuffer.append(new StringBuilder().append("0").append(str));
                            } else {
                                stringBuffer.append(str);
                            }
                        }
                        String mac = stringBuffer.toString().toUpperCase();
                        if (!"".equals(mac)){
                            data.add(mac);
                        }

                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return data;
    }

    private static String getSignatureNonce(int len) {
        StringBuilder signatureNonce = new StringBuilder();
        String strLibrary = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        Random rd = new Random();
        for (int i = 0; i < len; i++) {
            signatureNonce.append(strLibrary.charAt(rd.nextInt(strLibrary.length())));
        }
        return signatureNonce.toString();
    }

    private static String sortStringByRegex(String str, String regex) {
        StringBuilder sbb = new StringBuilder();
        String[] stss = str.split("&", 0);
        Arrays.sort(stss);
        for (int i = 0; i < stss.length; i++) {
            sbb.append(stss[i]);
            if (i < stss.length - 1){
                sbb.append("&");
            }
        }
        return percentEncode(sbb.toString());
    }
}