package com.trade.service.contract.util.esign;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import com.alibaba.fastjson2.JSONObject;
import com.trade.common.redis.RedisCache;
import com.trade.common.utils.SpringUtils;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * e签宝电子合同_工具类
 */
public class ESignTools {

    private static final Logger log = LoggerFactory.getLogger(ESignTools.class);

    private static class RedisCachesHolder {

        private static final RedisCache instance = SpringUtils.getBean(RedisCache.class);
    }

    public static RedisCache getRedisCache() {
        return RedisCachesHolder.instance;
    }

    /***
     * POST请求的头部信息
     * 
     * @param projectId
     * @param ContentType
     * @param encoding
     * @return
     */
    public static LinkedHashMap<String, String> getPOSTHeaders(String projectId, String projectSecret,
            String ContentType, String encoding) {
        LinkedHashMap<String, String> headers = new LinkedHashMap<String, String>();
        headers.put("X-Tsign-Open-App-Id", projectId);
        headers.put("X-Tsign-Open-App-Secret", projectSecret);
        headers.put("X-timevale-mode", "package");
        headers.put("Content-Type", ContentType);
        headers.put("Charset", encoding);
        return headers;
    }

    /***
     * 向指定URL发送POST方法的请求
     * 
     * @param apiUrl
     * @param data
     * @param encoding
     * @return
     */
    public static JSONObject sendPOST(String apiUrl, String data, LinkedHashMap<String, String> headers,
            String encoding) {
        StringBuffer strBuffer = null;
        JSONObject obj = null;
        try {
            // 建立连接
            URL url = new URL(apiUrl);
            HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
            // 需要输出
            httpURLConnection.setDoOutput(true);
            // 需要输入
            httpURLConnection.setDoInput(true);
            // 不允许缓存
            httpURLConnection.setUseCaches(false);
            httpURLConnection.setRequestMethod("POST");
            // 设置Headers
            if (null != headers) {
                for (String key : headers.keySet()) {
                    httpURLConnection.setRequestProperty(key, headers.get(key));
                }
            }
            // 连接会话
            httpURLConnection.connect();
            // 建立输入流，向指向的URL传入参数
            DataOutputStream dos = new DataOutputStream(httpURLConnection.getOutputStream());
            // 设置请求参数
            dos.write(data.getBytes(encoding));
            dos.flush();
            dos.close();
            // 获得响应状态
            int http_StatusCode = httpURLConnection.getResponseCode();
            String http_ResponseMessage = httpURLConnection.getResponseMessage();
            obj = new JSONObject();
            if (HttpURLConnection.HTTP_OK == http_StatusCode) {
                strBuffer = new StringBuffer();
                String readLine = new String();
                BufferedReader responseReader = new BufferedReader(
                        new InputStreamReader(httpURLConnection.getInputStream(), "UTF-8"));
                while ((readLine = responseReader.readLine()) != null) {
                    strBuffer.append(readLine);
                }
                responseReader.close();
                // System.out.println("http_StatusCode = " + http_StatusCode + "
                // request_Parameter = " + data);
                obj = toJSONObject(strBuffer.toString());
            } else {
                throw new RuntimeException(
                        MessageFormat.format("请求失败,失败原因: Http状态码 = {0} , {1}", http_StatusCode, http_ResponseMessage));
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return obj;
    }

    /***
     * PUT请求的头部信息
     *
     * @param ContentMD5
     * @param ContentType
     * @param encoding
     * @return
     */
    public static LinkedHashMap<String, String> getPUTHeaders(String ContentMD5, String ContentType, String encoding) {
        LinkedHashMap<String, String> headers = new LinkedHashMap<String, String>();
        headers.put("Content-MD5", ContentMD5);
        headers.put("Content-Type", ContentType);
        headers.put("Charset", encoding);
        return headers;
    }

    /***
     * 向指定URL发送PUT方法的请求
     *
     * @return
     */
    public static int sendPUT(String fileUploadUrl, String filePath, LinkedHashMap<String, String> headers) {
        StringBuffer strBuffer = null;
        // 是否上传成功
        int uploadSuccessCode = 0;
        try {
            // 建立连接
            URL url = new URL(fileUploadUrl);
            HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
            // 需要输出
            httpURLConnection.setDoOutput(true);
            // 需要输入
            httpURLConnection.setDoInput(true);
            // 不允许缓存
            httpURLConnection.setUseCaches(false);
            httpURLConnection.setRequestMethod("PUT");
            // 设置Headers
            if (null != headers) {
                for (String key : headers.keySet()) {
                    httpURLConnection.setRequestProperty(key, headers.get(key));
                }
            }
            // 连接会话
            httpURLConnection.connect();
            // 建立输入流，向指向的URL传入参数
            DataOutputStream dos = new DataOutputStream(httpURLConnection.getOutputStream());
            // 设置请求参数
            dos.write(getBytes(filePath));
            dos.flush();
            dos.close();
            // 获得响应状态
            int http_StatusCode = httpURLConnection.getResponseCode();
            String http_ResponseMessage = httpURLConnection.getResponseMessage();
            if (HttpURLConnection.HTTP_OK == http_StatusCode) {
                strBuffer = new StringBuffer();
                String readLine = new String();
                BufferedReader responseReader = new BufferedReader(
                        new InputStreamReader(httpURLConnection.getInputStream(), "UTF-8"));
                while ((readLine = responseReader.readLine()) != null) {
                    strBuffer.append(readLine);
                }
                responseReader.close();
                uploadSuccessCode = http_StatusCode;
            } else {
                throw new RuntimeException(
                        MessageFormat.format("文件上传失败,失败信息:Http状态码 = {0} , {1}", http_StatusCode, http_ResponseMessage));
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return uploadSuccessCode;
    }

    /***
     * 将JSON字符串转成JSON对象
     * 
     * @param str
     * @return JSON对象
     */
    public static JSONObject toJSONObject(String str) {
        JSONObject obj = JSONObject.parseObject(str);
        return obj;
    }

    public static String setFileJSONStr1(String filePath) {
        JSONObject obj = new JSONObject();
        obj.put("fileName", "测试合同_test.pdf");// 文件名称
        obj.put("fileSize", "15123");// 文件大小，单位字节
        obj.put("contentType", "application/pdf");// 文件内容的MIME 类型描述字符串
        obj.put("contentMd5", getContentMD5(filePath)); // 采用BASE64 编码的文件MD5 摘要值
        return obj.toString();
    }

    /***
     * 设置文件直传地址请求参数
     *
     * @return
     */
    public static String setFileJSONStr(String filePath) {
        File file = new File(filePath);
        JSONObject obj = new JSONObject();
        obj.put("fileName", getFileName(filePath));// 文件名称
        obj.put("fileSize", file.length());// 文件大小，单位字节
        obj.put("contentType", "application/pdf");// 文件内容的MIME 类型描述字符串
        obj.put("contentMd5", getContentMD5(filePath)); // 采用BASE64 编码的文件MD5 摘要值
        return obj.toString();
    }

    public static String getFileName(String filePath) {
        return filePath.substring(filePath.lastIndexOf("\\") + 1);
    }

    /***
     * 计算文件的Content-MD5
     * 
     * @param filePath
     * @return
     */
    public static String getContentMD5(String filePath) {
        // 获取文件MD5的二进制数组（128位）
        byte[] bytes = getFileMD5Bytes128(filePath);
        // 对文件MD5的二进制数组进行base64编码（而不是对32位的16进制字符串进行编码）
        return new String(Base64.encodeBase64(bytes));
    }

    /***
     * 获取文件MD5-二进制数组（128位）
     * 
     * @param filePath
     * @return
     * @throws IOException
     */
    public static byte[] getFileMD5Bytes128(String filePath) {
        FileInputStream fis = null;
        byte[] md5Bytes = null;
        try {
            File file = new File(filePath);
            fis = new FileInputStream(file);
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            byte[] buffer = new byte[1024];
            int length = -1;
            while ((length = fis.read(buffer, 0, 1024)) != -1) {
                md5.update(buffer, 0, length);
            }
            md5Bytes = md5.digest();
            fis.close();
        } catch (FileNotFoundException e) {
            log.error(e.getMessage());
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            log.error(e.getMessage());
            e.printStackTrace();
        } catch (IOException e) {
            log.error(e.getMessage());
            e.printStackTrace();
        }
        return md5Bytes;
    }

    /***
     * 计算文件的Content-MD5
     *
     * @param inputStream 文件输入流
     * @return String
     */
    public static String getContentMD5(InputStream inputStream) {
        // 获取文件MD5的二进制数组（128位）
        byte[] bytes = getFileMD5Bytes128(inputStream);
        // 对文件MD5的二进制数组进行base64编码（而不是对32位的16进制字符串进行编码）
        return new String(Base64.encodeBase64(bytes));
    }

    /***
     * 获取文件MD5-二进制数组（128位）
     *
     * @return byte[]
     */
    public static byte[] getFileMD5Bytes128(InputStream inputStream) {
        byte[] md5Bytes = null;
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            byte[] buffer = new byte[1024];
            int length;
            while ((length = inputStream.read(buffer, 0, 1024)) != -1) {
                md5.update(buffer, 0, length);
            }
            md5Bytes = md5.digest();
        } catch (IOException | NoSuchAlgorithmException e) {
            log.error(e.getMessage());
            e.printStackTrace();
        }
        return md5Bytes;
    }

    /**
     * 设置合同模板创建合同请求参数
     * 
     * @param fileKey 文件标识，由获取文件直传地址返回的filekey
     * @return
     */
    public static String setContractByLocalJSONStr(String fileKey) {
        JSONObject obj = new JSONObject();
        obj.put("fileKey", fileKey);
        return obj.toString();
    }

    /**
     * 设置归档流程请求参数
     * 
     * @param flowId 签署流程id，由创建流程接口的调用返回的flowId
     * @return
     */
    public static String setFlowIdJSONStr(String flowId) {
        JSONObject obj = new JSONObject();
        obj.put("flowId", flowId);
        return obj.toString();
    }

    /*******************************************************************************************************
     * e签宝签署电子文件新版
     *********************************************************************************************************/
    /***
     * 获取文件的Bytes
     * 
     * @param filePath
     * @return
     */
    public static byte[] getBytes(String filePath) {
        File file = new File(filePath);
        FileInputStream fis = null;
        byte[] buffer = null;
        try {
            fis = new FileInputStream(file);
            buffer = new byte[(int) file.length()];
            fis.read(buffer);
            fis.close();
        } catch (FileNotFoundException e) {
            log.error("获取文件二进制字节流异常:" + e.getMessage());
            e.printStackTrace();
        } catch (IOException e) {
            log.error("获取文件二进制字节流异常:" + e.getMessage());
            e.printStackTrace();
        } finally {
            try {
                fis.close();
            } catch (IOException e) {
                log.error("文件二进制字节流关闭时发生异常：" + e.getMessage());
                e.printStackTrace();
            }
        }
        return buffer;
    }

    /**
     * 创建常规请求头
     * 
     * @param
     */
    private static Map<String, String> buildCommHeader() {
        Map<String, String> header = new HashMap<>();
        header.put("X-Tsign-Open-App-Id", ESignConfig.PROJECT_ID_V2);
        RedisCache redisCache = getRedisCache();
        String token = redisCache.getCacheObject(ESignConstant.TOKEN);
        header.put("X-Tsign-Open-Token", token);
        header.put("Content-Type", "application/json");
        return header;
    }

    /**
     * 发送常规HTTP 请求
     * 
     * @param reqType 请求方式
     * @param url 请求路径
     * @param paramStr 请求参数
     * @return
     */
    public static JSONObject doCommHttp(RequestType reqType, String url, String paramStr) {
        return ESignHttpCfgHelper.sendHttp(reqType, url, buildCommHeader(), paramStr);
    }

    /**
     * 发送文件流上传 HTTP 请求
     * 
     * @param reqType 请求方式
     * @param url 请求路径
     * @param param 请求参数
     * @param contentMd5 文件contentMd5
     * @param contentType 文件MIME类型
     * @return
     */
    public static JSONObject doUploadHttp(RequestType reqType, String url, byte[] param, String contentMd5,
            String contentType) {
        return ESignHttpCfgHelper.sendHttp(reqType, url, buildUploadHeader(contentMd5, contentType), param);
    }

    /**
     * 创建文件流上传 请求头
     * 
     * @param contentMd5
     * @param contentType
     * @return
     */
    private static Map<String, String> buildUploadHeader(String contentMd5, String contentType) {
        Map<String, String> header = new HashMap<>();
        header.put("Content-MD5", contentMd5);
        header.put("Content-Type", contentType);
        return header;
    }

    /**
     * 通过上传文件方式创建文件【本地文件上传 / 模板文件上传】，仅支持 ".pdf" 文件 参数 待填充参数：
     * <p>
     * contentMd5：先计算文件md5值，再对该md5值进行base64编码【必填】
     * <p>
     * contentType：目标文件的MIME类型【必填】
     * <p>
     * fileName：文件名称（必须带上文件扩展名，必然会导致后续发起流程校验过不去，例如："合同.pdf" ）【必填】
     * <p>
     * fileSize:文件大小【必填】
     * <p>
     * accountId：所属账号 id，即个人账号 id 或 机构账号 id，如不传，则默认属 于对接平台【可空】
     * 
     * @param filePath {@link String} 文件路径
     * @param accountId {@link String} 账号Id
     * @return
     */
    public static String getUploadUrlParam(String filePath, String fileName, String accountId) {
        File file = new File(filePath);
        // if (!file.exists()) {
        // throw new RuntimeException(
        // MessageFormat.format("通过上传文件方式创建文件,失败信息：errCode = {0}, msg = {1}", "1", "文件不存在"));
        // }
        JSONObject json = new JSONObject();
        json.put("contentMd5", getContentMD5(filePath));
        json.put("contentType", "application/pdf");
        json.put("fileName", fileName);
        json.put("fileSize", String.valueOf(file.length()));
        json.put("accountId", accountId);
        return json.toString();
    }

    /**
     * 通过上传文件方式创建文件【本地文件上传 / 模板文件上传】，仅支持 ".pdf" 文件 参数 待填充参数：
     * <p>
     * contentMd5：先计算文件md5值，再对该md5值进行base64编码【必填】
     * <p>
     * contentType：目标文件的MIME类型【必填】
     * <p>
     * fileName：文件名称（必须带上文件扩展名，必然会导致后续发起流程校验过不去，例如："合同.pdf" ）【必填】
     * <p>
     * fileSize:文件大小【必填】
     * <p>
     * 
     * @return String
     */
    public static String getUploadUrlParam(InputStream inputStream, String fileName, long fileSize) {
        JSONObject json = new JSONObject();
        json.put("contentMd5", getContentMD5(inputStream));
        json.put("contentType", "application/pdf");
        json.put("fileName", fileName);
        json.put("fileSize", String.valueOf(fileSize));
        return json.toString();
    }
}
