package com.gree.mall.manager.utils.fadada;

import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fadada.sdk.base.client.FddBaseClient;
import com.fadada.sdk.base.model.req.*;
import com.fadada.sdk.extra.client.FddExtraClient;
import com.fadada.sdk.extra.model.req.GotoBatchSemiAutoSignPageParams;
import com.fadada.sdk.extra.model.req.QuerySignResultParams;
import com.fadada.sdk.extra.model.req.QuerySignStatusParams;
import com.fadada.sdk.extra.model.req.ViewTemplateParams;
import com.fadada.sdk.utils.crypt.FddEncryptTool;
import com.fadada.sdk.verify.client.FddVerifyClient;
import com.fadada.sdk.verify.model.req.*;
import com.gree.mall.manager.exception.RemoteServiceException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

@Slf4j
@Component
public class Auth4FDDUtil {

    private static String appId;
    private static String secret;
    private static String version;
    private static String url;

    public static void setMainValue(String appid, String secret, String version, String url) {
        Auth4FDDUtil.appId = appid;
        Auth4FDDUtil.secret = secret;
        Auth4FDDUtil.version = version;
        Auth4FDDUtil.url = url;
    }

    /**
     * 注册账号
     * @param accountType 账号类型1个人 2企业
     * @param transactionId 平台方自定义唯一标识
     * @return
     */
    public static JSONObject regAccount(String accountType, String transactionId) throws Exception {
        FddBaseClient baseClient = new FddBaseClient(appId, secret, version, url);
        RegisterAccountParams params = new RegisterAccountParams();
        params.setAccountType(accountType); // 账号类型1个人 2企业
        params.setOpenId(transactionId); // 平台方自定义唯一标识

        String result = baseClient.invokeRegisterAccount(params);
        return parseJson(result);
    }

