package com.yifu.cloud.plus.v1.yifu.archives.utils;

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * @ClassName: GZSign 签名工具类 主要提供加签、验签、hash编码等功能
 * ———— 瓜子提供的签名工具类
 * @date 2018年3月21日 下午4:38:40
 */
public class GZSign {

	private static final String STRING = "abcdefghijklmnopqrstuvwxyz";
	private static final String APP_KEY = "appkey";
	private static final String EXPIRES = "expires";
	private static final String NONCE = "nonce";
	private static final String SIGNATURE = "signature";

	/**
	 * 生成签名并且将签名加到参数表中
	 *
	 * @param params    业务参数列表信息
	 * @param appKey
	 * @param appSecret
	 * @return
	 */
	public static Map<String, Object> addSignature(Map<String, Object> params, String appKey, String appSecret) {
		String sign = generateSignature(params, appKey, appSecret);
		params.put(SIGNATURE, sign);
		return params;
	}

	/**
	 * 通过参数列表获取对应的签名值，验签时使用
	 *
	 * @param params 请求方传递过来的所有参数列表信息
	 * @return
	 */
	public static String getSignature(Map<String, Object> params, String appSecret) {
		if (params == null || params.size() == 0) {
			return null;
		}
		Map<String, Object> needParam = getNeedParam(params);
		String sortedParams = getEncodeString(needParam);
		return generateSignature(sortedParams, appSecret);
	}

	/**
	 * yangyuanzhu  替换掉了* 为%2A
	 *
	 * @param params
	 * @param appSecret
	 * @return
	 */
	public static String getSignatureReplaceStar(Map<String, Object> params, String appSecret) {
		if (params == null || params.size() == 0) {
			return null;
		}
		Map<String, Object> needParam = getNeedParam(params);
		String sortedParams = getEncodeString(needParam);
		sortedParams = sortedParams.replaceAll("\\*", "%2A");//给星号转成%2A
		return generateSignature(sortedParams, appSecret);
	}

	/**
	 * yangyuanzhu  替换掉了空格 为%2A
	 *
	 * @param params
	 * @param appSecret
	 * @return
	 */
	public static String getSignatureReplaceSpace(Map<String, Object> params, String appSecret) {
		if (params == null || params.size() == 0) {
			return null;
		}
		Map<String, Object> needParam = getNeedParam(params);
		String sortedParams = getEncodeString(needParam);
		sortedParams = sortedParams.replaceAll("\\+", "%20");//空格转成%2A
		return generateSignature(sortedParams, appSecret);
	}

	/**
	 * 根据参数计算签名值
	 *
	 * @param params
	 * @return
	 */
	private static String generateSignature(Map<String, Object> params, String appKey, String appSecret) {
		if (params == null || params.size() == 0) {
			return null;
		}
		params.put(APP_KEY, appKey);
		params.put(EXPIRES, getCurrentSecond());
		params.put(NONCE, generateRandomStr(6));
		String sortedParams = getEncodeString(params);
		return generateSignature(sortedParams, appSecret);
	}

	/**
	 * 去掉param里面计算签名时不需要的参数
	 *
	 * @param param
	 * @return
	 */
	private static Map<String, Object> getNeedParam(Map<String, Object> param) {
		Map<String, Object> result = new HashMap<>();
		for (Map.Entry<String, Object> entry : param.entrySet()) {
			if (!SIGNATURE.equals(entry.getKey())) {
				result.put(entry.getKey(), entry.getValue());
			}
		}
		return result;
	}

	/**
	 * sha256取hash Base64编码
	 *
	 * @param params
	 * @param secret
	 * @return
	 */
	private static String sha256HMACEncode(String params, String secret) {
		String result = "";
		try {
			Mac sha256HMAC = Mac.getInstance("HmacSHA256");
			SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
			sha256HMAC.init(secretKey);
			byte[] sha256HMACBytes = sha256HMAC.doFinal(params.getBytes());
			String hash = Base64.encodeBase64String(sha256HMACBytes);
			return hash;
		} catch (Exception e) {
		}
		return result;
	}

	/**
	 * 生成签名 取5-15共10位返回
	 *
	 * @param params
	 * @param appSecret
	 * @return
	 */
	private static String generateSignature(String params, String appSecret) {
		String result = md5(sha256HMACEncode(params, appSecret)).substring(5, 15);
		return result;
	}

	private static String md5(String value) {
		try {
			MessageDigest md = MessageDigest.getInstance("md5");
			byte[] e = md.digest(value.getBytes());
			return byteToHexString(e);
		} catch (NoSuchAlgorithmException e) {
		}
		return null;
	}

	private static String byteToHexString(byte[] salt) {
		StringBuffer hexString = new StringBuffer();
		for (int i = 0; i < salt.length; i++) {
			String hex = Integer.toHexString(salt[i] & 0xFF);
			if (hex.length() == 1) {
				hex = '0' + hex;
			}
			hexString.append(hex.toLowerCase());
		}
		return hexString.toString();
	}

	// 通过参数集合map 组装成需要签名的字符串id=123&name=456
	public static String getEncodeString(Map<String, Object> params) {
		if (params == null || params.size() == 0) {
			return "";
		}
		Iterator<String> it = params.keySet().iterator();
		ArrayList<String> al = new ArrayList<String>();
		while (it.hasNext()) {
			al.add(it.next());
		}
		// 字母升序排列
		Collections.sort(al);
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < al.size(); i++) {
			try {
				String key = al.get(i);
				sb.append(URLEncoder.encode(key == null ? "" : key.toString(), "UTF-8"));
				sb.append("=");
				String item = null;
				Object value = params.get(key);
				item = URLEncoder.encode(value == null ? "" : value.toString(), "UTF-8");
				sb.append(item);
			} catch (UnsupportedEncodingException e) {
			} catch (NullPointerException e) {
			}
			sb.append("&");
		}
		String result = sb.toString();
		return result.substring(0, result.length() - 1);
	}

	// 获取len个随机字符串
	public static String generateRandomStr(int len) {
		StringBuffer sb = new StringBuffer();
		int count = len <= STRING.length() ? len : STRING.length();
		Random random = new Random();
		for (int i = 0; i < count; i++) {
			sb.append(STRING.charAt(random.nextInt(STRING.length() - 1)));
		}
		return sb.toString();
	}

	// 获取当前时间秒数+60S
	public static String getCurrentSecond() {
		return (System.currentTimeMillis() / 1000 + 60) + "";
	}

	// 参数map转化成get接口参数格式
	public static String map2GetString(Map<String, Object> map) {
		if (map == null || map.size() < 1) {
			return "";
		}
		StringBuilder params = new StringBuilder();
		for (Map.Entry<String, Object> entry : map.entrySet()) {
			params.append(entry.getKey()).append("=");
			if (entry.getValue() != null) {
				params.append(entry.getValue());
			}
			params.append("&");
		}
		params.deleteCharAt(params.lastIndexOf("&"));
		return params.toString();
	}

}
