package com.yunniu.farming.wx;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yunniu.farming.util.HttpClientUtil;
import org.apache.commons.codec.digest.DigestUtils;
import org.jdom.JDOMException;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.*;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.*;

import java.io.File;
import java.io.InputStream;
import java.security.KeyStore;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import org.springframework.core.io.ClassPathResource;

public class WxUtil {
    private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    private static final Random RANDOM = new SecureRandom();

    /**
     * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
     *
     * @return boolean
     */
    public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams) throws Exception {
        StringBuffer sb = new StringBuffer();
        Set es = packageParams.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if (!"sign".equals(k) && null != v && !"".equals(v)) {
                sb.append(k + "=" + v + "&");
            }
        }

        sb.append("key=" + WxConfigUtil.API_KEY);

        //算出摘要  
        String mysign = MD5(sb.toString());
        String tenpaySign = ((String) packageParams.get("sign")).toLowerCase();

        //System.out.println(tenpaySign + "    " + mysign);  
        return tenpaySign.equals(mysign);
    }

    /**
     * 获取随机字符串 Nonce Str
     *
     * @return String 随机字符串
     */
    public static String generateNonceStr() {
        char[] nonceChars = new char[32];
        for (int index = 0; index < nonceChars.length; ++index) {
            nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
        }
        return new String(nonceChars);
    }


    /**
     * @param
     * @param parameters 请求参数
     * @return
     * @Description：sign签名
     */
    public static String createSignMD5(SortedMap<Object, Object> parameters) throws Exception {
        StringBuffer sb = new StringBuffer();
        Set es = parameters.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            Object v = entry.getValue();
            if (null != v && !"".equals(v)
                    && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + WxConfigUtil.API_KEY);
        String sign = DigestUtils.md5Hex(sb.toString());
        return sign.toUpperCase();

    }

    public static String createSignHMACSHA256(SortedMap<Object, Object> parameters) throws Exception {
        StringBuffer sb = new StringBuffer();
        Set es = parameters.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            Object v = entry.getValue();
            if (null != v && !"".equals(v)
                    && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + WxConfigUtil.API_KEY);
        String sign = HMACSHA256(sb.toString(), WxConfigUtil.API_KEY);
        return sign;

    }

    /**
     * 生成 MD5
     *
     * @param data 待处理数据
     * @return MD5结果
     */
    public static String MD5(String data) throws Exception {
        MessageDigest md = null;

        md = MessageDigest.getInstance("MD5");

        byte[] array = md.digest(data.getBytes("UTF-8"));

        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }

    /**
     * 生成 HMACSHA256
     *
     * @param data 待处理数据
     * @param key  密钥
     * @return 加密结果
     * @throws Exception
     */
    public static String HMACSHA256(String data, String key) throws Exception {
        Mac sha256_HMAC = null;
        sha256_HMAC = Mac.getInstance("HmacSHA256");

        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }


    /**
     * @param parameters 请求参数
     * @return
     * @Description：将请求参数转换为xml格式的string
     */
    public static String getRequestXml(SortedMap<Object, Object> parameters) {
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        Set es = parameters.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k)) {
                sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
            } else {
                sb.append("<" + k + ">" + v + "</" + k + ">");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }

    /**
     * @param return_code 返回编码
     * @param return_msg  返回信息
     * @return
     * @Description：返回给微信的参数
     */
    public static String setXML(String return_code, String return_msg) {
        return "<xml><return_code><![CDATA[" + return_code
                + "]]></return_code><return_msg><![CDATA[" + return_msg
                + "]]></return_msg></xml>";
    }


    /**
     * 发送https请求
     *
     * @param requestUrl    请求地址
     * @param requestMethod 请求方式（GET、POST）
     * @param outputStr     提交的数据
     * @return 返回微信服务器响应的信息
     */
    public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
        try {
            // 创建SSLContext对象，并使用我们指定的信任管理器初始化
            TrustManager[] tm = {new MyX509TrustManager()};
            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
            sslContext.init(null, tm, new SecureRandom());
            // 从上述SSLContext对象中得到SSLSocketFactory对象
            SSLSocketFactory ssf = sslContext.getSocketFactory();
            URL url = new URL(requestUrl);
            HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
            //conn.setSSLSocketFactory(ssf);
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            // 设置请求方式（GET/POST）
            conn.setRequestMethod(requestMethod);
            conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
            // 当outputStr不为null时向输出流写数据
            if (null != outputStr) {
                OutputStream outputStream = conn.getOutputStream();
                // 注意编码格式
                outputStream.write(outputStr.getBytes("UTF-8"));
                outputStream.close();
            }
            // 从输入流读取返回内容
            InputStream inputStream = conn.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String str = null;
            StringBuffer buffer = new StringBuffer();
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }
            // 释放资源
            bufferedReader.close();
            inputStreamReader.close();
            inputStream.close();
            inputStream = null;
            conn.disconnect();
            return buffer.toString();
        } catch (ConnectException ce) {
//          log.error("连接超时：{}", ce);
        } catch (Exception e) {
//          log.error("https请求异常：{}", e);
        }
        return null;
    }

    public static String doRefund(String url, String data) throws Exception {

        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        // 指定证书路径
        String path = "apiclient_cert.p12";
        ClassPathResource classPathResource = new ClassPathResource(path);

        //读取本机存放的PKCS12证书文件
        InputStream stream = classPathResource.getInputStream();
        String mchId = WxConfigUtil.MCH_ID;
        try {
            //指定PKCS12的密码(商户ID)
            keyStore.load(stream, mchId.toCharArray());
        } finally {
            stream.close();
        }
        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mchId.toCharArray()).build();
        //指定TLS版本
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslcontext,new String[] { "TLSv1.2"},null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        //设置httpclient的SSLSocketFactory
        CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
        try {
            HttpPost httpost = new HttpPost(url); // 设置响应头信息
            httpost.addHeader("Connection", "keep-alive");
            httpost.addHeader("Accept", "*/*");
            httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            httpost.addHeader("Host", "api.mch.weixin.qq.com");
            httpost.addHeader("X-Requested-With", "XMLHttpRequest");
            httpost.addHeader("Cache-Control", "max-age=0");
            httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
            httpost.setEntity(new StringEntity(data, "UTF-8"));
            CloseableHttpResponse response = httpclient.execute(httpost);
            try {
                HttpEntity entity = response.getEntity();
                String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
                EntityUtils.consume(entity);
                return jsonStr;
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
    }


    public static JSONObject httpsRequest(String requestUrl, String requestMethod) {
        JSONObject jsonObject = null;
        try {
            // 创建SSLContext对象，并使用我们指定的信任管理器初始化
            TrustManager[] tm = {new MyX509TrustManager()};
            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
            sslContext.init(null, tm, new SecureRandom());
            // 从上述SSLContext对象中得到SSLSocketFactory对象
            SSLSocketFactory ssf = sslContext.getSocketFactory();
            URL url = new URL(requestUrl);
            HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
            //conn.setSSLSocketFactory(ssf);
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            conn.setConnectTimeout(3000);
            // 设置请求方式（GET/POST）
            conn.setRequestMethod(requestMethod);
            //conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
            // 当outputStr不为null时向输出流写数据
            // 从输入流读取返回内容
            InputStream inputStream = conn.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String str = null;
            StringBuffer buffer = new StringBuffer();
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }
            // 释放资源
            bufferedReader.close();
            inputStreamReader.close();
            inputStream.close();
            inputStream = null;
            conn.disconnect();
            jsonObject = JSONObject.parseObject(buffer.toString());
        } catch (ConnectException ce) {
//                    log.error("连接超时：{}", ce);
        } catch (Exception e) {
            System.out.println(e);
//                    log.error("https请求异常：{}", e);
        }
        return jsonObject;
    }

    public static String urlEncodeUTF8(String source) {
        String result = source;
        try {
            result = java.net.URLEncoder.encode(source, "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return result;
    }


    public static String getSha1Sign(String decript) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-1");
            try {
                digest.update(decript.getBytes("UTF-8"));
            } catch (UnsupportedEncodingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            byte messageDigest[] = digest.digest();
            // Create Hex String
            StringBuffer hexString = new StringBuffer();
            // 字节数组转换为 十六进制 数
            for (int i = 0; i < messageDigest.length; i++) {
                String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexString.append(0);
                }
                hexString.append(shaHex);
            }
            return hexString.toString();

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }


    /**
     * 分账
     */
//    public static boolean profitsharing(Order order, String receivers) throws Exception {
//        SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
//        parameters.put("appid", WxConfigUtil.APPID); // 应用APPID
//        parameters.put("mch_id", WxConfigUtil.MCH_ID); // 微信支付分配的商户号
//        parameters.put("nonce_str", generateNonceStr()); // 随机字符串，不长于32位
//        parameters.put("sign_type", WxConfigUtil.HMACSHA256); // 签名类型
//        parameters.put("transaction_id", order.getTransactionId()); // 商品描述
//        parameters.put("out_trade_no", order.getOutTradeNo()); // 支付订单编号
//        parameters.put("status", "FINISHED"); //PROCESSING
//        parameters.put("receivers", receivers); //接收方
//        // 设置签名
//        String sign = WxUtil.createSignHMACSHA256(parameters);
//        System.out.println("sign=====" + sign);
//        parameters.put("sign", sign);
//        // 封装请求参数结束
//        String requestXML = WxUtil.getRequestXml(parameters);
//        System.out.println(requestXML);
//        // 调用统一下单接口
//        String result = CertHttpUtil.postData(WxConfigUtil.PROFIT_SHARING_URL, requestXML);
//        System.out.println(result);
//        Map<String, String> map = XMLUtil.doXMLParse(result);
//        if ((map.get("return_code")).equals("SUCCESS") && (map.get("result_code")).equals("SUCCESS")) {
//            return true;
//        }
//        return false;
//    }
    public static boolean addprofitsharingreceiver(String openId) {
        String receiver = "{\"type\": \"PERSONAL_OPENID\",\"account\":\"" + openId + "\",\"relation_type\": \"USER\"}";
        SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
        parameters.put("appid", WxConfigUtil.APPID); // 应用APPID
        parameters.put("mch_id", WxConfigUtil.MCH_ID); // 微信支付分配的商户号
        parameters.put("nonce_str", generateNonceStr()); // 随机字符串，不长于32位
        parameters.put("sign_type", WxConfigUtil.HMACSHA256); // 签名类型
        parameters.put("receiver", receiver); //接收方
        // 设置签名
        String sign = null;
        try {
            sign = WxUtil.createSignHMACSHA256(parameters);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("sign=====" + sign);
        parameters.put("sign", sign);
        // 封装请求参数结束
        String requestXML = WxUtil.getRequestXml(parameters);
        System.out.println(requestXML);
        //添加分账接收方
        String result = httpsRequest(WxConfigUtil.PROFIT_SHARING_ADDRECEIVER, "POST", requestXML);
        System.out.println(result);
        Map<String, String> map = null;
        try {
            map = XMLUtil.doXMLParse(result);
        } catch (JDOMException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        if ((map.get("return_code")).equals("SUCCESS") && (map.get("result_code")).equals("SUCCESS")) {
            return true;
        }
        return false;
    }




    public static void main(String[] args) {
        downloadMiniCode("123");
    }


    /**
     * 下载带参数的小程序二维码
     * https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/qr-code/wxacode.getUnlimited.html
     * by zhengkai.blog.csdn.net
     *
     * @param number 带过去小程序的参数，一般为你的业务参数，建议是id或者number
     */
    public static String downloadMiniCode(String number) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("scene", number);
//        paramMap.put("share", number);
        paramMap.put("is_hyaline", true);
//        paramMap.put("dateTime", System.currentTimeMillis());
//        paramMap.put("scene", sceneStr);
       paramMap.put("page", "pages/index/index"); // 这里特别注意前面不要加 '/'
//        paramMap.put("width", 430);
//        paramMap.put("auto_color", true); // 自动配置线条颜色
//        paramMap.put("is_hyaline", true); // 背景色透明
//        String imgFilePath = "/usr/upload/" + number + ".png";// 新生成的图片
        String imgFilePath = "D://upload//" + UUID.randomUUID() + ".png";// 新生成的图片
        try {
            URL url = new URL(WxConfigUtil.GET_MINICODE_URL + getAccessTokenAsUrl(WxConfigUtil.APPID, WxConfigUtil.App_Secret));
            HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
            httpURLConnection.setRequestMethod("POST");// 提交模式
            httpURLConnection.setConnectTimeout(10000);//连接超时 单位毫秒
            httpURLConnection.setReadTimeout(10000);//读取超时 单位毫秒
            // 发送POST请求必须设置如下两行
            httpURLConnection.setDoOutput(true);
            httpURLConnection.setDoInput(true);
            // 获取URLConnection对象对应的输出流
            PrintWriter printWriter = new PrintWriter(httpURLConnection.getOutputStream());
            printWriter.write(JSON.toJSONString(paramMap));
            // flush输出流的缓冲
            printWriter.flush();
            //开始获取数据
            BufferedInputStream bis = new BufferedInputStream(httpURLConnection.getInputStream());
            OutputStream os = new FileOutputStream(new File(imgFilePath));
            int len;
            //设置缓冲写入
            byte[] arr = new byte[2048];
            while ((len = bis.read(arr)) != -1) {
                os.write(arr, 0, len);
                os.flush();
            }
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return imgFilePath;
    }


    public static String getAccessTokenAsUrl(String appId, String appSecret) {
        String tokenStr = HttpClientUtil.doGet("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret=" + appSecret + "");
//        String tokenStr = HttpUtil.get("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret=" + appSecret + "");
        JSONObject jsonObject = JSONObject.parseObject(tokenStr);
        System.out.println(jsonObject);
        return "?access_token=" + jsonObject.getString("access_token");
    }
}