    /**
     * 获取企业实名认证地址
     * @param customerId 法大大客户编号
     * @param pageModify 是否允许用户页面修改 1允许 2不允许 默认为1
     * @param verifiedWay 实名认证套餐类型：  0：标准方案（对公打款+纸质审核+法人身份+法人授权）；1：对公打款；2：纸质审核；3：法人身份（授权）认证
     * @param mverifiedWay 管理员认证套餐类型：  0：三要素标准方案；  1：三要素补充方案；  2：四要素标准方案；  3：四要素补充方案；  4：纯三要素方案；  5：纯四要素方案；
     * @param legalAllowCompanyVerifyWay 指定管理员为"法人"身份下，允许的认证方式∶ 1.法人身份认证; 2.对公打款认证; 3.纸质材料认证;
     * @param agentAllowCompanyVerifyWay 指定管理员为"代理人"身份下，允许的认证方式∶ 1.法人授权认证; 2.对公打款认证; 3.纸质材料认证;可同时传入多个值，如"1,2“，表示代理身份下，只允许"法人授权认证"及"对公打款认证"方式，页面隐藏纸质材料认证"方式;传入空值时，表示允许所有认证方式;
     * @param idPhotoOptional 是否需要上传身份照片 0-只需要头像面 1-头像面与国徽面都需要 2-都不需要 默认为0
     * @param companyPrincipalType 企业负责人身份:1.法人，2代理人
     * @param certFlag 是否认证成功后自动申请实名证书  参数值为“0”：不申请，参数值为“1”：自动申请
     * @param option 操作类型 add（新增）modify（修改）不传默认add
     * @param verifiedSerialno 管理员认证流水号
     * @param legalName 法人姓名（代理人认证想要传法人姓名可用此参数）
     * @param mobile 法人/代理人/个人手机号
     * @param legalAuthorizedMobile 法人授权手机号,当接口中传入了“法人授权手机号”字段时，页面中选择法人授权认证时，会将传入的手机号展示出来
     * @param lang zh:中文，en:英文默认：中文
     * @param organizationType 组织类型 0：企业；1：政府/事业单位；2：其他组织；3：个体工商户
     * @param notifyUrl 回调地址
     * @param returnUrl 同步通知url
     * @param company
     * @param legal
     * @param agent
     * @param bank
     * @return
     */
    public static JSONObject getAuthCompanyUrl(String customerId, String pageModify, String verifiedWay, String mverifiedWay,
                                               String legalAllowCompanyVerifyWay, String agentAllowCompanyVerifyWay, String idPhotoOptional,
                                               String companyPrincipalType, String certFlag, String option, String verifiedSerialno,
                                               String legalName, String mobile, String legalAuthorizedMobile, String lang,
                                               String organizationType, String notifyUrl, String returnUrl,
                                               CompanyVerifyUrlParams.CompanyInfo company, CompanyVerifyUrlParams.LegalInfo legal, CompanyVerifyUrlParams.AgentInfo agent, CompanyVerifyUrlParams.BankInfo bank) {
        FddVerifyClient client = new FddVerifyClient(appId, secret, version, url);
        CompanyVerifyUrlParams params = new CompanyVerifyUrlParams();
        if (StringUtils.isBlank(customerId)) {
            throw new RemoteServiceException("法大大客户编号不能为空");
        }
        params.setCustomerId(customerId);
        params.setPageModify(Optional.ofNullable(pageModify).orElse("1"));//1允许 2不允许 默认为1 是否允许用户页面修改
        //以下是非必填参数
        params.setVerifiedWay(Optional.ofNullable(verifiedWay).orElse(""));//实名认证套餐类型：  0：标准方案（对公打款+纸质审核+法人身份+法人授权）；1：对公打款；2：纸质审核；3：法人身份（授权）认证
        params.setMVerifiedWay(Optional.ofNullable(mverifiedWay).orElse(""));//管理员认证套餐类型：  0：三要素标准方案；  1：三要素补充方案；  2：四要素标准方案；  3：四要素补充方案；  4：纯三要素方案；  5：纯四要素方案；
        params.setLegalAllowCompanyVerifyWay(Optional.ofNullable(legalAllowCompanyVerifyWay).orElse(""));//指定管理员为"法人"身份下，允许的认证方式∶ 1.法人身份认证; 2.对公打款认证; 3.纸质材料认证;
        params.setAgentAllowCompanyVerifyWay(Optional.ofNullable(agentAllowCompanyVerifyWay).orElse(""));//指定管理员为"代理人"身份下，允许的认证方式∶ 1.法人授权认证; 2.对公打款认证; 3.纸质材料认证;可同时传入多个值，如"1,2“，表示代理身份下，只允许"法人授权认证"及"对公打款认证"方式，页面隐藏纸质材料认证"方式;传入空值时，表示允许所有认证方式;
        params.setIdPhotoOptional(Optional.ofNullable(idPhotoOptional).orElse(""));//0-只需要头像面 1-头像面与国徽面都需要 2-都不需要 默认为0
        if (Objects.nonNull(company)) {
            //公司信息
            CompanyVerifyUrlParams.CompanyInfo companyInfo = new CompanyVerifyUrlParams.CompanyInfo();
            companyInfo.setCompanyName(company.getCompanyName());//企业名称
            companyInfo.setCreditNo(company.getCreditNo());//统一社会信用代码
            companyInfo.setCreditImagePath(company.getCreditImagePath());//统一社会信用代码证件照路径 可访问公网地址
            params.setCompanyInfo(null);
        }

        params.setCompanyPrincipalType(Optional.ofNullable(companyPrincipalType).orElse(""));//1.法人，2代理人

        if (params.getCompanyPrincipalType().equals("1")) {
            if (Objects.isNull(legal)) {
                throw new RemoteServiceException("法人信息不能为空");
            }
            //法人信息
            CompanyVerifyUrlParams.LegalInfo legalInfo = new CompanyVerifyUrlParams.LegalInfo();
            legalInfo.setLegalName(legal.getLegalName());//法人姓名
            legalInfo.setLegalId(legal.getLegalId());//法人证件号
            legalInfo.setLegalMobile(legal.getLegalMobile());//法人手机号（仅支持国内运营商）
            legalInfo.setLegalIdFrontPath(legal.getLegalIdFrontPath());//法人证件正面照下载地址
            legalInfo.setLegalIdBackPath(legal.getLegalIdBackPath());//法人证件反面照下载地址
            legalInfo.setBankCardNo(legal.getBankCardNo());//法人银行卡号
            params.setLegalInfo(legalInfo);
            params.setLegalIdFrontImg(null);
        } else if (params.getCompanyPrincipalType().equals("2")) {
            if (Objects.isNull(agent)) {
                throw new RemoteServiceException("代理人信息不能为空");
            }
            //代理人信息
            CompanyVerifyUrlParams.AgentInfo agentInfo = new CompanyVerifyUrlParams.AgentInfo();
            agentInfo.setAgentName(agent.getAgentName());
            agentInfo.setAgentId(agent.getAgentId());
            agentInfo.setAgentMobile(agent.getAgentMobile());
            agentInfo.setAgentIdFrontPath(agent.getAgentIdFrontPath());
            agentInfo.setAgentIdBackPath(agent.getAgentIdBackPath());
            agentInfo.setBankCardNo(agent.getBankCardNo());
            params.setAgentInfo(agentInfo);
            params.setAgentIdFrontImg(null);
            params.setCreditImage(null);
        }

        if (Objects.nonNull(bank)) {
            //银行卡信息
            CompanyVerifyUrlParams.BankInfo bankInfo = new CompanyVerifyUrlParams.BankInfo();
            bankInfo.setBankName(bank.getBankName());
            bankInfo.setBankId(bank.getBankId());
            bankInfo.setSubbranchName(bank.getSubbranchName());
            params.setBankInfo(null);
            params.setBankProvinceName("");
            params.setBankCityName("");
        }


        params.setCertFlag(Optional.ofNullable(certFlag).orElse("1"));//是否认证成功后自动申请实名证书  参数值为“0”：不申请，参数值为“1”：自动申请
        params.setOption(Optional.ofNullable(option).orElse(""));//add（新增）modify（修改）不传默认add
        params.setVerifiedSerialNo(Optional.ofNullable(verifiedSerialno).orElse(""));//管理员认证流水号
        params.setAuthorizationFile(null);//企业注册申请表
        params.setLegalName(Optional.ofNullable(legalName).orElse(""));//法人姓名（代理人认证想要传法人姓名可用此参数）
        params.setMobile(Optional.ofNullable(mobile).orElse(""));//法人/代理人/个人手机号
        params.setLegalAuthorizedMobile(Optional.ofNullable(legalAuthorizedMobile).orElse(""));//法人授权手机号,当接口中传入了“法人授权手机号”字段时，页面中选择法人授权认证时，会将传入的手机号展示出来
        params.setLang(Optional.ofNullable(lang).orElse(""));//zh:中文，en:英文默认：中文
        params.setOrganization_type(Optional.ofNullable(organizationType).orElse(""));//0：企业；1：政府/事业单位；2：其他组织；3：个体工商户
        params.setReturnUrl(Optional.ofNullable(returnUrl).orElse(""));
        params.setNotifyUrl(Optional.ofNullable(notifyUrl).orElse(""));

        String result = client.invokeCompanyVerifyUrl(params);
        return parseJson(result);
    }

