package com.kingdee.eas.custom.esign.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.kingdee.bos.BOSException;
import com.kingdee.bos.Context;
import com.kingdee.bos.dao.IObjectPK;
import com.kingdee.bos.dao.ormapping.ObjectUuidPK;
import com.kingdee.bos.util.BOSUuid;
import com.kingdee.eas.basedata.person.PersonInfo;
import com.kingdee.eas.custom.esign.*;
import com.kingdee.eas.custom.esign.bizEnum.EsignConfigEnum;
import com.kingdee.eas.custom.esign.bizEnum.EsignStatusEnum;
import com.kingdee.eas.custom.esign.bizEnum.SendStatusEnum;
import com.kingdee.eas.custom.esign.tsign.hz.comm.EsignHttpHelper;
import com.kingdee.eas.custom.esign.tsign.hz.comm.EsignHttpResponse;
import com.kingdee.eas.custom.esign.tsign.hz.enums.EsignRequestType;
import com.kingdee.eas.custom.esign.tsign.hz.exception.EsignException;
import com.kingdee.eas.mobileaccess.hr.sHR.config.EASConfig;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.utils.URIBuilder;

import java.net.URI;
import java.net.URISyntaxException;
import java.text.MessageFormat;
import java.util.List;
import java.util.Locale;
import java.util.Map;

/**
 * description: EsignHttpUtil <br>
 * date: 17/11/2025 下午 4:26 <br>
 * author: lhbj <br>
 * version: 1.0 <br>
 */
public class EsignHttpUtil {
    /**
     * 基础请求接口
     *
     * @param url         请求地址
     * @param jsonParm    JSON字符串参数
     * @param requestType 请求类型 POST/GET
     * @param debug       输出日志
     * @param sourceId    来源id
     * @return
     * @throws EsignException
     */
    public static EsignHttpResponse doCommHttp(Context ctx, String url, String jsonParm, EsignRequestType requestType, Boolean debug, String sourceId, EsignConfigEnum configEnum) throws EsignException {

        //
        //生成签名鉴权方式的的header
        Map<String, String> header = EsignHttpHelper.signAndBuildSignAndJsonHeader(EsignConfig.getInstance().getEsignAppId(),
                EsignConfig.getInstance().getEsignAppSecret(), jsonParm, requestType.name(), url, debug);
        EsignHttpResponse response = EsignHttpHelper.doCommHttp(EsignConfig.getInstance().getEsignHost(), url, requestType, jsonParm, header, debug);
        try {
            if (null == ctx) {
               return response;
            }
            if (null == ctx) {
                String currentSolutionName = EASConfig.getInstance().getSlnName();
                String currentDatabaseCenter = EASConfig.getInstance().getDcCode();
                ctx = new Context(new ObjectUuidPK(), currentSolutionName, currentDatabaseCenter, new Locale("l2"));
            }
            EsignLogInfo esignLogInfo = new EsignLogInfo();
            IEsignLog log = EsignLogFactory.getLocalInstance(ctx);
            esignLogInfo.setEsiginName(configEnum);
            esignLogInfo.setRequestState(String.valueOf(response.getStatus()));
            esignLogInfo.setUrl(url);
            esignLogInfo.setRequestData(jsonParm);
            esignLogInfo.setResponseData(response.getBody());
            esignLogInfo.setSource(sourceId);
            IObjectPK pk = log.addnew(esignLogInfo);
            response.setLogId(pk.toString());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        }
        //发起接口请求
        return response;
    }

    /**
     * POST请求接口
     *
     * @param url      请求地址
     * @param jsonParm JSON字符串参数
     * @param debug    输出日志
     * @param sourceId 来源id
     * @return
     * @throws EsignException
     */
    public static EsignHttpResponse POST(Context ctx, String url, String jsonParm, Boolean debug, String sourceId, EsignConfigEnum configEnum) throws EsignException {
        //请求方法
        EsignRequestType requestType = EsignRequestType.POST;
        return doCommHttp(ctx, url, jsonParm, requestType, debug, sourceId, configEnum);
    }

    public static EsignHttpResponse POST(Context ctx, String url, String jsonParm, String sourceId, EsignConfigEnum configEnum) throws EsignException {
        return POST(ctx, url, jsonParm, false, sourceId, configEnum);
    }

    /**
     * GET请求接口
     *
     * @param url      请求地址
     * @param jsonParm JSON字符串参数
     * @param debug    输出日志
     * @param sourceId 来源id
     * @return
     * @throws EsignException
     */
    public static EsignHttpResponse GET(Context ctx, String url, String jsonParm, Boolean debug, String sourceId, EsignConfigEnum configEnum) throws EsignException {
        //请求方法
        EsignRequestType requestType = EsignRequestType.GET;
        return doCommHttp(ctx, url, jsonParm, requestType, debug, sourceId, configEnum);
    }

