package com.hz.pm.api.irs.sign;

import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hz.pm.api.irs.config.IrsSealPlatformProperties;
import com.hz.pm.api.todocenter.model.dto.SignReqDTO;
import com.ningdatech.basic.exception.BizException;
import com.ningdatech.basic.util.StrPool;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
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.HttpClients;
import sun.misc.BASE64Decoder;

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.nio.charset.StandardCharsets;
import java.security.Key;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public class IRSAPIRequest {

    /**
     * pdf文件盖章
     * 接口地址：/V1/accounts/outerAccounts/create
     *
     * @return
     */
    public static String createSignPdf(SignReqDTO req) {
        String pdfEncode64 = req.getPdfEncode64();
        Float posX = req.getPosX();
        Float posY = req.getPosY();
        String sealSn = req.getSealSn();
        String fileName = req.getFileName();
        String posPage = req.getPosPage();
        Integer signType = req.getSignType();
        String resp;
        try {
            JSONObject ReqData = new JSONObject();
            ReqData.put("fileBase64", pdfEncode64);
            ReqData.put("sealSn", sealSn);
            ReqData.put("posX", posX);
            ReqData.put("posY", posY);
            ReqData.put("signType", signType);
            ReqData.put("posPage", posPage);
            ReqData.put("fileName", fileName);
            resp = post(ReqData, "post");
        } catch (Exception e) {
            throw new BizException("调用IRS盖章接口失败,印章编号为：" + sealSn + StrPool.SEMICOLON + e.getMessage());
        }
        // return obj;
        JSONObject jsonObject = JSON.parseObject(resp, JSONObject.class);
        String data = jsonObject.getString("data");
        JSONObject object = JSON.parseObject(data, JSONObject.class);
        // 获取盖好章的PDF文件内容Base64字符串
        return object.getString("signFileB64");
    }


    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(IrsSealPlatformProperties.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" + IrsSealPlatformProperties.accessKey + "\n" + date + "\n";
        Mac hasher = Mac.getInstance("HmacSHA256");
        hasher.init(new SecretKeySpec(IrsSealPlatformProperties.secretKey.getBytes(), "HmacSHA256"));
        byte[] hash = hasher.doFinal(message.getBytes());
        DatatypeConverter.printHexBinary(hash);
        String sign = DatatypeConverter.printBase64Binary(hash);

        // 计算电子印章组件signature值
        byte[] stream = data.toString().getBytes(StandardCharsets.UTF_8);
        // 签名数据,根据签名算法,对请求数据进行签名
        String signature = sign(stream);
        // 设置HTTP请求头
        HttpEntityEnclosingRequestBase req = new HttpPost(IrsSealPlatformProperties.apiUrl);
        // project-id为用户的projectId
        req.addHeader("appId", IrsSealPlatformProperties.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", IrsSealPlatformProperties.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);

        // 执行请求
        // 自定义 RequestConfig 设置请求超时时间
        RequestConfig requestConfig = RequestConfig.custom()
                // 连接超时时间，单位：毫秒
                .setConnectTimeout(5000)
                // 连接请求超时时间，单位：毫秒
                .setConnectionRequestTimeout(5000)
                // 数据读取超时时间，单位：毫秒
                .setSocketTimeout(5000)
                .build();
        // 通过 HttpClientBuilder 的 custom() 方法创建自定义的 HttpClient
        CloseableHttpClient customHttpClient = HttpClients.custom()
                .setDefaultRequestConfig(requestConfig)
                .build();
        HttpResponse res = customHttpClient.execute(req);
        int statusCode = res.getStatusLine().getStatusCode();
        log.info(String.valueOf(statusCode));
        if (200 != statusCode) {
            log.info(String.valueOf(statusCode));
        }
        // 获取响应
        InputStream in = res.getEntity().getContent();

        byte[] resp = readStream(in);
        String strRes = new String(resp, StandardCharsets.UTF_8);
        log.info(strRes);
        customHttpClient.close();
        return strRes;
    }

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

        // 获取安全密钥
        Key secKey = new SecretKeySpec(IrsSealPlatformProperties.projectSecret.getBytes(StandardCharsets.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(IrsSealPlatformProperties.secretKey.getBytes(StandardCharsets.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;
            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) {
        try (FileInputStream fin = new FileInputStream(file);
             BufferedInputStream bin = new BufferedInputStream(fin);
             ByteArrayOutputStream bos = new ByteArrayOutputStream();
             BufferedOutputStream bout = new BufferedOutputStream(bos);) {
            byte[] buffer = new byte[1024 * 16];
            int len = bin.read(buffer);
            while (len != -1) {
                bout.write(buffer, 0, len);
                len = bin.read(buffer);
            }
            //刷新此输出流并强制写出所有缓冲的输出字节
            bout.flush();
            byte[] bytes = bos.toByteArray();
            return String.valueOf(Base64.getEncoder().encode(bytes));
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

    /**
     * Description: 将base64编码内容转换为Pdf
     * <p>
     *
     * @param base64Content 文件的存储路径（含文件名）
     */
    public static void base64StringToPdf(String base64Content, String filePath) throws IOException {
        BASE64Decoder decoder = new BASE64Decoder();
        byte[] bytes = decoder.decodeBuffer(base64Content);
        File file = new File(filePath);
        FileUtil.mkParentDirs(file);
        try (ByteArrayInputStream byteInputStream = new ByteArrayInputStream(bytes);
             BufferedInputStream bis = new BufferedInputStream(byteInputStream);
             FileOutputStream fos = new FileOutputStream(file);
             BufferedOutputStream bos = new BufferedOutputStream(fos)) {
            byte[] buffer = new byte[1024 * 16];
            int length = bis.read(buffer);
            while (length != -1) {
                bos.write(buffer, 0, length);
                length = bis.read(buffer);
            }
            bos.flush();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }


    private static String getCanonicalQueryString(String query) {
        if (query == null || query.trim().isEmpty()) {
            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.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 BizException("encoding error");
            }
        });

        StringBuilder queryParamString = new StringBuilder();
        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);
    }

}