    /**
     * 获取个人实名认证地址
     * @param customerId 法大大客户编号
     * @param pageModify 是否允许用户页面修改 1允许 2不允许 默认为1
     * @param verifiedWay 实名认证套餐类型：0：三要素标准方案；1：三要素补充方案；2：四要素标准方案；3：四要素补充方案；4：纯三要素方案；5：纯四要素方案；9：人脸识别方案
     * 0、2-标准方案
     * [运营商三要素/银行卡四要素通过，才能人脸识别认证，人脸认证通过即认证通过]；
     * 1、3-补充方案
     * [运营商三要素/银行卡四要素通过的情况下认证成功，不通过的情况下进行人脸识别认证]；
     * 4、5-纯要素方案
     * [运营商三要素/银行卡四要素通过，则认证通过]；
     * 9-人脸识别方案
     * [默认二要素+人脸识别，会配置增加手机号采集，用户信息关联手机号后允许使用短信签署，也支持刷脸签署]
     * @param customerName 姓名
     * @param customerIdentType 是否支持其他证件类型 身份证-0 其他1 默认为0
     * @param customerIdentNo 证件号码
     * @param mobile 手机号码
     * @param identFrontPath 证件正面照下载地址 可访问公网地址
     * @param identBackPath 证件反面照下载地址 可访问公网地址
     * @param resultType 刷脸是否显示结果页面 参数值为“1”：直接跳转到return_url或法大大指定页面 参数值为“2”：需要用户点击确认后跳转到return_url或法大大指定页面 参数值为“3”：认证无论是否通过，直接跳转return_url，仅对个人认证有效 默认为2
     * @param certFlag 是否认证成功后自动申请实名证书 “0”：不申请 “1”：自动申请 默认为0
     * @param certType 证件类型: 0：身份证；1：护照；B：港澳居民来往内地通行证；C：台湾居民来往大陆通行证；
     * @param bankCardNo 个人银行卡
     * @param option add（新增） modify（修改）不传默认add
     * @param idPhotoOptional 是否需要上传身份照片
     * @param isMinProgram 是否跳转法大大公证处小程序认证 0：小程序webview内嵌法大大h5；1：跳转法大大公证处小程序认证；3：小程序中间页刷脸
     * @param lang zh：中文；en：英文 默认为中文
     * @param notifyUrl 回调地址
     * @param returnUrl 同步通知url
     * @param identFrontImgFile 证件正面照图片文件
     * @param identBackImg 证件反面照图片文件
     * @return
     */
    public static JSONObject getAuthPersonUrl(String customerId, String pageModify, String verifiedWay, String customerName,
                                              String customerIdentType, String customerIdentNo, String mobile,
                                              String identFrontPath, String identBackPath, String resultType, String certFlag,
                                              String certType, String bankCardNo, String option, String idPhotoOptional,
                                              String isMinProgram, String lang, String notifyUrl, String returnUrl,
                                              File identFrontImgFile, File identBackImg) {
        FddVerifyClient client = new FddVerifyClient(appId, secret, version, url);
        PersonVerifyUrlParams params = new PersonVerifyUrlParams();
        params.setCustomerId(customerId); //客户编号
        params.setVerifiedWay(Optional.ofNullable(verifiedWay).orElse("0")); //实名认证套餐类型
        params.setPageModify(Optional.ofNullable(pageModify).orElse("1")); //是否允许用户页面修改 1允许 2不允许
        // 以下是非必填参数
        params.setNotifyUrl(notifyUrl); //异步回调地址
        params.setReturnUrl(returnUrl); //同步通知url
        params.setCustomerName(Optional.ofNullable(customerName).orElse("")); //姓名
        params.setCustomerIdentType(Optional.ofNullable(customerIdentType).orElse("")); //证件类型
        params.setCustomerIdentNo(Optional.ofNullable(customerIdentNo).orElse("")); //证件号码
        params.setMobile(Optional.ofNullable(mobile).orElse("")); //手机号码
        params.setIdentFrontPath(Optional.ofNullable(identFrontPath).orElse("")); //证件正面照下载地址
        params.setIdentBackPath(Optional.ofNullable(identBackPath).orElse("")); //证件反面照下载地址
        params.setResultType(Optional.ofNullable(resultType).orElse("")); //刷脸是否显示结果页面
        params.setCertFlag(Optional.ofNullable(certFlag).orElse("1")); //是否认证成功后自动申请实名证书
        params.setCertType(Optional.ofNullable(certType).orElse("")); //证件类型
        params.setBankCardNo(Optional.ofNullable(bankCardNo).orElse("")); //个人银行卡
        params.setOption(Optional.ofNullable(option).orElse("")); //不传默认add
        params.setIdPhotoOptional(Optional.ofNullable(idPhotoOptional).orElse("")); //是否需要上传身份照片
        params.setIsMinProgram(Optional.ofNullable(isMinProgram).orElse("")); //是否跳转法大大公证处小程序认证
        params.setLang(Optional.ofNullable(lang).orElse("")); //zh：中文；en：英文
        params.setIsAllowOverseasBankCardAuth(""); //海外用户是否支持银行卡认证
        params.setIdentFrontImg(identFrontImgFile); //证件正面照图片文件
        params.setIdentBackImg(identBackImg); //证件反面照图片文件

        log.info("法大大个人认证请求参数：{}", JSONUtil.toJsonStr(params));
        String result = client.invokePersonVerifyUrl(params);
        return parseJson(result);
    }