    public static EsignHttpResponse GET(Context ctx, String url, String jsonParm, String sourceId, EsignConfigEnum configEnum) throws EsignException {
        return GET(ctx, url, jsonParm, false, sourceId, configEnum);
    }

    public static EsignHttpResponse DELETE(Context ctx, String url, String jsonParm, String sourceId, EsignConfigEnum configEnum) throws EsignException {
        return DELETE(ctx, url, jsonParm, false, sourceId, configEnum);
    }

    public static EsignHttpResponse DELETE(Context ctx, String url, String jsonParm, Boolean debug, String sourceId, EsignConfigEnum configEnum) throws EsignException {
        //请求方法
        EsignRequestType requestType = EsignRequestType.DELETE;
        return doCommHttp(ctx, url, jsonParm, requestType, debug, sourceId, configEnum);
    }

    public static EsignHttpResponse getDocTemplatesDetailById(Context ctx, String id) throws EsignException {

        return getDocTemplatesDetailById(ctx, id, null);
    }

    /**
     * 查询合同模板中控件详情
     * 参考文档：https://open.esign.cn/doc/opendoc/pdf-sign3/aoq509
     * 接口地址：https://{host}/v3/doc-templates/{docTemplateId}
     * 请求方法：GET
     *
     * @param id       模板id
     * @param sourceId 来源id
     * @return
     * @throws EsignException
     */
    public static EsignHttpResponse getDocTemplatesDetailById(Context ctx, String id, String sourceId) throws EsignException {
        EsignConfigEnum configEnum = EsignConfigEnum.docTemplatesDetailById;
        String apiaddr = EsignConfig.getInstance().get(configEnum.getValue());
        apiaddr = MessageFormat.format(apiaddr, id);
        String jsonParm = null;
        return GET(ctx, apiaddr, jsonParm, sourceId, configEnum);
    }

    /**
     * 填写模板生成文件
     * 参考文档：https://open.esign.cn/doc/opendoc/pdf-sign3/mv8a3i
     * 接口地址：https://{host}/v3/files/create-by-doc-template
     * 请求方法：POST
     * 注意事项：componentId与componentKey两个字段不能同时传值，只能一个有值
     *
     * @param json
     * @param sourceId 来源id
     * @return
     * @throws EsignException
     */
    public static EsignHttpResponse createByDocTemplate(Context ctx, String json, String sourceId) throws EsignException {
        EsignConfigEnum configEnum = EsignConfigEnum.createByDocTemplate;
        String apiaddr = EsignConfig.getInstance().get(configEnum.getValue());
        return POST(ctx, apiaddr, json, sourceId, configEnum);
    }


    /**
     * 查询机构认证信息
     * 参考文档：https://open.esign.cn/doc/opendoc/auth3/xxz4tc
     * 接口地址：https://{host}/v3/organizations/identity-info
     * 请求方法：GET
     * 注意事项：
     * 入参中orgId、orgName和orgIDCardNum三个参数只选择一个传入即可查询机构认证信息。
     * 查询优先级为 orgId > orgName > orgIDCardNum。
     *
     * @param orgId
     * @param orgName
     * @param orgIDCardNum
     * @param orgIDCardType 组织机构证件类型（传orgIDCardNum时，该参数为必传）
     *                      CRED_ORG_USCC - 统一社会信用代码     *                      CRED_ORG_REGCODE - 工商注册号
     * @param sourceId      来源id
     * @return
     * @throws EsignException
     */
    public static EsignHttpResponse getOrgIdentity_info(Context ctx, String orgId, String orgName, String orgIDCardNum, String orgIDCardType, String sourceId) throws EsignException, URISyntaxException {
        EsignConfigEnum configEnum = EsignConfigEnum.organizations_identity_info;
        String apiaddr = EsignConfig.getInstance().get(configEnum.getValue());
        if (StringUtils.isBlank(orgId) && StringUtils.isBlank(orgName)) {
            if (StringUtils.isNotBlank(orgIDCardNum) && StringUtils.isBlank(orgIDCardType)) {
                throw new EsignException("传orgIDCardNum时，orgIDCardType参数为必传");
            } else {
                throw new EsignException("orgId与orgName与orgIDCardNum不能都为空");
            }
        }

        URIBuilder uriBuilder = new URIBuilder(apiaddr);

        if (StringUtils.isNotBlank(orgId)) {
            //uriBuilder.queryParam("orgId", orgId);
            uriBuilder.addParameter("orgId", orgId);
        }

        if (StringUtils.isNotBlank(orgIDCardNum) && StringUtils.isNotBlank(orgIDCardType)) {
            uriBuilder.addParameter("orgIDCardNum", orgIDCardNum);
            uriBuilder.addParameter("orgIDCardType", orgIDCardType);
        }

        URI uri =uriBuilder.build();
        apiaddr = uri.toString();
        if (StringUtils.isNotBlank(orgName)) {
            if (apiaddr.indexOf("?")>0){
                apiaddr+="&orgName="+orgName;
            }else {
                apiaddr+="?orgName="+orgName;
            }
        }
        return GET(ctx, apiaddr, null, sourceId, configEnum);
    }

