package wx.wechat.common.signature;

import org.xml.sax.SAXException;
import com.test.shared.util.codec.MD5;
import com.test.shared.util.codec.SHA1;
import wx.wechat.common.Configure;
import wx.wechat.utils.XMLUtils;

import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;

/**
 * User: rizenguo
 * Date: 2014/10/29
 * Time: 15:23
 */
public class Signature {

    /**
     * @region 以下签名算法用于微信公众号管理
     */
    public static String getSign4MP(Map<String, Object> map) {

        ArrayList<String> list = new ArrayList<String>();
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            if (entry.getValue() != "") {
                list.add(entry.getKey() + "=" + entry.getValue() + "&");
            }
        }
        int size = list.size();
        String[] arrayToSort = list.toArray(new String[size]);
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < size; i++) {
            sb.append(arrayToSort[i]);
        }
        String result = sb.toString();

        //移除最后一个&
        result = result.substring(0, result.length() - 1);

        result = SHA1.encode(result);

        return result;
    }

    /**
     * @region 以下为用于微信支付的签名计算
     */

    /**
     * 签名算法
     *
     * @param o 要参与签名的数据对象
     * @return 签名
     * @throws IllegalAccessException
     */
//    public static String getSign4Pay(Object o) throws IllegalAccessException {
//        ArrayList<String> list = new ArrayList<String>();
//        Class cls = o.getClass();
//        Field[] fields = cls.getDeclaredFields();
//        for (Field f : fields) {
//            f.setAccessible(true);
//            if (f.get(o) != null && f.get(o) != "") {
//                list.add(f.getName() + "=" + f.get(o) + "&");
//            }
//        }
//        int size = list.size();
//        String[] arrayToSort = list.toArray(new String[size]);
//        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
//        StringBuilder sb = new StringBuilder();
//        for (int i = 0; i < size; i++) {
//            sb.append(arrayToSort[i]);
//        }
//        String result = sb.toString();
//        result += "key=" + Configure.mchKey;
//        result = MD5.MD5Encode(result).toUpperCase();
//        return result;
//    }

    /**
     * @param map
     * @return
     * @function 从Map中获取签名
     * 假设传送的参数如下：
     * <p>
     * appid： wxd930ea5d5a258f4f
     * <p>
     * mch_id： 10000100
     * <p>
     * device_info： 1000
     * <p>
     * body： test
     * <p>
     * nonce_str： ibuaiVcKdpRxkhJA
     * <p>
     * 第一步：对参数按照key=value的格式，并按照参数名ASCII字典序排序如下：
     * <p>
     * stringA="appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA";
     * <p>
     * 第二步：拼接API密钥：
     * <p>
     * stringSignTemp="stringA&key=192006250b4c09247ec02edce69f6a2d"
     * <p>
     * sign=MD5(stringSignTemp).toUpperCase()="9A0A8659F005D6984697E2CA0A9CF3B7"
     * <p>
     * 最终得到最终发送的数据：
     * <p>
     * <xml>
     * <p>
     * <appid>wxd930ea5d5a258f4f</appid>
     * <p>
     * <mch_id>10000100</mch_id>
     * <p>
     * <device_info>1000<device_info>
     * <p>
     * <body>test</body>
     * <p>
     * <nonce_str>ibuaiVcKdpRxkhJA</nonce_str>
     * <p>
     * <sign>9A0A8659F005D6984697E2CA0A9CF3B7</sign>
     * <p>
     * <xml>
     */
    public static String getSign4Pay(Map<String, Object> map) {
        ArrayList<String> list = new ArrayList<String>();
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            if (entry.getValue() != "") {
                list.add(entry.getKey() + "=" + entry.getValue() + "&");
            }
        }
        int size = list.size();
        String[] arrayToSort = list.toArray(new String[size]);
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < size; i++) {
            sb.append(arrayToSort[i]);
        }
        String result = sb.toString();
        result += "key=" + Configure.mchKey;
        //Util.log("Sign Before com.test.shared.util.codec.MD5:" + result);
        result = MD5.MD5Encode(result).toUpperCase();
        //Util.log("Sign Result:" + result);
        return result;
    }

    /**
     * 从API返回的XML数据里面重新计算一次签名
     *
     * @param responseString API返回的XML数据
     * @return 新鲜出炉的签名
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws SAXException
     */
    public static String getSignFromResponseString(String responseString) throws IOException, SAXException, ParserConfigurationException {
        Map<String, Object> map = XMLUtils.XML2Map(responseString);
        //清掉返回数据对象里面的Sign数据（不能把这个数据也加进去进行签名），然后用签名算法进行签名
        map.put("sign", "");
        //将API返回的数据根据用签名算法进行计算新的签名，用来跟API返回的签名进行比较
        return Signature.getSign4Pay(map);
    }

    /**
     * 检验API返回的数据里面的签名是否合法，避免数据在传输的过程中被第三方篡改
     *
     * @param responseString API返回的XML数据字符串
     * @return API签名是否合法
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws SAXException
     */
    public static boolean checkIsSignValidFromResponseString(String responseString) throws ParserConfigurationException, IOException, SAXException {

        Map<String, Object> map = XMLUtils.XML2Map(responseString);

        String signFromAPIResponse = map.get("sign").toString();
        if (signFromAPIResponse == "" || signFromAPIResponse == null) {
            return false;
        }
        //清掉返回数据对象里面的Sign数据（不能把这个数据也加进去进行签名），然后用签名算法进行签名
        map.put("sign", "");
        //将API返回的数据根据用签名算法进行计算新的签名，用来跟API返回的签名进行比较
        String signForAPIResponse = Signature.getSign4Pay(map);

        if (!signForAPIResponse.equals(signFromAPIResponse)) {
            //签名验不过，表示这个API返回的数据有可能已经被篡改了
            return false;
        }
        return true;
    }

}