    /**
     * 查询企业实名认证信息
     * @param verifiedSerialNo 获取企业实名认证地址返回的交易号transactionNo
     * @return
     */
    public static JSONObject findCompanyCertInfo(String verifiedSerialNo) {
        if (StringUtils.isBlank(verifiedSerialNo)) {
            throw new RemoteServiceException("交易号不能为空");
        }
        FddVerifyClient client = new FddVerifyClient(appId, secret, version, url);
        FindCompanyCertParams params = new FindCompanyCertParams();
        params.setVerifiedSerialNo(verifiedSerialNo);//此处填获取企业实名认证地址返回的交易号
        String result = client.invokeFindCompanyCert(params);
        return parseJson(result);
    }

    /**
     * 查询个人实名认证信息
     * @param verifiedSerialNo 获取个人实名认证地址返回的交易号transactionNo
     * @return
     */
    public static JSONObject findPersonCertInfo(String verifiedSerialNo) {
        if (StringUtils.isBlank(verifiedSerialNo)) {
            throw new RemoteServiceException("交易号不能为空");
        }
        FddVerifyClient client = new FddVerifyClient(appId, secret, version, url);
        FindPersonCertParams params = new FindPersonCertParams();
        params.setVerifiedSerialNo(verifiedSerialNo); //此处填获取个人实名认证地址返回的交易号
        String result = client.invokeFindPersonCert(params);
        return parseJson(result);
    }

    /**
     * 通过uuid下载文件
     * @param uuid 传个人实名认证接口查询中返回的认证图片id，或查询企业认证信息中返回的企业认证申请表id
     * @param docType 0：图片（默认图片）1：pdf (仅支持企业申请表模板)
     * @return
     */
    public static String getFileForUUID(String uuid, String docType){
        if (StringUtils.isBlank(uuid)) {
            throw new RemoteServiceException("uuid不能为空");
        }
        FddVerifyClient client = new FddVerifyClient(appId, secret, version, url);
        FileForUUIDParams params = new FileForUUIDParams();
        params.setUuid(uuid);//此处传个人实名认证接口查询中返回的认证图片id，或查询企业认证信息中返回的企业认证申请表id
        params.setDocType(Optional.ofNullable(docType).orElse("0"));//0：图片（默认图片）1：pdf (仅支持企业申请表模板)
        String result = client.invokeFileForUUID(params);
        return result;
    }

    /**
     * 申请实名证书（认证certFlag=1时，不用再调次方法）
     * @param customerId 法大大客户编号
     * @param verifiedSerialNo 获取实名认证地址返回的交易号transactionNo
     * @return
     */
    public static JSONObject applyCert(String customerId, String verifiedSerialNo) {
        if (StringUtils.isBlank(customerId)) {
            throw new RemoteServiceException("法大大客户编号不能为空");
        }
        if (StringUtils.isBlank(verifiedSerialNo)) {
            throw new RemoteServiceException("交易号不能为空");
        }
        FddVerifyClient client = new FddVerifyClient(appId, secret, version, url);
        ApplyCertParams params = new ApplyCertParams();
        params.setCustomerId(customerId); //客户编号
        params.setVerifiedSerialNo(verifiedSerialNo); //填写获取实名认证地址返回的交易号transactionNo
        String result = client.invokeApplyCert(params);
        return parseJson(result);
    }

    /**
     * 上传合同接口
     * @param contractId 平台自定义唯一合同编号
     * @param title 合同标题
     * @param file PDF模板 文件大小要求<=20MB (file或docUrl二选一)
     * @param docUrl 文档地址 公网可访问 (file或docUrl二选一)
     */
    public static JSONObject uploadContract(String contractId, String title, File file, String docUrl) {
        if (StringUtils.isBlank(contractId)) {
            throw new RemoteServiceException("合同编号不能为空");
        }
        FddBaseClient baseClient = new FddBaseClient(appId, secret, version, url);
        UploadDocsParams params = new UploadDocsParams();
        params.setContractId(contractId); //自定义合同id
        params.setDocTitle(title); //合同标题
        //下列PDF模板和文档地址二选一
        if (Objects.nonNull(file)) {
            params.setFile(file); // PDF模板
        } else {
            if (StringUtils.isBlank(docUrl)) {
                throw new RemoteServiceException("文档地址不能为空");
            }
            params.setDocUrl(docUrl); // 文档地址
        }
        params.setDocType(".pdf"); //合同类型 目前仅支持pdf格式
        String result = baseClient.invokeUploadDocs(params);
        return parseJson(result);
    }