    public static EsignHttpResponse getOrgIdentity_infoByOrgId(Context ctx, String orgId) throws EsignException, URISyntaxException {
        return getOrgIdentity_info(ctx, orgId, null, null, null, null);
    }

    public static EsignHttpResponse getOrgIdentity_infoByOrgName(Context ctx, String orgName) throws EsignException, URISyntaxException {
        return getOrgIdentity_info(ctx, null, orgName, null, null, null);
    }

    public static EsignHttpResponse getOrgIdentity_infoByOrgIDCardNum(Context ctx, String orgIDCardNum, String orgIDCardType) throws EsignException, URISyntaxException {
        return getOrgIdentity_info(ctx, null, null, orgIDCardNum, orgIDCardType, null);
    }

    /**
     * 查询个人认证信息
     * 参考文档：https://open.esign.cn/doc/opendoc/auth3/xxz4tc
     * 接口地址：https://{host}/v3/persons/identity-info
     * 请求方法：GET
     * 注意事项：
     * 入参中psnId、psnAccount和psnIDCardNum三个参数只选择一个传入即可查询个人的认证信息。
     * 查询优先级为 psnId > psnAccount > psnIDCardNum
     *
     * @param psnId
     * @param psnAccount
     * @param psnIDCardNum
     * @param psnIDCardType 个人证件号类型 （传psnIDCardNum时，证件类型为必传项）
     *                      CRED_PSN_CH_IDCARD - 中国大陆居民身份证
     *                      CRED_PSN_CH_HONGKONG - 香港来往大陆通行证
     *                      CRED_PSN_CH_MACAO - 澳门来往大陆通行证
     *                      CRED_PSN_CH_TWCARD - 台湾来往大陆通行证
     *                      CRED_PSN_PASSPORT - 护照
     * @param sourceId      来源id
     * @return
     * @throws EsignException
     */
    public static EsignHttpResponse getPersonIdentity_info(Context ctx, String psnId, String psnAccount, String psnIDCardNum, String psnIDCardType, String sourceId) throws EsignException, URISyntaxException {
        EsignConfigEnum configEnum = EsignConfigEnum.persons_identity_info;
        String apiaddr = EsignConfig.getInstance().get(configEnum.getValue());
        if (StringUtils.isBlank(psnId) && StringUtils.isBlank(psnAccount)) {
            if (StringUtils.isNotBlank(psnIDCardNum) && StringUtils.isBlank(psnIDCardType)) {
                throw new EsignException("传orgIDCardNum时，psnIDCardType参数为必传");
            } else {
                throw new EsignException("orgId与orgName与orgIDCardNum不能都为空");
            }
        }
        URIBuilder uriBuilder = new URIBuilder(apiaddr);
        if (StringUtils.isNotBlank(psnId)) {
            uriBuilder.addParameter("psnId", psnId);
        }
        if (StringUtils.isNotBlank(psnAccount)) {
            uriBuilder.addParameter("psnAccount", psnAccount);
        }
        if (StringUtils.isNotBlank(psnIDCardNum) && StringUtils.isNotBlank(psnIDCardType)) {
            uriBuilder.addParameter("psnIDCardNum", psnIDCardNum);
            uriBuilder.addParameter("psnIDCardType", psnIDCardType);
        }
        URI uri = uriBuilder.build(); // 自动编码
        apiaddr = uri.toString();
        return GET(ctx, apiaddr, null, sourceId, configEnum);
    }

    public static EsignHttpResponse getPersonIdentity_infoByPsnId(Context ctx, String psnId) throws EsignException, URISyntaxException {
        return getPersonIdentity_info(ctx, psnId, null, null, null, null);
    }

    public static EsignHttpResponse getPersonIdentity_infoByPsnAccount(Context ctx, String psnAccount) throws EsignException, URISyntaxException {
        return getPersonIdentity_info(ctx, null, psnAccount, null, null, null);
    }

    public static EsignHttpResponse getPersonIdentity_infoByOrgIDCardNum(Context ctx, String psnIDCardNum, String psnIDCardType) throws EsignException, URISyntaxException {
        return getPersonIdentity_info(ctx, null, null, psnIDCardNum, psnIDCardType, null);
    }

