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

import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.List;
import java.util.stream.Collectors;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.trade.common.constant.HttpStatus;
import com.trade.common.exception.ServiceException;
import com.trade.service.contract.util.esign.entity.*;
import com.trade.service.filestorage.util.FileUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.util.CollectionUtils;

/**
 * e签宝请求工具类
 */
public class ESignRequest {

    private static final Logger logger = LogManager.getLogger(ESignRequest.class);

    /**
     * 创建个人账号 说明：
     * <p>
     * 对接方调用本接口在 e 签宝平台中创建个人账号，生成的accountId请妥善保管,后续有关该用户的所有 操作都需使用该用户的 accountId。<br>
     * 如提供用户证件信息，则将根据提供的用户证件 信息申请数字证书<br>
     * <p>
     */
    public static String createPersonAcctId(ESignPersonAccount personAccount) {
        String param = JSON.toJSONString(personAccount);
        JSONObject result = ESignTools.doCommHttp(RequestType.POST, ESignConfig.createPerAcc_URL(), param);
        int errCode = result.getInteger("code");
        String personalAccountId = "";
        if (0 != errCode) {
            throw new ServiceException(
                    MessageFormat.format("创建个人账号失败,失败信息：errCode = {0}, msg = {1}", errCode, result.get("message")),
                    HttpStatus.ERROR);
        } else {
            JSONObject obj = ESignJSONHelper.castDataJson(result, JSONObject.class);
            personalAccountId = obj.getString("accountId");
            logger.info("创建个人账户成功！" + result);
        }
        return personalAccountId;
    }

    /**
     * 个人账号修改【 按照账号 ID】 说明：
     * <p>
     * 对接方在 e 签宝平台中使用账号 id 更新个人账号，如变更姓 名，则系统根据变更后的身份信息自动申请新数字证书， 如账号已实名则会变更为未 实名状态。
     * <p>
     * 
     * @return
     */
    public static void updatePersonAcctByAcctId(ESignPersonAccountUpdate personUpdate) {
        String param = JSON.toJSONString(personUpdate);
        JSONObject result = ESignTools.doCommHttp(RequestType.PUT,
                ESignConfig.modifyPerAccById_URL(personUpdate.getAccountId()), param);
        int code = result.getInteger("code");
        if (0 != code) {
            throw new ServiceException(MessageFormat.format("个人账号修改【 按照账号 ID】修改失败,失败信息：errCode = {0}, msg = {1}", code,
                    result.get("message")), HttpStatus.ERROR);
        }
    }

    /**
     * 查询个人账户（按照第三方用户ID查询） </br>
     * 接口描述: 通过接口(/v1/accounts/createByThirdPartyUserId)创建的账号，当对接方创建账户时传入第三方平台的userId时，支持使用第三方平台的userId查询用户的账号。
     *
     * @param thirdId 创建账户时传入的第三方用户id
     * @return
     */
    public static ESignPersonAccountResponse qryPersonAcctByThirdId(String thirdId) {
        JSONObject result = ESignTools.doCommHttp(RequestType.GET, ESignConfig.queryAccByThirdId_URL(thirdId), null);
        int code = result.getInteger("code");
        Object obj = result.get("data");
        if (0 != code || null == obj) {
            logger.info(MessageFormat.format("查询个人账号【按照第三方用户 ID】失败,失败信息：errCode = {0}, msg = {1}", code,
                    result.get("message")));
            return null;
        } else {
            return JSON.parseObject(obj.toString(), ESignPersonAccountResponse.class);
        }
    }