    /**
     * 模板上传接口
     * @param templateId 平台自定义唯一模板编号
     * @param file PDF模板 文件大小要求<=20MB (file或docUrl二选一)
     * @param docUrl 文档地址 公网可访问 (file或docUrl二选一)
     */
    public static JSONObject uploadTemplate(String templateId, File file, String docUrl) {
        if (StringUtils.isBlank(templateId)) {
            throw new RemoteServiceException("模板编号不能为空");
        }
        FddBaseClient baseClient = new FddBaseClient(appId, secret, version, url);
        UploadTemplateParams params = new UploadTemplateParams();
        params.setTemplateId(templateId);  // 平台自定义唯一模板编号
        //下列PDF模板和文档地址二选一
        if (Objects.nonNull(file)) {
            params.setFile(file); // PDF模板
        } else {
            if (StringUtils.isBlank(docUrl)) {
                throw new RemoteServiceException("文档地址不能为空");
            }
            params.setDocUrl(docUrl); // 文档地址
        }
        String result = baseClient.invokeUploadTemplate(params);
        return parseJson(result);
    }

    /**
     * 模板填充接口
     * @param templateId 模板编号(已上传的模板编号)
     * @param contractId 平台自定义唯一合同编号
     * @param title 文档标题
     * @param fontSize 字体大小
     * @param fontType 字体类型
     * @param parameterMap 填充内容 JsonObject字符串示例：{"platform":"深圳法大大","customer_name":"工程师","year":"2022"}
     * @return
     */
    public static JSONObject generateContract(String templateId, String contractId, String title, String fontSize,
                                              String fontType, String parameterMap) {
        if (StringUtils.isBlank(templateId)) {
            throw new RemoteServiceException("模板编号不能为空");
        }
        if (StringUtils.isBlank(templateId)) {
            throw new RemoteServiceException("合同编号不能为空");
        }
        FddBaseClient baseClient = new FddBaseClient(appId, secret, version, url);
        GenerateContractParams params = new GenerateContractParams();
        params.setTemplateId(templateId);  //模板编号
        params.setContractId(contractId); //合同编号
        //以下是非必填参数
        params.setDocTitle(Optional.ofNullable(title).orElse("")); // 文档标题
        params.setFontSize(Optional.ofNullable(fontSize).orElse("")); //字体大小
        params.setFontType(Optional.ofNullable(fontType).orElse("")); //字体类型
        params.setParameterMap(Optional.ofNullable(parameterMap).orElse("")); //填充内容,json字符串
        params.setDynamicTables(""); //动态表格
        // 在版本号设置为 2.1 时，实现了对填充内容和动态表单进行3DES加密
        baseClient.setVersion("2.1");
        String result = baseClient.invokeGenerateContract(params);
        return parseJson(result);
    }

    /**
     * 手动签接口
     * @param transactionId 平台自定义唯一交易号
     * @param contractId 此处传入调用上传或填充合同接口成功时定义的合同编号
     * @param customerId 此处传入认证成功后成功申请证书的客户编号
     * @param docTitle 文档标题
     * @param mobileSignType 签章类型 0：全部；1：标准；2：手写 当接口中该参数传入有值时，以接口传入的配置为准，否则则取运营后台配置
     * @param signKeyword 定位关键字
     * @param keyx 关键字偏移量，偏移x位置 [-595,595]之间的数字
     * @param keyy 关键字偏移量，偏移y位置 [-842,842]之间的数字
     * @param keywordStrategy 关键字签章策略 0：所有关键字签章 （默认）；1：第一个关键字签章 ；2：最后一个关键字签章；
     * @param returnUrl 页面跳转URL（签署结果同步通知）
     * @param notifyUrl 签署结果异步通知URL
     * @param readTime 合同必读时间 0-60秒
     * @param readType 是否需要短信验证打开链接 0：否  1：是 不传值，默认为否
     * @param requiredRead 合同是否必读至末页 0：非必读  1：必读
     * @param signVerifyWay 签署意愿方式  1：短信验证；3：刷脸验证 10：短信验证和刷脸验证二选一  当接口中该参数传入有值时，以接口传入的值为准，否则则取运营后台配置
     * @param verifyWayFlag 签署意愿方式选择人脸识别时， 人脸识别失败后自动调整为短信 0：不切换；1：切换 默认为0
     * @param openEnvironment 打开环境 0、跳转h5；（默认） 1、支持在客户小程序path中跳转，path的写法如 /page/page1；2、跳转法大大公证处小程序；4、由中间页跳转法大大公证处小程序
     * @return
     */
    public static String extSign(String transactionId, String contractId, String customerId, String docTitle, String mobileSignType,
                                 String signKeyword, String keyx, String keyy, String keywordStrategy, String returnUrl,
                                 String notifyUrl, String readTime, String readType, String requiredRead, String signVerifyWay,
                                 String verifyWayFlag, String openEnvironment) {
        if (StringUtils.isBlank(transactionId)) {
            throw new RemoteServiceException("交易号不能为空");
        }
        if (StringUtils.isBlank(contractId)) {
            throw new RemoteServiceException("合同编号不能为空");
        }
        if (StringUtils.isBlank(customerId)) {
            throw new RemoteServiceException("法大大客户编号不能为空");
        }
        FddBaseClient client = new FddBaseClient(appId, secret, version, url);
        ExtSignParams params = new ExtSignParams();
        params.setTransactionId(transactionId);//平台自定义唯一交易号
        params.setContractId(contractId);//此处传入调用上传或填充合同接口成功时定义的合同编号
        params.setCustomerId(customerId);//此处传入认证成功后成功申请证书的客户编号
        params.setDocTitle(docTitle);
        params.setMobileSignType(mobileSignType);
        params.setPositionType("0");//0-关键字（默认）1-坐标
        params.setSignKeyword(signKeyword);
        params.setKeyx(keyx);// 关键字偏移量，偏移x位置
        params.setKeyy(keyy);// 关键字偏移量，偏移y位置
        params.setKeywordStrategy(Optional.ofNullable(keywordStrategy).orElse("0"));//0-所有关键字签章 （默认） 1-第一个关键字签章 2-最后一个关键字签章
        params.setReturnUrl(returnUrl);
        params.setNotifyUrl(notifyUrl);
        params.setReadTime(Optional.ofNullable(readTime).orElse(""));
        params.setReadType(Optional.ofNullable(readType).orElse(""));
        params.setRequiredRead(Optional.ofNullable(requiredRead).orElse(""));
        params.setSignVerifyWay(Optional.ofNullable(signVerifyWay).orElse(""));
        params.setVerifyWayFlag(Optional.ofNullable(verifyWayFlag).orElse("0"));
        params.setOpenEnvironment(Optional.ofNullable(openEnvironment).orElse("0"));
        return client.invokeExtSign(params);
    }