    /**
     * 查询签署流程详情
     * 参考文档：https://open.esign.cn/doc/opendoc/pdf-sign3/xxk4q6
     * 接口地址：https://{host}/v3/sign-flow/{signFlowId}/detail
     * 请求方法：GET
     * 注意事项：
     *
     * @param signFlowId 签署流程ID
     * @param sourceId   来源id
     * @return
     */
    public static EsignHttpResponse getSign_fields(Context ctx, String signFlowId, String sourceId) throws EsignException {
        EsignConfigEnum configEnum = EsignConfigEnum.detail_sign_fields;
        String apiaddr = EsignConfig.getInstance().get(configEnum.getValue());
        apiaddr = MessageFormat.format(apiaddr, signFlowId);
        return GET(ctx, apiaddr, null, sourceId, configEnum);
    }

    public static EsignHttpResponse getSign_fields(Context ctx, String signFlowId) throws EsignException {
        return getSign_fields(ctx, signFlowId, null);
    }

    /**
     * 追加签署区
     * 参考文档：https://open.esign.cn/doc/opendoc/pdf-sign3/ohzup7
     * 接口地址：https://{host}/v3/sign-flow/{signFlowId}/signers/sign-fields
     * 请求方法：POST
     * 注意事项：在追加一个签署区时，请确保流程在开启之前已添加了该签署区所在的待签署文件，参考【追加待签文件】；
     * 流程在“草稿”和“签署中”状态时，允许向流程中再追加签署区；
     * 【基于文件发起签署】时设置了自动完结（autoFinish为 true）的流程不支持再添加签署区。
     *
     * @param signFlowId 签署流程ID
     * @param json       添加签署方信息
     * @param sourceId   来源id
     * @return
     */
    public static EsignHttpResponse addSign_fields(Context ctx, String signFlowId, String json, String sourceId) throws EsignException {
        EsignConfigEnum configEnum = EsignConfigEnum.add_sign_fields;
        String apiaddr = EsignConfig.getInstance().get(configEnum.getValue());
        apiaddr = MessageFormat.format(apiaddr, signFlowId);
        return POST(ctx, apiaddr, json, sourceId, configEnum);
    }

    /**
     * 删除签署区
     * 参考文档：https://open.esign.cn/doc/opendoc/pdf-sign3/ohzup7
     * 接口地址：https://{host}/v3/sign-flow/{signFlowId}/signers/sign-fields
     * 请求方法：POST
     * 注意事项：在追加一个签署区时，请确保流程在开启之前已添加了该签署区所在的待签署文件，参考【追加待签文件】；
     * 流程在“草稿”和“签署中”状态时，允许向流程中再追加签署区；
     * 【基于文件发起签署】时设置了自动完结（autoFinish为 true）的流程不支持再添加签署区。
     *
     * @param signFlowId   签署流程ID
     * @param signFieldIds 添加签署方信息
     * @param sourceId     来源id
     * @return
     */
    public static EsignHttpResponse delSign_fields(Context ctx, String signFlowId, String signFieldIds, String sourceId) throws EsignException, URISyntaxException {
        EsignConfigEnum configEnum = EsignConfigEnum.del_sign_fields;
        String apiaddr = EsignConfig.getInstance().get(configEnum.getValue());
        apiaddr = MessageFormat.format(apiaddr, signFlowId);
        URIBuilder uriBuilder = new URIBuilder(apiaddr);
        if (StringUtils.isNotBlank(signFieldIds)) {
            uriBuilder.addParameter("signFieldIds", signFieldIds);
        }
        URI uri = uriBuilder.build(); // 自动编码
        apiaddr = uri.toString();
        return DELETE(ctx, apiaddr, null, sourceId, configEnum);
    }

    /**
     * 添加抄送方信息
     * 参考文档：https://open.esign.cn/doc/opendoc/pdf-sign3/ohzup7
     * 接口地址：https://{host}/v3/sign-flow/{signFlowId}/signers/sign-fields
     * 请求方法：POST
     * 注意事项：
     * “抄送方”的概念：
     * 指不参与签署文件的机构或个人，可以进行查看签署流程中的签署文件以及附属材料等信息，当流程中的签署方全部完成签署，抄送方也会收到签署完成的通知。
     * 接口注意事项：
     * 自动完结的流程（autoFinish设置为true）不支持添加抄送方，否则将会报错：“自动归档流程开启后，不允许添加签署区”。
     * 添加的抄送方不可与流程中已有的抄送方重复。。
     *
     * @param signFlowId 签署流程ID
     * @param json       添加抄送方信息
     * @param sourceId   来源id
     * @return
     */
    public static EsignHttpResponse addCopiers(Context ctx, String signFlowId, String json, String sourceId) throws EsignException {
        EsignConfigEnum configEnum = EsignConfigEnum.add_copiers;
        String apiaddr = EsignConfig.getInstance().get(configEnum.getValue());
        apiaddr = MessageFormat.format(apiaddr, signFlowId);
        return POST(ctx, apiaddr, json, sourceId, configEnum);
    }

