package com.ningdatech.pmapi.irs.sign;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.io.*;
import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.security.Key;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;


public class IRSAPIRequest {
    //天印服务器接口信息
//    private static String ProjectID = "XXX";
//    private static String ProjectSecret = "XXXX";
//    private static String accessKey = "XXXX";
//    private static String secretKey = "XXXX";
//    private static String apiUrl = "https://ibcdsg.zj.gov.cn:8443/restapi/prod/IC33000020220309000004/seal-platform/seal/v1/rest/sign/signPdf";

    private static String ProjectID = "XXX";
    private static String ProjectSecret = "XXXX";
    private static String accessKey = "XXXX";
    private static String secretKey = "XXXX";
    private static String apiUrl = "https://ibcdsg.zj.gov.cn:8443/restapi/prod/IC33000020220309000004/seal-platform/seal/v1/rest/sign/signPdf";


    public static void main(String[] args) throws Exception {

        createSignPdf();

    }

    /**
     * pdf文件盖章
     * 接口地址：/V1/accounts/outerAccounts/create
     *
     * @return
     */
    public static JSONObject createSignPdf() {
        JSONObject obj = null;
        String resp = null;
        try {
            JSONObject ReqData = new JSONObject();
            String pathname = "D://001.pdf";
            File file = new File(pathname);
            String fileByte1 = IRSAPIRequest.PDFToBase64(file);
            ReqData.put("fileBase64", fileByte1);
            ReqData.put("sealSn", "33012108041829053952");
            ReqData.put("posX", "200");
            ReqData.put("posY", "200");
            ReqData.put("signType", "1");
            // ReqData.put("key", "单位");
            ReqData.put("posPage", "1");
            ReqData.put("fileName", "01.pdf");

            resp = post(ReqData, "post");
            JSONObject jsonObject = JSON.parseObject(resp);
            String data = jsonObject.getString("data");
            JSONObject jsondata = JSON.parseObject(data);
            String signFileB64 = jsondata.getString("signFileB64");
            IRSAPIRequest.base64StringToPdf(signFileB64, "D:\\test21.pdf");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }


    public static String post(JSONObject data, String requestMethod) throws Exception {

        //计算irs请求头里面参数信息
        DateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
        dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
        String date = dateFormat.format(new Date());
        URL url = new URL(apiUrl);
        URI uri = new URI(url.getProtocol(), url.getHost(), url.getPath(), url.getQuery(), null);
        String canonicalQueryString = getCanonicalQueryString(uri.getQuery());
        String message = requestMethod.toUpperCase() + "\n" + uri.getPath() + "\n" + canonicalQueryString + "\n" + accessKey + "\n" + date + "\n";
        Mac hasher = Mac.getInstance("HmacSHA256");
        hasher.init(new SecretKeySpec(secretKey.getBytes(), "HmacSHA256"));
        byte[] hash = hasher.doFinal(message.getBytes());
        DatatypeConverter.printHexBinary(hash);
        String sign = DatatypeConverter.printBase64Binary(hash);
            /*byte[] stream1 = message.toString().getBytes("UTF-8");
            String sign = sign1(stream1);*/

        // 计算电子印章组件signature值
        String myData = data.toString();
        System.out.println(myData);
        byte[] stream = data.toString().getBytes("UTF-8");
        // 签名数据,根据签名算法,对请求数据进行签名
        String signature = sign(stream);
        //System.out.println(signature);
        // 设置HTTP请求头
        HttpEntityEnclosingRequestBase req = new HttpPost(apiUrl);
        // project-id为用户的projectId
        req.addHeader("appId", ProjectID);
        // signature为之前生成的签名
        req.addHeader("signature", signature);
        req.addHeader("X-BG-HMAC-SIGNATURE", sign);
        req.addHeader("X-BG-HMAC-ALGORITHM", "hmac-sha256");
        req.addHeader("X-BG-HMAC-ACCESS-KEY", accessKey);
        req.addHeader("X-BG-DATE-TIME", date);
        req.addHeader("Content-Type", "application/json");

        // 设置HTTP请求体
        HttpEntity entity = new ByteArrayEntity(stream, ContentType.create(ContentType.APPLICATION_JSON.getMimeType(), "UTF-8"));
        req.setEntity(entity);

        // 执行请求
        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
        CloseableHttpClient cli = httpClientBuilder.build();
        HttpResponse res = cli.execute(req);
        int statusCode = res.getStatusLine().getStatusCode();
        System.out.println(statusCode);
        if (200 != statusCode) {
            System.out.println(statusCode);
        }
        // 获取响应
        InputStream in = res.getEntity().getContent();

        byte[] resp = readStream(in);
        String strRes = new String(resp, "UTF-8");
        System.out.println(strRes);
        cli.close();
        return strRes;
    }

    private static String sign(byte[] stream) throws Exception {
        // 获取消息验证码类的实例，算法选择"HmacSHA256"
        Mac mac = Mac.getInstance("HmacSHA256");

        // 获取安全密钥
        Key secKey = new SecretKeySpec(ProjectSecret.getBytes("UTF-8"), mac.getAlgorithm());

        // 初始化
        mac.init(secKey);

        // 获得签名
        byte[] sign = mac.doFinal(stream);

        // 将byte[]格式的签名用binary编码转化为字符串返回
        return binaryEncode(sign);

    }

    private static String sign1(byte[] stream) throws Exception {
        // 获取消息验证码类的实例，算法选择"HmacSHA256"
        Mac mac = Mac.getInstance("HmacSHA256");

        // 获取安全密钥
        Key secKey = new SecretKeySpec(secretKey.getBytes("UTF-8"), mac.getAlgorithm());

        // 初始化
        mac.init(secKey);

        // 获得签名
        byte[] sign = mac.doFinal(stream);

        // 将byte[]格式的签名用binary编码转化为字符串返回
        return binaryEncode(sign);

    }

    public static String binaryEncode(byte[] data) {
        final char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

        StringBuilder builder = new StringBuilder();

        for (byte i : data) {
            builder.append(hexDigits[i >>> 4 & 0xf]);
            builder.append(hexDigits[i & 0xf]);
        }

        return builder.toString();
    }

    public static byte[] readStream(InputStream in) throws IOException {

        ByteArrayOutputStream output = new ByteArrayOutputStream();

        byte[] buffer = new byte[1024 * 10];
        try {

            int n = 0;
            while ((n = in.read(buffer)) != -1) {
                output.write(buffer, 0, n);
            }

            return output.toByteArray();

        } finally {
            in.close();
            output.close();
        }
    }

    /**
     * Description: 将pdf文件转换为Base64编码
     * <p>
     * //     * @param 要转的的pdf文件
     *
     * @Author fuyuwei
     * Create Date: 2015年8月3日 下午9:52:30
     */
    public static String PDFToBase64(File file) {
        BASE64Encoder encoder = new BASE64Encoder();
        FileInputStream fin = null;
        BufferedInputStream bin = null;
        ByteArrayOutputStream baos = null;
        BufferedOutputStream bout = null;
        try {
            fin = new FileInputStream(file);
            bin = new BufferedInputStream(fin);
            baos = new ByteArrayOutputStream();
            bout = new BufferedOutputStream(baos);
            byte[] buffer = new byte[1024];
            int len = bin.read(buffer);
            while (len != -1) {
                bout.write(buffer, 0, len);
                len = bin.read(buffer);
            }
            //刷新此输出流并强制写出所有缓冲的输出字节
            bout.flush();
            byte[] bytes = baos.toByteArray();
            return Base64.encode(bytes);
            //return encoder.encodeBuffer(bytes);

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fin.close();
                bin.close();
                bout.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * Description: 将base64编码内容转换为Pdf
     * <p>
     * //     * @param base64编码内容，文件的存储路径（含文件名）
     *
     * @Author fuyuwei
     * Create Date: 2015年7月30日 上午9:40:23
     */
    public static void base64StringToPdf(String base64Content, String filePath) {
        BASE64Decoder decoder = new BASE64Decoder();
        BufferedInputStream bis = null;
        FileOutputStream fos = null;
        BufferedOutputStream bos = null;

        try {
            byte[] bytes = decoder.decodeBuffer(base64Content);//base64编码内容转换为字节数组
            ByteArrayInputStream byteInputStream = new ByteArrayInputStream(bytes);
            bis = new BufferedInputStream(byteInputStream);
            File file = new File(filePath);
            File path = file.getParentFile();
            if (!path.exists()) {
                path.mkdirs();
            }
            fos = new FileOutputStream(file);
            bos = new BufferedOutputStream(fos);

            byte[] buffer = new byte[1024];
            int length = bis.read(buffer);
            while (length != -1) {
                bos.write(buffer, 0, length);
                length = bis.read(buffer);
            }
            bos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //closeStream(bis, fos, bos);
        }
    }


    private static String getCanonicalQueryString(String query) {
        if (query == null || query.trim().length() == 0) {
            return "";
        }
        List<Pair<String, String>> queryParamList = new ArrayList<>();
        String[] params = query.split("&");
        for (String param : params) {
            int eqIndex = param.indexOf("=");
            String key = param.substring(0, eqIndex);
            String value = param.substring(eqIndex + 1);
            Pair<String, String> pair = Pair.of(key, value);
            queryParamList.add(pair);
        }

        List<Pair<String, String>> sortedParamList = queryParamList.stream().sorted(Comparator.comparing(param -> param.getKey() + "=" + Optional.ofNullable(param.getValue()).orElse(""))).collect(Collectors.toList());
        List<Pair<String, String>> encodeParamList = new ArrayList<>();
        sortedParamList.stream().forEach(param -> {
            try {
                String key = URLEncoder.encode(param.getKey(), "utf-8");
                String value = URLEncoder.encode(Optional.ofNullable(param.getValue()).orElse(""), "utf-8").replaceAll("\\%2B", "%20").replaceAll("\\+", "%20").replaceAll("\\%21", "!").replaceAll("\\%27", "'").replaceAll("\\%28", "(").replaceAll("\\%29", ")").replaceAll("\\%7E", "~").replaceAll("\\%25", "%");
                encodeParamList.add(Pair.of(key, value));
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException("encoding error");
            }
        });
        StringBuilder queryParamString = new StringBuilder(64);
        for (Pair<String, String> encodeParam : encodeParamList) {
            queryParamString.append(encodeParam.getKey()).append("=").append(Optional.ofNullable(encodeParam.getValue()).orElse(""));
            queryParamString.append("&");
        }

        return queryParamString.substring(0, queryParamString.length() - 1);
    }
}