    /**
     * 文档批量签署接口(半自动模式)
     * @param batchId 批次号（流水号）
     * @param batchTitle 批量请求标题
     * @param signData 签章数据 JsonArray[sign_data] 示例：[{"contractId":"cid01","signKeyword":"kw01","transactionId":"tid01"}, {"contractId":"cid02","signKeyword":"kw02","transactionId":"tid02"}]
     * @param customerId 此处传入认证成功后成功申请证书的客户编号
     * @param verificationType 验证类型 1短信签， 3 刷脸签 5音视频双录（不填默认为短信签）
     * @param mobileSignType 签章类型 0：标准  1：手写  2：全部
     * @param openEnvironment 打开环境 0、跳转h5；（默认） 1、支持在客户小程序path中跳转，path的写法如 /page/page1；4、小程序中间页方案（包括刷脸和音视频小程序）
     * @param returnUrl 页面跳转 URL
     * @param notifyUrl 签署结果异步通知 URL
     * @return
     */
    public static String batchExtSign(String batchId, String batchTitle, String signData, String customerId, String verificationType,
                                      String mobileSignType, String openEnvironment, String returnUrl, String notifyUrl) {
        FddExtraClient client = new FddExtraClient(appId, secret, version, url);
        GotoBatchSemiAutoSignPageParams params = new GotoBatchSemiAutoSignPageParams();
        //构造signdata
//        JSONArray sign_data = new JSONArray();
//        for (int j = 0; j < contract_id_list.size(); j++) {
//            JSONObject jsonObject = new JSONObject();
//            jsonObject.put("contractId", contract_id_list.get(j));
//            jsonObject.put("keywordStrategy", "0");
//            jsonObject.put("signKeyword", "xxx");
//            jsonObject.put("transactionId", "xxx");
//            sign_data.add(jsonObject);
//        }

        params.setBatchId(batchId);//批次号
        params.setBatchTitle(batchTitle);//批次名称
        params.setSignData(signData);//签章数据
        params.setCustomerId(customerId);//客户编号
        params.setVerificationType(Optional.ofNullable(verificationType).orElse("1"));//1短信 3刷脸 5音视频刷脸
        params.setMobileSignType(Optional.ofNullable(mobileSignType).orElse("0"));//0 标准 1手写 2全部
        params.setOpenEnvironment(Optional.ofNullable(openEnvironment).orElse("0"));//可选 1：内嵌小程序 4：集成sdk中转页面

        params.setReturnUrl(returnUrl);//跳转地址
        params.setNotifyUrl(notifyUrl);//异步通知地址
        return client.invokeGotoBatchSemiAutoSignPage(params);
    }

    /**
     * 授权自动签接口
     * @param transactionId 平台自定义唯一交易号
     * @param contractId 指该份线上授权委托书的合同编号，自定义即可
     * @param customerId 传入注册返回的个人或企业客户编号
     * @param returnUrl 同步通知签署结果地址
     * @param notifyUrl 异步通知签署结果地址
     * @return
     */
    public static String beforeAuthAuto(String transactionId, String contractId, String customerId, String returnUrl,
                                        String notifyUrl) {
        if (StringUtils.isBlank(transactionId)) {
            throw new RemoteServiceException("交易号不能为空");
        }
        if (StringUtils.isBlank(contractId)) {
            throw new RemoteServiceException("自定义合同编号不能为空");
        }
        if (StringUtils.isBlank(customerId)) {
            throw new RemoteServiceException("法大大客户编号不能为空");
        }
        FddBaseClient client = new FddBaseClient(appId, secret, version, url);
        BeforeAuthSignParams params = new BeforeAuthSignParams();
        params.setTransactionId(transactionId);//平台自定义唯一交易号
        params.setAuthType("1");//1:授权自动签（目前只能填1）
        params.setContractId(contractId);//指该份线上授权委托书的合同编号，自定义即可
        params.setCustomerId(customerId);//传入注册返回的个人或企业客户编号
        params.setReturnUrl(returnUrl);//同步通知签署结果地址
        params.setNotifyUrl(notifyUrl);//异步通知签署结果地址
        return client.invokeBeforeAuthSign(params);
    }