    /**
     * 创建机构账号 说明：
     * <p>
     * 1. 对接方调用本接口在 e 签宝平台中创建机构账号，后续有关该机构的所有操作都 需使用该机构的 accountId。如提供机构证件信息，则将根据提供的机构证件信 息申请数字证书。
     * <p>
     * 2. 由于机构是一个组织实体，机构账号时需要机构创建人执行，同时如该机构创建 人完成机构实名认证，则在签署和管理操作时需要由创建人执行，故在创建机构 时需要先调用接口完成机构创建人创建，并将机构创建人的账号 Id 传入接口
     *
     * @return String 企业账户ID
     */
    public static String createOrgAcctId(ESignOrganAccount organAccount) {
        String param = JSON.toJSONString(organAccount);
        JSONObject result = ESignTools.doCommHttp(RequestType.POST, ESignConfig.createOrgAcc_URL(), param);
        int code = result.getInteger("code");
        String organAccountId;
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("创建企业账户失败,失败信息：errCode = {0}, msg = {1}", code, result.get("message")),
                    HttpStatus.ERROR);
        } else {
            JSONObject obj = ESignJSONHelper.castDataJson(result, JSONObject.class);
            organAccountId = obj.getString("orgId");
            logger.info("创建企业账户成功！" + result);
        }
        return organAccountId;
    }

    /**
     * 机构账号修改【按照账号 ID】 说明：
     * <p>
     * 对接方调用本接口在 e 签宝平台中使用账号 id 更新机构账号，如变更名 称后，则系统根据变更后的信息自动申请新数字证书， 如账号已实名则会变更为未实 名状态。
     */
    public static void updateOrgAcctByOrgId(ESignOrganAccountUpdate organAccountUpdate) {
        String param = JSON.toJSONString(organAccountUpdate);
        JSONObject result = ESignTools.doCommHttp(RequestType.PUT,
                ESignConfig.modifyOrgAccById_URL(organAccountUpdate.getOrgId()), param);
        int code = result.getInteger("code");
        if (0 != code) {
            throw new ServiceException(MessageFormat.format("构账号修改【按照账号 ID】修改失败,失败信息：errCode = {0}, msg = {1}", code,
                    result.get("message")), HttpStatus.ERROR);
        }
    }

    /**
     * 查询机构账号（按照第三方机构ID查询）
     * <p>
     * 接口描述：通过2.2.1接口创建的账号，当对接方创建账户时传入第三方平台机构Id，支持使用第三方平台机构Id查询机构的账号。
     * 
     * @param thirdId 第三方平台机构id
     */
    public static ESignOrganAccountResponse qryOrgAcctByThirdId(String thirdId) {
        JSONObject result = ESignTools.doCommHttp(RequestType.GET, ESignConfig.queryOrgAccByThirdId_URL(thirdId), null);
        int code = result.getInteger("code");
        Object obj = result.get("data");
        if (0 != code || null == obj) {
            logger.info(MessageFormat.format("查询机构账号【按照第三方机构 ID】失败,失败信息：errCode = {0}, msg = {1}", code,
                    result.get("message")));
            return null;
        } else {
            return JSON.parseObject(obj.toString(), ESignOrganAccountResponse.class);
        }
    }

    /**
     * 通过上传方式创建文件 说明:
     * <p>
     * 为隔离业务数据流和文件流，e 签宝采用文件直传的方式完成用户文件的上传。对 接方通过该方法获取文件上传的授权地址
     * <p>
     * 对接方直接使用本地待签文件进行签署时，需先调用该接口完成本地文件上传 e 签宝
     * <p>
     * 对接方通过接口创建文件模板时，需要先调用该接口完成模板文件上传 e 签宝
     * <p>
     * 目前仅支持文件格式: pdf 组装参数：
     * <p>
     * 
     * @return JSONObject
     */
    public static JSONObject createFileByUpload(String filePath, String fileName, String accountId) {
        String param = ESignTools.getUploadUrlParam(filePath, fileName, accountId);
        JSONObject result = ESignTools.doCommHttp(RequestType.POST, ESignConfig.fileUpload_URL(), param);
        int code = result.getInteger("code");
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("通过上传方式创建文件,失败信息：errCode = {0}, msg = {1}", code, result.get("message")),
                    HttpStatus.ERROR);
        } else {
            return ESignJSONHelper.castDataJson(result, JSONObject.class);
        }
    }


    public static JSONObject createFileByUpload(InputStream inputStream, String fileName, long fileSize) {
        String param = ESignTools.getUploadUrlParam(inputStream, fileName, fileSize);
        JSONObject result = ESignTools.doCommHttp(RequestType.POST, ESignConfig.fileUpload_URL(), param);
        int code = result.getInteger("code");
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("通过上传方式创建文件,失败信息：errCode = {0}, msg = {1}", code, result.get("message")),
                    HttpStatus.ERROR);
        } else {
            return ESignJSONHelper.castDataJson(result, JSONObject.class);
        }
    }

    /**
     * 文件流上传文件 说明：
     * <p>
     * 要注意正确设置文件的contentMd5、文件MIME以及字节流等信息，否则会导致Http状态为400的异常
     * 
     * @param filePath 文件路径
     * @param uploadUrl 上传方式创建文件时返回的uploadUrl
     */
    public static void streamUpload(String filePath, String uploadUrl) {
        byte[] bytes = ESignTools.getBytes(filePath);
        // String contentMd5 = FileHelper.getContentMD5(filePath);
        String contentMd5 = ESignTools.getContentMD5(filePath);
        // String conentType = FileHelper.getContentType(filePath);
        String conentType = "application/pdf";
        JSONObject result = ESignTools.doUploadHttp(RequestType.PUT, uploadUrl, bytes, contentMd5, conentType);
        int code = result.getInteger("errCode");
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("文件流上传文件文件,失败信息：errCode = {0}, msg = {1}", code, result.get("message")),
                    HttpStatus.ERROR);
        }
    }


    public static void streamUpload(InputStream inputStream, String uploadUrl) {
        byte[] bytes;
        // input stream 转换为 byte[]
        try {
            inputStream.reset();
            // inputStream 是ByteArrayInputStream，每次读取后指针会移动，需要reset
            bytes = FileUtil.toByteArray(inputStream);
            inputStream.reset();
        } catch (IOException e) {
            e.printStackTrace();
            throw new ServiceException("文件转换失败", HttpStatus.ERROR);
        }

        String contentMd5 = ESignTools.getContentMD5(inputStream);
        JSONObject result = ESignTools.doUploadHttp(RequestType.PUT, uploadUrl, bytes, contentMd5, "application/pdf");
        int code = result.getInteger("errCode");
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("文件流上传文件文件,失败信息：errCode = {0}, msg = {1}", code, result.get("message")),
                    HttpStatus.ERROR);
        }
    }

    /**
     * 创建签署流程 组装参数：
     * <p>
     * {@see ESignContractProcess#process}
     * 
     * @return flowId
     */
    public static String creatESignFlow(ESignContractProcess process) {
        String param = JSON.toJSONString(process);
        JSONObject result = ESignTools.doCommHttp(RequestType.POST, ESignConfig.createFlows_URL(), param);
        int code = result.getInteger("code");
        String flowId;
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("创建签署流程失败,失败信息：errCode = {0}, msg = {1}", code, result.get("message")),
                    HttpStatus.ERROR);
        }
        JSONObject obj = ESignJSONHelper.castDataJson(result, JSONObject.class);
        flowId = obj.getString("flowId");
        logger.info("创建签署流程成功！" + result);
        return flowId;
    }

    /**
     * 签署流程归档 说明：
     * <p>
     * 手动归档签署流程，归档后所有资源均不可修改。归档前签署流程中的所有签署人必须都签署完成。如创建流程时设置了自动归档，则无需调用本接口，签署完成后系统会自动调用
     * 
     * @param flowId 创建签署流程时返回的签署流程ID
     */
    public static void archivESignFlow(String flowId) {
        // 因e签宝处理间隙调用归档导致的报错，此处增加4秒的线程等待 add by 20221209
        // {"code":1437113,"message":"签署区没有全部完成","data":null}
        try {
            Thread.sleep(1000 * 4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        JSONObject result = ESignTools.doCommHttp(RequestType.PUT, ESignConfig.archiveFlows_URL(flowId), null);
        int code = result.getInteger("code");
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("签署流程归档失败,失败信息：errCode = {0}, msg = {1}", code, result.get("message")),
                    HttpStatus.ERROR);
        } else {
            // JSONObject obj = ESignJSONHelper.castDataJson(result, JSONObject.class);
            logger.info("签署流程归档成功！" + result);
        }
    }

    /**
     * 流程文档添加 说明：
     * <p>
     * 向流程中添加待签署文档，文档必须先用文档管理接口创建，创建方式请参见文件管理接口文档。已经开启的流程不能再添加签署文档. 组装参数：
     * <p>
     * 
     * @param flowId 创建签署流程时返回的签署流程ID
     * @author 宫清
     * @date 2019年7月21日 下午5:44:33
     */
    public static void addFlowDoc(String flowId, List<ESignContractDoc> docList) {
        if (null == docList || docList.size() == 0) {
            throw new ServiceException(MessageFormat.format("流程文档添加失败：errCode = {0}, msg = {1}", "文档ID为空"));
        }
        JSONObject outterjson = new JSONObject();
        JSONArray jarr = JSONArray.parseArray(JSON.toJSONString(docList));
        outterjson.put("docs", jarr);
        String param = JSON.toJSONString(outterjson);
        JSONObject result = ESignTools.doCommHttp(RequestType.POST, ESignConfig.aboutDocument_URL(flowId, null), param);
        int code = result.getInteger("code");
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("流程文档添加失败,失败信息：errCode = {0}, msg = {1}", code, result.get("message")),
                    HttpStatus.ERROR);
        } else {
            logger.info("流程文档添加成功！" + result);
        }
    }

    /**
     * 流程附件添加 接口描述：
     * <p>
     * 为流程添加附件, 附件必须先用文档管理接口创建，附件无需签署，只作为签署过程中的参考，比如录音、视频, 图片, 文档等。
     * </p>
     *
     * @param flowId 创建签署流程时返回的签署流程ID
     * @param attachmentList 流程附件信息
     */
    public static void addFlowAttachment(String flowId, List<ESignContractAttachment> attachmentList) {
        if (CollectionUtils.isEmpty(attachmentList)) {
            throw new ServiceException(MessageFormat.format("流程附件添加失败：errCode = {0}, msg = {1}", "流程附件为空"));
        }
        JSONObject outterjson = new JSONObject();
        JSONArray jarr = JSONArray.parseArray(JSON.toJSONString(attachmentList));
        outterjson.put("attachments", jarr);
        String param = JSON.toJSONString(outterjson);
        JSONObject result = ESignTools.doCommHttp(RequestType.POST, ESignConfig.aboutAttachment_URL(flowId), param);
        int code = result.getInteger("code");
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("流程附件添加失败,失败信息：errCode = {0}, msg = {1}", code, result.get("message")),
                    HttpStatus.ERROR);
        } else {
            logger.info("流程附件添加成功！" + result);
        }
    }

    /**
     * 流程文档下载 说明：
     * <p>
     * 流程归档后，查询和下载签署后的文件
     * 
     * @param flowId 创建签署流程时返回的签署流程ID
     * @param fileIds 文档id列表,多个id使用英文“，”分隔
     */
    public static List<ESignFileDownload> downloadFlowDoc(String flowId, String fileIds) {
        JSONObject result = ESignTools.doCommHttp(RequestType.GET, ESignConfig.aboutDocument_URL(flowId, fileIds),
                null);
        List<ESignFileDownload> fileList = null;
        int code = result.getInteger("code");
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("流程文档下载失败,失败信息：errCode = {0}, msg = {1}", code, result.get("message")),
                    HttpStatus.ERROR);
        } else {
            JSONObject obj = ESignJSONHelper.castDataJson(result, JSONObject.class);
            Object objDocs = obj.get("docs");
            JSONArray jarr = JSONArray.parseArray(objDocs.toString());
            fileList = jarr.toJavaList(ESignFileDownload.class);
            logger.info("**************流程文档下载 objDocs*********" + objDocs);
            logger.info("流程文档下载成功！" + result);
            return fileList;
        }
    }

    /**
     * 添加平台自动盖章签署区 说明：
     * <p>
     * 向指定流程中创建签署区，每个签署区视为一个任务，系统会自动按照流程流转。 签署区的添加必须在签署文档添加之后, 签署区信息内部包含签署文档信息（平台自动签无需指定签署人信息，默认签署人是对接的企业）。
     * 签署区创建完成，流程开启后，系统将自动完成“对接平台自动盖章签署区”的盖章，对接平台可全程无感完成本次签署。
     * <p>
     * 
     * @param flowId 创建签署流程时返回的签署流程ID
     * @param ESignDoclist
     * @return
     * @date 2019年7月21日 下午8:09:07
     */
    public static JSONObject addPlatformAutoSignArea(String flowId, List<ESignDoc> ESignDoclist) {
        // String param = ESignSignParamUtil.addPlatformSignAreaParam(fileIds, sealIds);
        JSONArray jarr = JSONArray.parseArray(JSON.toJSONString(ESignDoclist));
        JSONObject json = new JSONObject();
        json.put("signfields", jarr);
        String param = json.toString();
        JSONObject result = ESignTools.doCommHttp(RequestType.POST,
                ESignConfig.addAutoSignfieldsForPlatform_URL(flowId), param);
        int code = result.getInteger("code");
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("添加平台自动盖章签署区失败,失败信息：errCode = {0}, msg = {1}", code, result.get("message")),
                    HttpStatus.ERROR);
        } else {
            System.out.println("添加平台自动盖章签署区成功！" + result);
            return ESignJSONHelper.castDataJson(result, JSONObject.class);
        }
    }

    /**
     * 创建机构模板印章 说明：
     * <p>
     * 通过模版参数，创建机构印章 组装参数：
     * <p>
     * {@link ESignSealParamUtil#addOrgTemplateSealParam(String, String, Integer, Integer, String, String, String, String)}
     * 
     * @param orgId 机构账号ID
     * @return
     * @throws
     * @author 宫清
     * @date 2019年7月21日 下午3:51:56
     */
    public static String createOrgTemplateSeal(String orgId, ESignOrgTemplateSeal orgTemplateseal) {
        // String param = ESignSealParamUtil.addOrgTemplateSealParam(alias, color, height, width, htext, qtext, type,
        // central);
        String param = JSON.toJSONString(orgTemplateseal);
        JSONObject result = ESignTools.doCommHttp(RequestType.POST, ESignConfig.createOfficialSeal_URL(orgId), param);
        int code = result.getInteger("code");
        String orgSealId = "";
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("创建机构模板印章失败,失败信息：errCode = {0}, msg = {1}", code, result.get("message")),
                    HttpStatus.ERROR);
        } else {
            System.out.println("创建机构模板印章成功！" + result);
            JSONObject json = ESignJSONHelper.castDataJson(result, JSONObject.class);
            orgSealId = json.getString("sealId");
        }
        return orgSealId;
    }

    /**
     * 创建个人模板印章 说明：
     * <p>
     * 通过模版参数，创建个人印章 组装参数：
     * <p>
     * {@link ESignSealParamUtil#addPersonTemplateSealParam(String, String, Integer, Integer, String)}
     * 
     * @param accountId 用户账号ID
     * @return
     * @author 宫清
     * @date 2019年7月21日 下午3:42:42
     */
    public static String createPersonTemplateSeal(String accountId, String alias, String color, Integer height,
            Integer width, String type) {
        String param = ESignSealParamUtil.addPersonTemplateSealParam(alias, color, height, width, type);
        JSONObject result = ESignTools.doCommHttp(RequestType.POST, ESignConfig.createPerSeal_URL(accountId), param);
        int code = result.getInteger("code");
        String personSealId = "";
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("创建个人模板印章失败,失败信息：errCode = {0}, msg = {1}", code, result.get("message")),
                    HttpStatus.ERROR);
        } else {
            System.out.println("创建个人模板印章成功！" + result);
            JSONObject json = ESignJSONHelper.castDataJson(result, JSONObject.class);
            personSealId = json.getString("sealId");
        }
        return personSealId;
    }

    /**
     * 创建个人/机构图片印章 组装参数：
     * <p>
     * 
     * @param accountId 个人/机构账号ID
     * @return
     * @throws
     * @author 宫清
     * @date 2019年7月21日 下午4:02:50
     */
    public static JSONObject createPicSeal(String accountId, String alias, Integer height, Integer width, String type,
            String data, Boolean transparentFlag) {
        String param = ESignSealParamUtil.addPicSealParam(alias, height, width, type, data, transparentFlag);
        JSONObject result = ESignTools.doCommHttp(RequestType.POST, ESignConfig.createImageSeal_URL(accountId), param);
        int code = result.getInteger("code");
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("创建个人/机构图片印章失败,失败信息：errCode = {0}, msg = {1}", code, result.get("message")),
                    HttpStatus.ERROR);
        } else {
            System.out.println("创建个人/机构图片印章成功！" + result);
            JSONObject json = ESignJSONHelper.castDataJson(result, JSONObject.class);
            return json;
        }
    }

    /**
     * 添加签署方自动盖章签署区 说明：
     * <p>
     * 接口描述 <br/>
     * （1）向指定流程中创建签署区，每个签署区视为一个任务，系统会自动按照流程流转。 签署区的添加必须在签署文档添加之后,
     * 签署区信息内部包含签署人、签署文档信息。签署区创建完成，流程开启后，系统将自动完成“用户自动盖章签署区”的盖章。用户可全程无感完成本次签署。创建签署方自动盖章签署区前，需确定已完成账号静默签署授权 。
     * （2）签署方自动盖章的合同不符合电子签名法中对可靠的要求，仅适用于对法律效力要求不高的场景，或由对接平台方自行校验真实身份和真实意愿。
     * （3）静默签署对于签署方来说是无感知的，需要在签署前，由平台方与签署方签订授权协议，确保法律流程完善（线下线上签都可以，确保法律效力即可）（授权协议可参考https://qianxiaoxia.yuque.com/docs/share/cf2ff0e1-04d3-4bc3-8459-631613848490）。
     * （4）静默签署在出证时，无法直接从官网申请，需要申请人工出证（需要人工出证费用）。 <br/>
     * （5）添加签署方自动盖章签署区接口适用于签署方用户添加签署区，不适用于平台方添加签署区。
     * <p>
     * 
     * @param flowId 创建签署流程时返回的签署流程ID
     * @param ESignDocList 签署区列表数据
     * @date 2019年7月21日 下午8:09:07
     */
    public static JSONObject addSignerAutoSignArea(String flowId, List<ESignDoc> ESignDocList) {
        // String param = ESignSignParamUtil.addSignerAutoSignAreaParam(fileIds, authorizedAccountIds, sealIds);
        JSONArray jarr = JSONArray.parseArray(JSON.toJSONString(ESignDocList));
        JSONObject json = new JSONObject();
        json.put("signfields", jarr);
        String param = json.toString();
        JSONObject result = ESignTools.doCommHttp(RequestType.POST, ESignConfig.addAutoSignfieldsForPerson_URL(flowId),
                param);
        int code = result.getInteger("code");
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("添加签署方自动盖章签署区失败,失败信息：errCode = {0}, msg = {1}", code, result.get("message")),
                    HttpStatus.ERROR);
        } else {
            System.out.println("添加签署方自动盖章签署区成功！" + result);
            JSONObject job = ESignJSONHelper.castDataJson(result, JSONObject.class);
            return job;
        }
    }

    /**
     * 添加签署方手动盖章签署区 说明：
     * <p>
     * 向指定流程中创建签署区，每个签署区视为一个任务，系统会自动按照流程流转。 签署区的添加必须在签署文档添加之后, 签署区信息内部包含签署文档信息.
     * 签署区创建完成，流程开启后，可获取用户手动签署链接，通过此链接可打开文件签署页面，进行人工确认签署 组装参数：
     * <p>
     * 
     * @param flowId 创建签署流程时返回的签署流程ID
     */
    public static JSONObject addSignerHandSignArea(String flowId, List<ESignDoc> ESignSignfieldList) {
        // String param = ESignSignParamUtil.addSignerHandSignAreaParam(fileIds, signerAccountIds,
        // authorizedAccountIds);
        JSONArray jarr = JSONArray.parseArray(JSON.toJSONString(ESignSignfieldList));
        JSONObject json = new JSONObject();
        json.put("signfields", jarr);
        String param = json.toString();
        JSONObject result = ESignTools.doCommHttp(RequestType.POST, ESignConfig.addHandSignfieldsForPerson_URL(flowId),
                param);
        int code = result.getInteger("code");
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("添加签署方手动盖章签署区失败,失败信息：errCode = {0}, msg = {1}", code, result.get("message")),
                    HttpStatus.ERROR);
        } else {
            logger.info("添加签署方手动盖章签署区成功！" + result);
            return ESignJSONHelper.castDataJson(result, JSONObject.class);
        }
    }

    /**
     * 签署流程开启 说明：
     * <p>
     * 开启签署流程，开启后流程文档不可再添加或修改文档，签署任务会自动按照设置开始流转。流程开启后，归档前，可随时追加签署区（指定签署人的签署信息）
     * 
     * @param flowId 创建签署流程时返回的签署流程ID
     * @author 宫清
     * @date 2019年7月21日 下午5:35:48
     */
    public static void startSignFlow(String flowId) {
        JSONObject result = ESignTools.doCommHttp(RequestType.PUT, ESignConfig.startFlows_URL(flowId), null);
        int code = result.getInteger("code");
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("签署流程开启失败,失败信息：errCode = {0}, msg = {1}", code, result.get("message")),
                    HttpStatus.ERROR);
        } else {
            logger.info("签署流程开启成功！" + result);
        }
    }

    /**
     * 签署流程撤销 说明：
     * <p>
     * 撤销签署流程，撤销后流程中止，所有签署无效
     * 
     * @param flowId 创建签署流程时返回的签署流程ID
     * @param revokeReason 撤销原因,默认"撤销"
     */
    public static void revokESignFlow(String flowId, String revokeReason) {
        JSONObject json = new JSONObject();
        json.put("revokeReason", revokeReason);
        String param = json.toString();
        JSONObject result = ESignTools.doCommHttp(RequestType.PUT, ESignConfig.revokeFlows_URL(flowId), param);
        int code = result.getInteger("code");
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("签署流程撤销失败,失败信息：errCode = {0}, msg = {1}", code, result.get("message")),
                    HttpStatus.ERROR);
        } else {
            System.out.println("签署流程撤销成功！" + result);
        }
    }

    /**
     * 设置静默签署 组装参数：
     * <p>
     * 
     * @param accountId 授权人 id，即个人账 号 id 或机构账号 id
     * @param deadLine 授权截止时间, 格式为 yyyy-MM-dd HH:mm:ss
     * @author 宫清
     * @date 2019年7月20日 下午6:37:37
     */
    public static void setAutoSign(String accountId, String deadLine) {
        JSONObject json = new JSONObject();
        json.put("deadline", deadLine);
        String param = json.toString();
        JSONObject result = ESignTools.doCommHttp(RequestType.POST, ESignConfig.signAuth_URL(accountId), param);
        int code = result.getInteger("code");
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("设置静默签署失败,失败信息：errCode = {0}, msg = {1}", code, result.get("message")),
                    HttpStatus.ERROR);
        } else {
            ESignJSONHelper.castDataJson(result, Object.class);
            System.out.println("设置静默签署成功！" + result);
        }
    }

    /**
     * 搜索关键字坐标
     *
     * @param flowId 流程id
     * @param fileId 文档id
     * @param keywords 关键字列表，逗号分割 void
     */
    public static ESignDocPosition searchWordsPosition(String flowId, String fileId, String keywords) {
        JSONObject result = ESignTools.doCommHttp(RequestType.GET,
                ESignConfig.getKeywordsPosition_URL(flowId, fileId, keywords), null);
        int code = result.getInteger("code");
        // 页码
        int pageIndex = 1;
        Float posx = 500f;
        Float posy = 100f;
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("搜索关键字坐标失败,失败信息：errCode = {0}, msg = {1}", code, result.get("message")),
                    HttpStatus.ERROR);
        } else {
            JSONArray jarr = JSONArray.parseArray(result.get("data").toString());
            List<ESignKeyword> positionList = jarr.toJavaList(ESignKeyword.class);
            if (null != positionList && positionList.size() > 0) {
                ESignKeyword keyword = positionList.get(0);
                if (null != keyword.getPositionList() && keyword.getPositionList().size() > 0) {
                    ESignKeywordPosition position = keyword.getPositionList().get(0);
                    pageIndex = position.getPageIndex();
                    ESignKeywordCoordinate coordinate = position.getCoordinateList().get(0);
                    if (null != coordinate) {
                        posx = coordinate.getPosx();
                        posy = coordinate.getPosy();
                    }
                }
            }
            ESignDocPosition position = new ESignDocPosition(String.valueOf(pageIndex), posx, posy);
            logger.info("搜索关键字坐标成功！" + result);
            return position;
        }
    }

    /**
     * 获取签署地址 说明：
     * <p>
     * 流程开启后，获取指定签署人的签署链接地址，如仅传入签署人账号id，则获取的签署任务链接仅包含本人的签署任务；如同时签署人账号id+机构id，则获取的签署任务链接包含企业与个人的签署任务
     * 
     * @param flowId 创建签署流程时返回的签署流程ID
     * @param accountId 签署人账号id, 可指定, 默认用当前登录人账号id
     * @param organizeId 机构Id，传入本参数后，获取当前操作人代签企业的签署任务,默认空
     * @param urlType 链接类型: 0-签署链接;1-预览链接;默认0
     * @return
     */
    public static ESignUrlResponse qrySignUrl(String flowId, String accountId, String organizeId, String urlType) {
        JSONObject json = ESignTools.doCommHttp(RequestType.GET,
                ESignConfig.sign_URL(flowId, accountId, organizeId, urlType), null);
        JSONObject obj = ESignJSONHelper.castDataJson(json, JSONObject.class);
        int code = json.getInteger("code");
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("获取签署地址失败,失败信息：errCode = {0}, msg = {1}", code, obj.get("message")),
                    HttpStatus.ERROR);
        } else {
            return JSON.parseObject(obj.toString(), ESignUrlResponse.class);
        }
    }

    /**
     * 获取活体照片
     *
     * @param param 请求参数
     */
    public static String getFacePhotoUrl(String param) {
        JSONObject json = ESignTools.doCommHttp(RequestType.POST, ESignConfig.identityDetail_URL(), param);
        int code = json.getInteger("code");
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("获取签署中意愿/实名认证信息,失败信息：errCode = {0}, msg = {1}", code, json.get("message")),
                    HttpStatus.ERROR);
        } else {
            JSONArray dataJsonArray = json.getJSONArray("data");
            if (dataJsonArray != null && dataJsonArray.size() > 0) {
                for (int i = 0; i < dataJsonArray.size(); i++) {
                    JSONObject jsonObject = dataJsonArray.getJSONObject(i);
                    if (jsonObject != null) {
                        String identityType = jsonObject.getString("identityType");// 认证类型
                        // 只取意愿认证&实名认证的腾讯云刷脸
                        if (ESignConstant.FACE_TECENT_CLOUD_H5.equals(identityType)
                                || ESignConstant.FACEAUTH_TECENT_CLOUD.equals(identityType)) {
                            String identityDetail = jsonObject.getString("identityDetail");
                            if (identityDetail != null) {
                                logger.info("identityType:" + identityType + ",identityDetail:" + identityDetail);
                                ESignIdentity ESignIdentity = JSON.parseObject(identityDetail, ESignIdentity.class);
                                if (ESignIdentity != null && ESignIdentity.getFacePhotoUrl() != null) {
                                    logger.info("获取认证照片完成，photoUrl={}", ESignIdentity.getFacePhotoUrl());
                                    return ESignIdentity.getFacePhotoUrl();
                                }
                            }
                        }
                    }
                }
            }
        }
        return null;
    }

    /**
     * 根据签署流程查询账号认证状态
     * 
     * @param flowId 签署流程id
     * @param accountId 账户id
     * @return 实名认证流程ID
     */
    public static String getIdentityAuthId(String flowId, String accountId) {
        logger.info("调用e签宝查询账号认证状态,accountId={}", accountId);
        JSONObject json = ESignTools.doCommHttp(RequestType.GET, ESignConfig.getIdentityStatus(flowId, accountId),
                null);
        int code = json.getInteger("code");
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("查询账号认证状态,失败信息：errCode = {0}, msg = {1}", code, json.get("message")),
                    HttpStatus.ERROR);
        } else {
            logger.info("根据签署流程查询账号认证状态返回结果：{}", json.toString());
            JSONObject data = json.getJSONObject("data");
            if (null != data) {
                JSONArray realNameRecord = data.getJSONArray("realNameRecord");
                if (null != realNameRecord && !realNameRecord.isEmpty()) {
                    List<ESignRealNameRecord> realNameRecordList = realNameRecord.toJavaList(ESignRealNameRecord.class);
                    realNameRecordList = realNameRecordList.stream().filter(e -> 1 == e.getStatus())
                            .collect(Collectors.toList());
                    if (!realNameRecordList.isEmpty()) {
                        return realNameRecordList.get(0).getAuthId();// 返回任意一条认证成功的数据
                    } else {
                        logger.info("调用e签宝查询账号认证状态未查询到有效的实名认证记录");
                    }
                }
            }
        }
        return null;
    }

    /**
     * 查询认证状态
     * 
     * @param authId 认证流程id
     * @return String
     */
    public static String getIdentityStatusByAuthId(String authId) {
        JSONObject json = ESignTools.doCommHttp(RequestType.GET, ESignConfig.getIdentityInfo(authId), null);
        int code = json.getInteger("code");
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("查询账号认证状态,失败信息：errCode = {0}, msg = {1}", code, json.get("message")),
                    HttpStatus.ERROR);
        } else {
            logger.info("查询认证信息接口返回结果：{}", json.toString());
            JSONObject data = json.getJSONObject("data");
            if (null != data) {
                return data.getString("status");
            }
        }
        return null;
    }

    /**
     * 注销机构签署账号
     * 
     * @param orgId 机构账号id
     * @return boolean
     */
    public static boolean deleteOrganization(String orgId) {
        JSONObject json = ESignTools.doCommHttp(RequestType.DELETE, ESignConfig.deleteOrganization(orgId), null);
        int code = json.getInteger("code");
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("调用注销机构签署账号接口失败,失败信息：errCode = {0}, msg = {1}", code, json.get("message")),
                    HttpStatus.ERROR);
        }
        return true;
    }

    /**
     * 个人账号实名认证
     *
     * @param accountId 个人账号id
     * @return boolean
     */
    public static ESignIndividualIdentityRes individualIdentity(String accountId) {
        JSONObject json = new JSONObject();
        json.put("faceauthMode", ESignConstant.FACE_AUTH_MODE_ZHIMA);
        json.put("callbackUrl", "");
        json.put("notifyUrl", ESignConstant.IDENTITY_CALL_BACK_URL);
        String param = json.toString();
        JSONObject result = ESignTools.doCommHttp(RequestType.POST, ESignConfig.individualIdentity_URL(accountId),
                param);
        JSONObject data = ESignJSONHelper.castDataJson(result, JSONObject.class);
        int code = result.getInteger("code");
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("调用个人账号实名认证接口失败,失败信息：errCode = {0}, msg = {1}", code, json.get("message")),
                    HttpStatus.ERROR);
        } else {
            return JSON.parseObject(data.toString(), ESignIndividualIdentityRes.class);
        }
    }

    /**
     * 企业账号实名认证（三要素）
     *
     * @param enterpriseId 企业账号id
     * @param agentAccountId 办理人账号id
     * @return boolean
     */
    public static ESignEnterpriseIdentityRes enterpriseIdentity(String enterpriseId, String agentAccountId) {
        JSONObject json = new JSONObject();
        json.put("agentAccountId", agentAccountId);// 办理人账号Id，要求办理人账号ID已完成个人实名认证
        json.put("notifyUrl", ESignConstant.IDENTITY_CALL_BACK_URL);
        String param = json.toString();
        JSONObject result = ESignTools.doCommHttp(RequestType.POST,
                ESignConfig.organizationIdentityThree_URL(enterpriseId), param);
        JSONObject data = ESignJSONHelper.castDataJson(result, JSONObject.class);
        int code = result.getInteger("code");
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("调用个人账号实名认证接口失败,失败信息：errCode = {0}, msg = {1}", code, result.get("message")),
                    HttpStatus.ERROR);
        } else {
            return JSON.parseObject(data.toString(), ESignEnterpriseIdentityRes.class);
        }
    }

    /**
     * 企业发起反向打款认证
     *
     * @param flowId 企业实名认证流程id
     * @return boolean
     */
    public static ESignReversePaymentRes enterpriseReversePayment(String flowId) {
        JSONObject result = ESignTools.doCommHttp(RequestType.POST, ESignConfig.organizationReversePayment_URL(flowId),
                null);
        JSONObject data = ESignJSONHelper.castDataJson(result, JSONObject.class);
        int code = result.getInteger("code");
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("调用反向打款认证接口失败,失败信息：errCode = {0}, msg = {1}", code, result.get("message")),
                    HttpStatus.ERROR);
        } else {
            return JSON.parseObject(data.toString(), ESignReversePaymentRes.class);
        }
    }

    /**
     * 【反向打款认证】查询打款进度
     *
     * @param flowId 企业实名认证流程id
     * @return boolean
     */
    public static ESignReversePaymentProcess queryReversePaymentProcess(String flowId) {
        JSONObject result = ESignTools.doCommHttp(RequestType.GET, ESignConfig.reversePaymentProcess_URL(flowId), null);
        JSONObject data = ESignJSONHelper.castDataJson(result, JSONObject.class);
        int code = result.getInteger("code");
        if (0 != code) {
            throw new ServiceException(
                    MessageFormat.format("查询打款进度失败,失败信息：errCode = {0}, msg = {1}", code, result.get("message")),
                    HttpStatus.ERROR);
        } else {
            return JSON.parseObject(data.toString(), ESignReversePaymentProcess.class);
        }
    }

    /**
     * 个人手机三要素认证
     * 
     * @param idNo 身份证号
     * @param name 姓名
     * @param mobileNo 手机号
     * @return 认证id
     */
    public static String verifyIndividualTel(String idNo, String name, String mobileNo) {
        ESignTokenHelper.getTokenData();
        JSONObject json = new JSONObject();
        json.put("idNo", idNo.trim());
        json.put("name", name.trim());
        json.put("mobileNo", mobileNo.trim());
        String param = json.toString();
        JSONObject result = ESignTools.doCommHttp(RequestType.POST, ESignConfig.individualTelecom3Factors_URL(), param);
        JSONObject data = ESignJSONHelper.castDataJson(result, JSONObject.class);
        int code = result.getInteger("code");
        if (0 != code) {
            logger.warn(MessageFormat.format("个人手机三要素认证接口,失败信息：errCode = {0}, msg = {1}", code, result.get("message")));
            return null;
        } else {
            return data.getString("verifyId");
        }
    }
}