    /**
     * 删除抄送方信息
     * 参考文档：https://open.esign.cn/doc/opendoc/pdf-sign3/bdn9yt
     * 接口地址：https://{host}/v3/sign-flow/{signFlowId}/copiers/delete
     * <p>
     * 请求方法：POST
     * 注意事项：流程若已开启，将不支持再删除抄送方信息。
     *
     * @param signFlowId 签署流程ID
     * @param json       删除抄送方信息
     * @param sourceId   来源id
     * @return
     */
    public static EsignHttpResponse delCopiers(Context ctx, String signFlowId, String json, String sourceId) throws EsignException {
        EsignConfigEnum configEnum = EsignConfigEnum.del_copiers;
        String apiaddr = EsignConfig.getInstance().get(configEnum.getValue());
        apiaddr = MessageFormat.format(apiaddr, signFlowId);
        return POST(ctx, apiaddr, json, sourceId, configEnum);
    }

    /**
     * 基于文件发起签署
     * 参考文档：https://open.esign.cn/doc/opendoc/pdf-sign3/su5g42
     * 接口地址：https://{host}/v3/sign-flow/create-by-file
     * 请求方法：POST
     * 注意事项：
     * 1. 单个签署流程中对签署文件（docs）要求如下：
     * 单个签署流程中所添加的文件个数不可超过50个。
     * 单个文件大小不可超过50MB。
     * 单个文件内单页大小不可超过20MB（文件内含图片时，需特别关注单页大小）。
     * 单个签署流程中所添加的文件大小总和不可超过500MB。
     * 2. 单个签署流程中一次性添加的签署方（signers）不要超过10个，如果超过10个后续可以用《追加签署区》接口追加，整个流程不能超过50个签署方。
     * 3. 单个签署流程中所添加的签署区（signFields）总和不要超过300个。
     * 4. 单个签署流程中对附属材料（attachments）要求如下：
     * 单个签署流程中所添加的附件个数不可超过50个。
     * 单个附件大小不可超过10MB。
     * 5. autoStart自动开启签署流程，默认值 true
     * true - 自动开启（发起签署流程，将直接进入“签署中”状态）
     * false - 非自动开启（发起“草稿”状态的签署流程，需调用【开启签署流程】接口后流程进入“签署中”状态）
     * 补充说明： 自动开启的流程不允许再追加待签署文件，点击这里了解更多流程状态说明。
     *
     * @param ctx        上下文
     * @param personId   签署员工id
     * @param fileName   签署文件名称
     * @param operatorId 操作人
     * @param efileId    发起单据id
     * @param json       签署信息
     * @return
     */
    public static EsignHttpResponse create_by_file(Context ctx, String personId, String fileName, String operatorId, String efileId,
                                                   String json) throws BOSException {
        EsignHttpResponse resp = create_by_file(ctx, personId, fileName, operatorId, efileId, EsignStatusEnum.SIGNING, json);
        return resp;
    }