    /**
     * 查询授权自动签状态接口
     * @param customerId 法大大客户编号
     * @return
     */
    public static JSONObject queryAuthAutoStatus(String customerId) {
        if (StringUtils.isBlank(customerId)) {
            throw new RemoteServiceException("法大大客户编号不能为空");
        }
        FddBaseClient client = new FddBaseClient(appId, secret, version, url);
        GetAuthStatusParams params = new GetAuthStatusParams();
        params.setCustomerId(customerId);//传入调用授权自动签接口授权之后的客户编号
        String result = client.invokeGetAuthStatus(params);
        return parseJson(result);
    }

    /**
     * 自动签署接口
     * @param transactionId 平台自定义唯一交易号
     * @param contractId 此处传入调用上传或填充合同接口成功时定义的合同编号
     * @param customerId 此处传入认证成功后成功申请证书的客户编号
     * @param docTitle 文档标题
     * @param keywordStrategy 关键字签章策略 0：所有关键字签章  1：第一个关键字签章 ；   2：最后一个关键字签章
     * @param signKeyword 定位关键字
     * @param keyx 关键字偏移量，偏移x位置
     * @param keyy 关键字偏移量，偏移y位置
     * @param notifyUrl 签署结果异步通知URL
     * @return
     */
    public static JSONObject extSignAuto(String transactionId, String contractId, String customerId, String docTitle,
                                         String keywordStrategy, String signKeyword, String keyx, String keyy, String notifyUrl) {
        if (StringUtils.isBlank(transactionId)) {
            throw new RemoteServiceException("交易号不能为空");
        }
        if (StringUtils.isBlank(contractId)) {
            throw new RemoteServiceException("自定义合同编号不能为空");
        }
        if (StringUtils.isBlank(customerId)) {
            throw new RemoteServiceException("法大大客户编号不能为空");
        }
        FddBaseClient client = new FddBaseClient(appId, secret, version, url);
        ExtSignAutoParams params = new ExtSignAutoParams();
        params.setTransactionId(transactionId);//平台自定义唯一交易号
        params.setContractId(contractId);//此处传入调用上传或填充合同接口成功时定义的合同编号
        params.setCustomerId(customerId);//此处传入认证成功后成功申请证书的客户编号
        params.setDocTitle(docTitle);
        params.setPositionType("0");//0-关键字（默认）1-坐标
//        params.setSignaturePositions(getSignaturePositions());
        params.setKeywordStrategy(Optional.ofNullable(keywordStrategy).orElse("0"));//0：所有关键字签章  1：第一个关键字签章 ；   2：最后一个关键字签章
        params.setSignKeyword(Optional.ofNullable(signKeyword).orElse(""));
        params.setKeyx(Optional.ofNullable(keyx).orElse(""));// 关键字偏移量，偏移x位置
        params.setKeyy(Optional.ofNullable(keyy).orElse(""));// 关键字偏移量，偏移y位置
        params.setNotifyUrl(Optional.ofNullable(notifyUrl).orElse(""));
        String result = client.invokeExtSignAuto(params);
        return parseJson(result);
    }

    /**
     * 查询批次签署状态结果
     * @param batchId 批次号（流水号）
     * @return
     */
    public static String queryBatchStatus(String batchId) {
        String reqUrl = url + "query_batch_status.api";
        String timestamp = getTimeStamp();
        String msgDigest = getMsgDigest(batchId, timestamp);
        Map<String, Object> reqMap = new HashMap<>();
        reqMap.put("app_id", appId);
        reqMap.put("timestamp", timestamp);
        reqMap.put("v", version);
        reqMap.put("msg_digest", msgDigest);
        reqMap.put("batch_id", batchId);
        return HttpUtil.post(reqUrl, reqMap);
    }

    /**
     * 合同模板查看接口
     * @param templateId 合同模板编号
     * @return
     */
    public static String queryContractTemplate(String templateId) {
        if (StringUtils.isBlank(templateId)) {
            throw new RemoteServiceException("合同编号不能为空");
        }
        FddExtraClient client = new FddExtraClient(appId, secret, version, url);
        ViewTemplateParams params = new ViewTemplateParams();
        params.setTemplateId(templateId);
        return client.invokeViewTemplate(params);
    }

    /**
     * 合同查看接口
     * @param contractId 合同编号
     * @return
     */
    public static String viewContract(String contractId) {
        if (StringUtils.isBlank(contractId)) {
            throw new RemoteServiceException("合同编号不能为空");
        }
        FddBaseClient client = new FddBaseClient(appId, secret, version, url);
        ViewPdfURLParams params = new ViewPdfURLParams();
        params.setContractId(contractId); //此处传入调用上传或填充合同接口成功时定义的合同编号
        return client.invokeViewPdfURL(params);
    }

    /**
     * 合同下载接口
     * @param contractId 合同编号
     * @return
     */
    public static String downloadContract(String contractId) {
        if (StringUtils.isBlank(contractId)) {
            throw new RemoteServiceException("合同编号不能为空");
        }
        FddBaseClient client = new FddBaseClient(appId, secret, version, url);
        DownloadPdfParams params = new DownloadPdfParams();
        params.setContractId(contractId); // 合同编号
        //如下，传setPath参数可以直接保存文件到本地，不传则返回url
        params.setPath(""); // 指定路径，如：D:\\pdf\\uuidNew.pdf
        return client.invokeDownloadPdf(params);
    }

    /**
     * 合同归档接口
     * @param contractId 合同编号
     * @return
     */
    public static JSONObject contractFiling(String contractId) {
        if (StringUtils.isBlank(contractId)) {
            throw new RemoteServiceException("合同编号不能为空");
        }
        FddBaseClient client = new FddBaseClient(appId, secret, version, url);
        ContractFillingParams params = new ContractFillingParams();
        params.setContractId(contractId); //此处传入调用上传或填充合同接口成功时定义的合同编号
        String result = client.invokeContractFilling(params);
        return parseJson(result);
    }

    /**
     * 查询客户签署状态接口
     * @param contractId 合同编号
     * @param customerId 法大大客户编号
     * @return
     */
    public static JSONObject querySignStatus(String contractId, String customerId) {
//        正例：
//        {
//            "code": "1000",
//                "download_url": "http://FDDServer:Port/api//getdocs.action?app_id=xxxxxx",
//                "msg": "操作成功",
//                "result": "success",
//                "sign_status": 1,
//                "sign_status_desc": "已签",
//                "transaction_id": "1233211234567111",
//                "viewpdf_url": "http://FDDServer:Port/api//viewdocs.action?app_id=xxxxxx"
//        }
//        反例：
//        {
//            "code": "2002",
//                "download_url": null,
//                "msg": "参数有误（合同编号和客户编号不能为空）",
//                "result": "error",
//                "sign_status": 0,
//                "sign_status_desc": null,
//                "transaction_id": null,
//                "viewpdf_url": null
//        }
        if (StringUtils.isBlank(contractId)) {
            throw new RemoteServiceException("合同编号不能为空");
        }
        FddExtraClient client = new FddExtraClient(appId, secret, version, url);
        QuerySignStatusParams params = new QuerySignStatusParams();
        params.setContractId(contractId);
        params.setCustomerId(customerId);
        String result = client.invokeQuerySignStatus(params);
        return parseJson(result);
    }

    /**
     * 用于主动查询用户签署结果
     * @param contractId 合同编号
     * @param customerId 法大大客户编号
     * @param transactionId 签署时所传交易号
     * @return
     */
    public static JSONObject querySignStatus(String contractId, String customerId, String transactionId) {
//        正例：
//        {
//            "code": 1,
//                "data": {
//            "download_url": "http://FDDServer:Port/api/downLoadContract.api?app_id=xxxxxx",
//                    "endTime": "2021-07-16 16:52:39",
//                    "result": "3000",
//                    "result_desc": "签章成功",
//                    "view_url": "http://FDDServer:Port/viewContract.api?app_id=xxxxxx"
//        },
//            "msg": "success"
//        }
//        反例：
//        {"code":1201,"data":null,"msg":"签署记录为空"}
        FddExtraClient client = new FddExtraClient(appId, secret, version, url);
        QuerySignResultParams params = new QuerySignResultParams();
        params.setCustomerId(customerId);
        params.setContractId(contractId);
        params.setTransactionId(transactionId);//签署时传入的交易号
        String result = client.invokeQuerySignResult(params);
        return parseJson(result);
    }

    private static JSONObject parseJson(String result) {
        JSONObject resultJson = JSONUtil.parseObj(result);
        String code = resultJson.get("code", String.class);
        String msg = resultJson.get("msg", String.class);
        if (code.equals("1") || code.equals("1000") || code.equals("1201") ) {
            return resultJson;
        }
        throw new RemoteServiceException("法大大接口: " + msg);
    }

    /**
     * 地址编码转换
     * @param bizContent
     * @return
     */
    public static String decodeUrl(String bizContent) {
        try {
            bizContent = URLDecoder.decode(bizContent, "utf-8");
            bizContent = new String(Base64.decodeBase64(bizContent.getBytes()));
        } catch (UnsupportedEncodingException e) {
            return "";
        }
        return bizContent;
    }

    public static String getTimeStamp() {
        Timestamp ts = new Timestamp(System.currentTimeMillis());
        DateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        return sdf.format(ts);
    }

    public static String getMsgDigest(String str, String timestamp) {
        String sha1 = "";
        try {
            sha1 = FddEncryptTool.sha1(appId + FddEncryptTool.md5Digest("" + timestamp + "") + FddEncryptTool.sha1(secret + str) + "");
        } catch (Exception var4) {
            throw new RuntimeException(var4);
        }

        return new String(FddEncryptTool.Base64Encode(sha1.getBytes()));
    }


}