    public static EsignHttpResponse create_by_file(Context ctx, String personId, String fileName, String operatorId, String efileId,
                                                   EsignStatusEnum esignStatus,
                                                   String json) throws BOSException {
        EsignConfigEnum configEnum = EsignConfigEnum.create_by_file;
        String apiaddr = EsignConfig.getInstance().get(configEnum.getValue());
        EsignHttpResponse resp = new EsignHttpResponse();
        resp.setStatus(500);

        /**
         * 发起签署前需要新增电子签全域状态总览表
         */
        IESignGlobalStatusOverview globalStatusOverview = ESignGlobalStatusOverviewFactory.getLocalInstance(ctx);
        ESignGlobalStatusOverviewInfo info = new ESignGlobalStatusOverviewInfo();
//        try {
//            String str = "select fid from CT_ESI_ESGSO where cfefileId=?";
//            IRowSet rs = DbUtil.executeQuery(ctx, str, new Object[]{efileId});
//            if (rs.next()) {
//                String fid = rs.getString("fid");
//                info = globalStatusOverview.getESignGlobalStatusOverviewInfo(new ObjectUuidPK(fid));
//            }
//        } catch (SQLException | EASBizException e) {
//            e.printStackTrace();
//        }

        info.setEsignStatus(EsignStatusEnum.DRAFT);
        info.setSendStatus(SendStatusEnum.FAILURE);
        if (StringUtils.isNotBlank(personId)) {
            PersonInfo personInfo = new PersonInfo();
            personInfo.setId(BOSUuid.read(personId));
            info.setPerson(personInfo);
        }
        if (StringUtils.isNotBlank(operatorId)) {
            PersonInfo operator = new PersonInfo();
            operator.setId(BOSUuid.read(operatorId));
            info.setOperator(operator);
        }
        info.setFileName(fileName);

        info.setEfileId(efileId);

        info.setRequestParams(json);
        info.setEsignName(configEnum);
        try {
            resp = POST(ctx, apiaddr, json, efileId, configEnum);
            if (resp.getStatus() >= 200 && resp.getStatus() < 300) {
                JSONObject object = JSON.parseObject(resp.getBody());
                if ("0".equals(String.valueOf(object.get("code")))) {
                    JSONObject data = object.getJSONObject("data");
                    info.setSignFlowId(data.getString("signFlowId"));
                    info.setSendStatus(SendStatusEnum.SUCCESS);
                    info.setEsignStatus(esignStatus);
                }
            }
            info.setSourceBillId(resp.getLogId());
        } catch (Exception e) {
            e.printStackTrace();
            info.setSendStatus(SendStatusEnum.FAILURE);
        } finally {
            try {
                globalStatusOverview.save(info);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return resp;
    }

    /**
     * 开启签署流程
     * 参考文档：https://open.esign.cn/doc/opendoc/pdf-sign3/bdn9yt
     * 接口地址：https://{host}/v3/sign-flow/{signFlowId}/start
     * <p>
     * 请求方法：POST
     * 注意事项：流程若已开启，将不支持再删除抄送方信息。
     *
     * @param signFlowId 签署流程ID
     * @param sourceId   来源id
     * @return
     */
    public static EsignHttpResponse start_by_file(Context ctx, String signFlowId, String sourceId) throws EsignException {
        EsignConfigEnum configEnum = EsignConfigEnum.start_by_file;
        String apiaddr = EsignConfig.getInstance().get(configEnum.getValue());
        apiaddr = MessageFormat.format(apiaddr, signFlowId);
        return POST(ctx, apiaddr, null, sourceId, configEnum);
    }

    /**
     * 撤销签署流程
     * 参考文档：https://open.esign.cn/doc/opendoc/pdf-sign3/klbicu
     * 接口地址：https://{host}/v3/sign-flow/{signFlowId}/revoke
     * <p>
     * 请求方法：POST
     * 注意事项：撤销签署中的流程，撤销后签署流程将终止，变为已撤销状态。
     *
     * @param signFlowId   签署流程ID
     * @param revokeReason 撤销原因 撤销原因最多50字
     * @param sourceId     来源id
     * @return
     */
    public static EsignHttpResponse revoke_by_file(Context ctx, String signFlowId, String revokeReason, String sourceId) throws EsignException {
        EsignConfigEnum configEnum = EsignConfigEnum.revoke_by_file;
        String apiaddr = EsignConfig.getInstance().get(configEnum.getValue());
        apiaddr = MessageFormat.format(apiaddr, signFlowId);
        Map<String, String> jsonMap = Maps.newHashMap();
        jsonMap.put("revokeReason", (StringUtils.isNotBlank(revokeReason) ? revokeReason : ""));
        String json = JSON.toJSONString(jsonMap);
        return POST(ctx, apiaddr, json, sourceId, configEnum);
    }

    /**
     * 催签流程中签署人
     * 参考文档：https://open.esign.cn/doc/opendoc/pdf-sign3/yws940
     * 接口地址：https://{host}/v3/sign-flow/{signFlowId}/urge
     * 请求方法：POST
     * 【注意事项】
     * 发起签署之后的前半小时不可进行催签；
     * 与上一次催签，请至少间隔十分钟再发起下一次催签提醒。
     *
     * @param signFlowId 签署流程ID
     * @param json       催签的签署人信息
     * @param sourceId   来源id
     * @return
     */
    public static EsignHttpResponse urge_by_file(Context ctx, String signFlowId, String json, String sourceId) throws EsignException {
        EsignConfigEnum configEnum = EsignConfigEnum.urge_by_file;
        String apiaddr = EsignConfig.getInstance().get(configEnum.getValue());
        apiaddr = MessageFormat.format(apiaddr, signFlowId);
        return POST(ctx, apiaddr, json, sourceId, configEnum);
    }

    /**
     * 下载已签署文件及附属材料
     * 参考文档：https://open.esign.cn/doc/opendoc/pdf-sign3/kczf8g
     * 接口地址：https://{host}/v3/sign-flow/{signFlowId}/file-download-url
     * 请求方法：GET
     * 【注意事项】
     * 未签署完成的流程，无法下载相关文件，否则会报错："流程非签署完成状态，不允许下载文档"。
     *
     * @param signFlowId       签署流程ID
     * @param urlAvailableDate 下载链接有效期，单位：秒。默认：3600秒（60分钟）
     *                         可传入：1-3600
     *                         补充说明：
     *                         为链接设置有效期是一项安全措施，旨在降低因无关人员访问而导致的信息泄露风险。
     * @param sourceId         来源id
     * @return
     */
    public static EsignHttpResponse getFile_download_url(Context ctx, String signFlowId, Integer urlAvailableDate, String sourceId) throws EsignException, URISyntaxException {
        EsignConfigEnum configEnum = EsignConfigEnum.file_download_url;
        String apiaddr = EsignConfig.getInstance().get(configEnum.getValue());
        apiaddr = MessageFormat.format(apiaddr, signFlowId);
        if (null != urlAvailableDate && urlAvailableDate > 0 && urlAvailableDate <= 3600) {
            URIBuilder uriBuilder = new URIBuilder(apiaddr);
            uriBuilder.addParameter("urlAvailableDate", String.valueOf(urlAvailableDate));
            URI uri = uriBuilder.build(); // 自动编码
            apiaddr = uri.toString();
        }
        return GET(ctx, apiaddr, null, sourceId, configEnum);
    }

    /**
     * 下载签署中文件
     * 参考文档：https://open.esign.cn/doc/opendoc/pdf-sign3/gkgc4729sa67upfn
     * 接口地址：https://{host}/v3/sign-flow/{signFlowId}/preview-file-download-url
     * 请求方法：GET
     * 【注意事项】
     * 通过该接口可以下载流程正在签署中的文件进行预览查看，用于开发者内部系统展示合同内容。本接口下载的文件因为是过程中文件所以不支持验签和出证，并且文件中会带有“本文档仅供预览查看”的水印字样。
     *
     * @param signFlowId 签署流程ID
     * @param docFileId  本次签署流程中的文件ID【注】仅支持签署文件，不支持附件
     *                   可传入：1-3600
     *                   补充说明：
     *                   为链接设置有效期是一项安全措施，旨在降低因无关人员访问而导致的信息泄露风险。
     * @param sourceId   来源id
     * @return
     */
    public static EsignHttpResponse getPreview_file_download_url(Context ctx, String signFlowId, String docFileId, String sourceId) throws EsignException, URISyntaxException {
        EsignConfigEnum configEnum = EsignConfigEnum.preview_file_download_url;
        String apiaddr = EsignConfig.getInstance().get(configEnum.getValue());
        apiaddr = MessageFormat.format(apiaddr, signFlowId);
        URIBuilder uriBuilder = new URIBuilder(apiaddr);
        uriBuilder.addParameter("docFileId", docFileId);
        URI uri = uriBuilder.build(); // 自动编码
        apiaddr = uri.toString();
        return GET(ctx, apiaddr, null, sourceId, configEnum);
    }

    /**
     * 查询企业内部印章
     * 参考文档：https://open.esign.cn/doc/opendoc/seal3/ups6h1
     * 接口地址：https://{host}/v3/seals/org-own-seal-list?orgId=xx&pageNum=1&pageSize=20
     * 请求方法：GET
     * 【注意事项】
     * 查询 orgId （机构企业）名下自身创建的内部自有企业印章，包括印章的编号、名称、状态、印章业务类型、印章图片下载地址等信息。
     *
     * @param orgId          机构账号ID 【注】用户在e签宝注册实名后才有账号ID，账号ID获取方式请使用【查询机构认证信息】接口通过组织机构名称/组织机构证件号进行查询
     * @param pageNum        查询页码
     * @param pageSize       每页显示的数量，最大值：20
     * @param sealBizTypes   印章业务类型（多项可使用英文逗号分隔）
     *                       PUBLIC - 公章
     *                       CONTRACT - 合同专用章
     *                       FINANCE - 财务专用章
     *                       PERSONNEL - 人事专用章
     *                       LEGAL_PERSON - 法定代表人章
     *                       OMMON - 其他
     * @param revocationSeal 是否需要查询已吊销印章，默认不查询 【注】曾经有用印记录的印章删除后即为已吊销状态
     *                       false - 不查询已吊销印章
     *                       ture - 查询已吊销印章
     * @param sourceId       来源id
     * @return
     */
    public static EsignHttpResponse getOrg_own_seal_list(Context ctx, String orgId,
                                                         Integer pageNum, Integer pageSize,
                                                         String sealBizTypes, Boolean revocationSeal,
                                                         String sourceId
    ) throws EsignException, URISyntaxException {
        EsignConfigEnum configEnum = EsignConfigEnum.org_own_seal_list;
        String apiaddr = EsignConfig.getInstance().get(configEnum.getValue());

        URIBuilder uriBuilder = new URIBuilder(apiaddr);
        uriBuilder.addParameter("orgId", orgId);
        if(pageNum<0) {
            uriBuilder.addParameter("pageNum", String.valueOf(1));
        }else {
            uriBuilder.addParameter("pageNum", String.valueOf(pageNum));
        }
        if(pageSize<=0) {
            uriBuilder.addParameter("pageSize", String.valueOf(1));
        }else if(pageSize>20){
            uriBuilder.addParameter("pageSize", String.valueOf(20));
        }else {
            uriBuilder.addParameter("pageSize", String.valueOf(pageSize));
        }
        if(StringUtils.isNotBlank(sealBizTypes)){
            uriBuilder.addParameter("sealBizTypes", sealBizTypes);
        }
        if(null!=revocationSeal){
            uriBuilder.addParameter("revocationSeal", revocationSeal.toString());
        }
        URI uri = uriBuilder.build(); // 自动编码
        apiaddr = uri.toString();
        return GET(ctx, apiaddr, null, sourceId, configEnum);
    }
    /**
     * 查询文件上传状态
     * 参考文档：https://open.esign.cn/doc/opendoc/pdf-sign3/qz4aip
     * 接口地址：https://{host}/v3/files/{fileId}
     * @param ctx
     * @param fileId
     * @param sourceId
     * 请求方法：GET
     */
    public static EsignHttpResponse getFileStatus(Context ctx,String fileId, String sourceId) throws EsignException {
        EsignConfigEnum configEnum = EsignConfigEnum.getFilesState;
        String apiaddr = EsignConfig.getInstance().get(configEnum.getValue());
        apiaddr = MessageFormat.format(apiaddr, fileId);
        //发起接口请求
        return GET(ctx, apiaddr, null, sourceId, configEnum);
    }
    /**
     * 检索文件关键字坐标
     * 参考文档：https://open.esign.cn/doc/opendoc/pdf-sign3/ze0ahv
     * 接口地址：https://{host}/v3/files/{fileId}/keyword-positions
     * @param ctx
     * @param fileId
     * @param keywords
     * @param sourceId
     * 请求方法：GET
     */
    public static EsignHttpResponse keyword_positions(Context ctx,String fileId, List<String> keywords, String sourceId) throws EsignException {
        EsignConfigEnum configEnum = EsignConfigEnum.keyword_positions;
        String apiaddr = EsignConfig.getInstance().get(configEnum.getValue());
        apiaddr = MessageFormat.format(apiaddr, fileId);
        Map<String,List<String>> jsonMap = Maps.newHashMap();
        jsonMap.put("keywords",keywords);
        String json = JSON.toJSONString(jsonMap);
        //发起接口请求
        return POST(ctx, apiaddr, json, sourceId, configEnum);
    }

    /**
     * 获取拖章定位页面
     * 参考文档：https://open.esign.cn/doc/opendoc/pdf-sign3/gw0r0w5qb5cx2ft6
     * 接口地址：https://{host}/v3/files/get-seal-position-url
     * @param ctx
     * @param fileId
     * @param signerRoles
     * @param sourceId
     * 请求方法：POST
     */
    public static EsignHttpResponse get_seal_position_url(Context ctx,String fileId,List<String> signerRoles, String sourceId) throws EsignException {
        EsignConfigEnum configEnum = EsignConfigEnum.get_seal_position_url;
        String apiaddr = EsignConfig.getInstance().get(configEnum.getValue());
        apiaddr = MessageFormat.format(apiaddr, fileId);
        Map<String,Object> jsonMap = Maps.newHashMap();
        jsonMap.put("customBizNum",System.currentTimeMillis());
        jsonMap.put("fileId",fileId);
        jsonMap.put("signerRoles",signerRoles);
        jsonMap.put("clientType","ALL");
        jsonMap.put("redirectUrl",EsignConfig.getInstance().get(EsignConfigEnum.NOTIFYURL_VALUE));
        String json = JSON.toJSONString(jsonMap);
        //发起接口请求
        return POST(ctx, apiaddr, json, sourceId, configEnum);
    }

    /**
     * 查询签署流程列表
     * 参考文档：https://open.esign.cn/doc/opendoc/pdf-sign3/kq4b2e
     * 接口地址：https://{host}/v3/sign-flow/sign-flow-list
     * @param ctx
     * @param jsonMap
     * @param sourceId
     * 请求方法：GET
     */
    public static EsignHttpResponse sign_flow_list(Context ctx, Map<String,Object> jsonMap, String sourceId) throws EsignException {
        EsignConfigEnum configEnum = EsignConfigEnum.sign_flow_list;
        String apiaddr = EsignConfig.getInstance().get(configEnum.getValue());

        String json = JSON.toJSONString(jsonMap);
        //发起接口请求
        return POST(ctx, apiaddr, json, sourceId, configEnum);
    }
}
