package com.bcxin.signature.service.impl;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.DateUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bcxin.backend.core.edis.EDIConstants;
import com.bcxin.signature.components.JTLZPendingQueueComponent;
import com.bcxin.signature.components.JTLZPushComponent;
import com.bcxin.signature.config.FileModeConfig;
import com.bcxin.signature.config.FileWriteConfig;
import com.bcxin.signature.entity.SignatureQueuesBazgz;
import com.bcxin.signature.entity.SignatureQueuesDocument;
import com.bcxin.signature.service.SignatureService;
import com.bcxin.signature.util.ftp.*;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StopWatch;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * <b> 签章业务相关 </b>
 * @author ZXF
 * @create 2024/11/20 0020 18:14
 * @version
 * @注意事项 </b>
 */
@Slf4j
@Service
public class SignatureServiceImpl implements SignatureService {
    private static final Logger logger = LoggerFactory.getLogger(SignatureServiceImpl.class);

    @Value("${myapps.signature.api}")
    String api;
    @Value("${myapps.signature.appid}")
    String appid;
    @Value("${myapps.signature.sealno}")
    String sealno;
    @Value("${myapps.signature.license.ip_address}")
    String ip_address;

    @Value("${myapps.signature.license.app_key}")
    String app_key;
    @Value("${myapps.signature.license.app_secret}")
    String app_secret;
    @Value("${myapps.signature.license.account}")
    String account;
    @Value("${myapps.signature.license.password}")
    String password;

    //签章接口（单文档单章 支持PDF|OFD）
    private static final String STAMPFILE = "/channel/stampFile";
    //4.2.2.签章接口（不同文档不同章 支持PDF|OFD）
    private static final String FLEXIBLESEALFILE = "/channel/flexibleSealFile";
    //验章接口（支持PDF|OFD）
    private static final String VERIFYFILE = "/channel/verifyFile";
    //文书业务目录
    private static final String DOCUMENTPATH = "/document";
    //文书业务目录
    private static final String SIGNATUREATH = "/signature";

    //京通靓证 登录 获取token
    private static final String LICENSE_LOGIN = "/license-app/v1/security/login";
    //京通靓证 2.2.1 制证签发接口
    private static final String LICENSE_ISSUE= "/license-app/v1/license/{item_code}/issue?access_token=";
    //京通靓证 2.2.2 废止证照
    private static final String LICENSE_ABOLISH= "/license-app/v1/license/{item_code}/abolish?access_token=";

    // 静态 Map 存储 token 及其生成时间
    private static Map<String, Instant> tokenMap = new HashMap<>();
    private static int count_jtlz_document = 0;

    private final ThreadPoolTaskExecutor taskExecutor;
    private final JTLZPushComponent jtlzPushComponent;
    private final JTLZPendingQueueComponent pendingQueueComponent;

    public SignatureServiceImpl(ThreadPoolTaskExecutor taskExecutor, JTLZPushComponent jtlzPushComponent, JTLZPendingQueueComponent pendingQueueComponent) {
        this.taskExecutor = taskExecutor;
        this.jtlzPushComponent = jtlzPushComponent;
        this.pendingQueueComponent = pendingQueueComponent;
    }

    /**
     * <b> 文书签章业务 </b>
     * @author ZXF
     * @create 2024/11/20 0020 17:33
     * @version
     * @注意事项 </b>
     */
    @Override
    public void documentSignature() {
        //ftp文件存储路径
        String ftpIn = FileModeConfig.getFilePath();
        String ftpOut = FileModeConfig.getFilePath();
        List<String> list = FtpUtil.filenames(ftpIn,"");
        for (String filename: list) {
            System.err.println("====> 文书签章定时任务.documentSignature.filename：" + filename);
            String content = FtpUtil.getFileContent(ftpIn,filename);
            System.err.println("====> 文书签章定时任务.documentSignature.content：" + content);
            if(StringUtils.isEmpty(content)){
                continue;
            }
            String[] arr = filename.split("-");
            // 保安资格证业务类型编号01 参考类DocumentType.BAZGZ
            if("01".equals(arr[1])){
                bazgzServer(ftpIn, ftpOut, filename, content);
            }else{
                documentServer(ftpIn, ftpOut, filename, content, arr[1]);
            }
        }

    }

    private void documentServer(String ftpIn, String ftpOut, String filename, String content, String type){
        SignatureQueuesDocument document = JSON.parseObject(content, SignatureQueuesDocument.class);
        System.err.println("====> 文书签章定时任务.documentSignature.document：" + JSON.toJSONString(document));
        if(document == null){
            return;
        }
        document.setStatus("2");
        String[] arr = document.getFile_url().split("-");
        String baseeContent = FtpUtil.downloadAsBase64(ftpIn,arr[1]);
        Result ret = verifySignature(baseeContent,document.getBusiness_name());
        if("200".equals(ret.status)){
            JSONObject dataJson = JSONObject.parseObject(JSON.toJSONString(ret.data));
            String result = dataJson.getString("result");
            if("FALSE".equals(result)){
                //根据接口返回结果更新数据库
                ret = sendSignature(document.getStamp_xy(), baseeContent,document.getBusiness_name());
                if("200".equals(ret.status)){
                    File file = JSONObject.parseObject(JSON.toJSONString(ret.data),File.class);
                    //base64文件流写入原文件地址
                    try {
                        if(StringUtils.isEmpty(file.fileB64)){
                            throw new IOException("文件不存在");
                        }
//                            base64ToFile(file.fileB64,rootPath+document.getFile_url());

                        //返回结果写入新建ftp out下对应文书文件
                        FtpUtil.uploadBase64(ftpOut, arr[1], file.fileB64);
                        document.setStatus("1");
                        //如果是保安服务许可证书签章，需要推送京通靓证
//                        if("16".equals(type)){
//                            //推送京通靓证
//                            document.setJt_code(syncJT(document.getData(),document.getOperator(),filename,file.fileB64));
//                        }
                    } catch (IOException e) {
                        document.setProcessed_result(e.getMessage());
                    }
                }
            }else{
                //如果文件已存在章，不重复盖章
                document.setStatus("1");
            }
        }

        document.setProcessed_result(ret.getMessage());
        document.setLastmodified(new Date());
        System.err.println("====> 文书签章定时任务.documentSignature.document：" + JSON.toJSONString(document));
        //新内容写入前缀为OUT|的文件
        FtpUtil.uploadBase64(ftpOut, filename.replace("IN|","OUT|"), JSON.toJSONString(document));
    }

    private void bazgzServer(String ftpIn, String ftpOut, String filename, String content){
        SignatureQueuesBazgz bazgz = JSON.parseObject(content, SignatureQueuesBazgz.class);
        System.err.println("====> 资格证签章定时任务.bazgzSignature.bazgz：" + JSON.toJSONString(bazgz));
        if(bazgz == null){
            return;
        }
        bazgz.setStatus("2");
        String[] arr = bazgz.getFile_url().split("-");
        String baseeContent = FtpUtil.downloadAsBase64(ftpIn,arr[1]);
        Result ret = verifySignature(baseeContent,bazgz.getBusiness_name());
        if("200".equals(ret.status)){
            JSONObject dataJson = JSONObject.parseObject(JSON.toJSONString(ret.data));
            String result = dataJson.getString("result");
            if("FALSE".equals(result)){
                //根据接口返回结果更新数据库
                ret = sendSignature(bazgz.getStamp_xy(), baseeContent,bazgz.getBusiness_name());
                if("200".equals(ret.status)){
                    File file = JSONObject.parseObject(JSON.toJSONString(ret.data),File.class);
                    //base64文件流写入原文件地址
                    try {
                        if(StringUtils.isEmpty(file.fileB64)){
                            throw new IOException("文件不存在");
                        }
//                            base64ToFile(file.fileB64,rootPath+document.getFile_url());

                        //返回结果写入新建ftp out下对应文书文件
                        FtpUtil.uploadBase64(ftpOut, arr[1], file.fileB64);
                        bazgz.setStatus("1");
                        //推送京通靓证
//                        bazgz.setJt_code(syncJT(bazgz.getData(),bazgz.getOperator(),filename,file.fileB64));
                    } catch (IOException e) {
                        bazgz.setProcessed_result(e.getMessage());
                    }
                }
            }else{
                //如果文件已存在章，不重复盖章
                bazgz.setStatus("1");
            }
        }

        bazgz.setProcessed_result(ret.getMessage());
        bazgz.setLastmodified(new Date());
        System.err.println("====> 资格证签章定时任务.bazgzSignature.bazgz：" + JSON.toJSONString(bazgz));
        //新内容写入前缀为OUT|的文件
        FtpUtil.uploadBase64(ftpOut, filename.replace("IN|","OUT|"), JSON.toJSONString(bazgz));
    }

    private static void base64ToFile(String base64String,String filePath) throws IOException {
        // 解码Base64字符串
        byte[] decodedBytes = Base64.decode(base64String);
        // 将字节数组写入文件
        Path path = Paths.get(filePath);
        Files.write(path, decodedBytes);
    }

    /**
     * <b> 资格证签章业务（待定） </b>
     * TODO 资格证可以改造统一走文书签章同一业务规则实现签章及推送靓证
     * @author ZXF
     * @create 2024/11/20 0020 17:33
     * @version
     * @注意事项 </b>
     */
    @Override
    public void bazgzSignature() {
        //ftp文件存储路径
        String ftpIn = FileModeConfig.getFilePath();
        String ftpOut = FileModeConfig.getFilePath();
        List<String> list = FtpUtil.filenames(ftpIn, "");
        SignatureQueuesBazgz bazgz;
        for (String filename : list) {
            System.err.println("====> 资格证签章定时任务.bazgzSignature.filename：" + filename);
            String content = FtpUtil.getFileContent(ftpIn, filename);
            System.err.println("====> 资格证签章定时任务.bazgzSignature.content：" + content);
            if (StringUtils.isEmpty(content)) {
                continue;
            }
            bazgzServer(ftpIn, ftpOut, filename, content);

        }
    }

    /**
     * <b> 京通靓证业务(读取文件推送) </b>
     * @author ZXF
     * @create 2024/11/20 0020 17:33
     * @version
     * @注意事项 </b>
     */
    @Override
    public void signatureJTLZ() {
        logger.error("executing signatureJTLZ...");
        FtpConnection topFtp = ThreadFtpContext.getFtpConnection();
        String traceId = UUID.randomUUID().toString();
        Collection<String> pendingFileNames = null;
        StopWatch metaStopWatch = new StopWatch();
        Exception topException = null;
        try {
            metaStopWatch.start();
            pendingFileNames = FtpUtil.getFtpMetaFiles(topFtp);
        } catch (Exception ex) {
            topException = ex;
        } finally {
            metaStopWatch.stop();
            logger.error("{} geting pending files:{}; cost {} seconds",
                    traceId,
                    pendingFileNames == null ? "EMPTY" :
                            pendingFileNames.size(),
                    metaStopWatch.getTotalTimeSeconds(), topException);
        }

        if (CollectionUtils.isEmpty(pendingFileNames) || true) {
            //Collection<String> directlyFiles = FtpUtil.getDirectlyFiles(topFtp);
            Collection<String> directlyFiles = pendingQueueComponent.getPendingContentFiles(EDIConstants.PUSH_IN_FILE_PREFIX);
            Exception exception = null;
            StopWatch watch = new StopWatch();
            watch.start();
            try {
                this.jtlzPushComponent.execute(traceId, directlyFiles.toArray(new String[directlyFiles.size()]),false);
            } catch (Exception ex) {
                exception = ex;
            } finally {
                watch.stop();
                logger.error("{}-Done for directly execute cost {} seconds[pendingFileNames={}] size={}",
                        (exception == null ? "SUCCESS" : "ERROR"),
                        watch.getTotalTimeSeconds(),
                        (CollectionUtils.isEmpty(pendingFileNames) ? "Empty" : pendingFileNames.stream().collect(Collectors.joining(","))),
                        (directlyFiles == null ? "EMPTY" : directlyFiles.size()),
                        exception);
            }

            if (CollectionUtils.isEmpty(pendingFileNames)) {
                return;
            }
        }

        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        boolean isEmpty = false;
        logger.error("{} prepare to process the pendingFileNames:{}", traceId, isEmpty, pendingFileNames.size());
        Exception lastException = null;
        try {
            List<CompletableFuture> resultFutures =
                    pendingFileNames.stream().map(rfn -> {
                        return CompletableFuture.runAsync(() -> {
                            this.jtlzPushComponent.execute(traceId, rfn);
                        }, this.taskExecutor);
                    }).collect(Collectors.toList());

            Object rt = CompletableFuture.allOf(resultFutures.toArray(new CompletableFuture[resultFutures.size()])).join();
            logger.error("success:{}", rt);
        } catch (Exception ex) {
            lastException = ex;
        } finally {
            stopWatch.stop();
            log.error("{}-({}) Done for signatureJTLZ cost {} seconds - pendingFileSize={}",
                    traceId,
                    (lastException == null ? "SUCCESS" : "ERROR"),
                    stopWatch.getTotalTimeSeconds(), pendingFileNames.size()
            );
        }
    }

    /**
     * <b> 京通靓证业务保安服务许可证(读取文件推送) </b>
     * @author ZXF
     * @create 2024/11/20 0020 17:33
     * @version
     * @注意事项 </b>
     */
    @Override
    public void signatureJTLZDocument() {
        Collection<String> directlyFiles =
                this.pendingQueueComponent.getPendingContentFiles(EDIConstants.PUSH_IN_FILE_DOCUMENT_PREFIX);

        /*
        long v = System.currentTimeMillis();
        //定义当前时间5分钟前的文件名
        String minuteName = DateUtil.format(DateUtil.offsetMinute(DateUtil.date(),-5),"yyyyMMddHHmm");
        String readFtpName = "INJTLZDOCUMENT-"+minuteName+".bcx";
        //读取文件
        String fileContent = FtpUtil.getFileContent(FileModeConfig.getFilePath(),readFtpName);
        if(StringUtils.isEmpty(fileContent)){
            logger.error("====> jtlzDocumentTask.signatureJTLZDocument.no find file("+FileModeConfig.getUserName()+" , "+FileModeConfig.getFilePath()+").readFtpName：" + readFtpName);
            return;
        }
        String[] list = fileContent.split(",");
        count_jtlz_document = list.length;
        logger.error("====> jtlzDocumentTask.signatureJTLZDocument.fileContent.splitLength：" + count_jtlz_document);
        int count = 0;
        for (String filename: list) {
            if(count == 5){
                count = 0;
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            this.taskExecutor.execute(() -> {
                long a = System.currentTimeMillis();
                try{
                    String content = FtpUtil.getFileContent( FileModeConfig.getFilePath(),filename);
                    if(StringUtils.isEmpty(content)){
                        jtlzNullRet(filename);
                    }else{
                        jtlzServer( filename, content);
                    }
                }catch (Exception e){
                    jtlzErrRet(filename,e.getMessage());
                }finally {
                    FtpUtil.delFilename(FileModeConfig.getFilePath(), filename);
                    count_jtlz_document -=1;
                }
            });
            count++;
        }
        //等待30秒，如果线程还没全部完成，则跳出循环
        int i = 0;
        while (count_jtlz_document > 0) {
            if(i==15){
                break;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            i++;
        }
        log.info("========> jtlzDocumentTask.signatureJTLZDocument.end.totalTime："+(System.currentTimeMillis()-v)/1000+"s    ****************************");

         */
    }


    /**
     * <b> 定时任务，每天0点跑，首次程序运行从当前时间减10分钟开始拼文件名并读取和删除文件，
     * 每次循环加1分钟直到10万分钟（循环大概10万次）；
     * 首次执行完后修改监听时间为1500分钟大概1天</b>
     */
    @Async
    public void asyncDeleteBcxFile(int start, int end) {
        Date now = DateUtil.date();
        for (int i = start; i <= end; i++) {
            try {
                Date offsetTime = DateUtil.offsetMinute(now, -i);
                String minuteName = DateUtil.format(offsetTime, "yyyyMMddHHmm");

                String readFtpNameFZ = "INJTLZFZ-" + minuteName + ".bcx";
                String readFtpName = "INJTLZ-" + minuteName + ".bcx";
                String readFtpNameD = "INJTLZDOCUMENT-" + minuteName + ".bcx";

                List<String> contents = new ArrayList<>();

                addIfNotEmpty(contents, FtpUtil.getFileContent(FileModeConfig.getFilePath(), readFtpNameFZ));
                addIfNotEmpty(contents, FtpUtil.getFileContent(FileModeConfig.getFilePath(), readFtpName));
                addIfNotEmpty(contents, FtpUtil.getFileContent(FileModeConfig.getFilePath(), readFtpNameD));

                if (contents.isEmpty()) {
                    continue;
                }

                String[] fileList = String.join(",", contents).split(",");

                for (String filename : fileList) {
                    if (StringUtils.isNotEmpty(filename)) {
                        boolean deleted = FtpUtil.delFilename(FileModeConfig.getFilePath(), filename);
                        if (!deleted) {
                            System.err.println("Failed to delete file: " + filename);
                        }
                    }
                }
            } catch (Exception e) {
                log.error("Error processing minute offset {}", i, e);
            }
        }
    }

    // 辅助方法：仅当内容非空时加入列表
    private void addIfNotEmpty(List<String> list, String content) {
        if (StringUtils.isNotEmpty(content)) {
            list.add(content);
        }
    }

    /**
     * <b> 主方法检查前一天的残留文件处理（每天定时跑） </b>
     * @author ZXF
     * @create 2025/07/14 0014 13:10
     * @version
     * @注意事项 </b>
     */
    public void batchDeleteBcxFile() {
        int totalMinutes = 1440;
        int batchSize = 150; // 每批处理100分钟的数据
        for (int i = 10; i < totalMinutes; i += batchSize) {
            int end = Math.min(i + batchSize - 1, totalMinutes);
            this.asyncDeleteBcxFile(i, end);
        }
    }
    // 主方法检查前100000分钟的残留文件处理（只跑1次）
    /**
     * <b> 京通靓证业务(废止) </b>
     * @author ZXF
     * @create 2024/11/20 0020 17:33
     * @version
     * @注意事项 </b>
     */
    @Override
    public void signatureJTLZ_FZ() {
        long v = System.currentTimeMillis();
        //定义当前时间5分钟前的文件名
        String minuteName = DateUtil.format(DateUtil.offsetMinute(DateUtil.date(),-5),"yyyyMMddHHmm");
        String readFtpName = "INJTLZFZ-"+minuteName+".bcx";
        //读取文件
        String fileContent = FtpUtil.getFileContent(FileModeConfig.getFilePath(),readFtpName);
        if(StringUtils.isEmpty(fileContent)){
            logger.error("====> jtlzFZTask.signatureJTLZ_FZ.no find file("+FileModeConfig.getUserName()+" , "+FileModeConfig.getFilePath()+").readFtpName：" + readFtpName);
            return;
        }

        String[] list = fileContent.split(",");
        int count = 0;
        for (String filename: list) {
            if(count == 5){
                count = 0;
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            this.taskExecutor.execute(() -> {
                try{
                    String content = FtpUtil.getFileContent( FileModeConfig.getFilePath(),filename);
                    if(StringUtils.isEmpty(content)){
                        jtlzNullRet(filename);
                    }else{
                        jtlzFzServer( filename, content);
                    }
                }catch (Exception e){
                    System.err.println("====> jtlzFZTask.signatureJTLZ_FZ.jtlzFzServer.filename:"+filename+"，ERROR：" + e.getMessage());
                }finally {
                    FtpUtil.delFilename(FileModeConfig.getFilePath(), filename);
                }
            });
            count++;
        }
        long s = 59000-((System.currentTimeMillis()-v));
        if(s>0){
            try {
                Thread.sleep(s);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void jtlzNullRet(String filename) {
        String outFilename = filename.replace("IN-", "OUT-");
        String result = "{\"ack_code\":\"FAILURE\",\"errors\":[{\"code\":\"12409\",\"severity\":\"ERROR\",\"message\":\"文件不存在或内容为空\",\"innerCode\":\"\"}],\"sign\":null,\"sign_method\":null,\"timestamp\":null,\"correlation_id\":null,\"response_id\":\"a40e028c-d566-44fc-a2fa-ee4b8df715dc\",\"data\":null}";
        //新内容写入前缀为OUT|的文件
        FtpWriteUtil.uploadBase64(FileWriteConfig.getFilePath(), outFilename, result);
    }
    private void jtlzErrRet(String filename,String msg){
        String outFilename = filename.replace("IN-","OUT-");
        String result = "{\"ack_code\":\"FAILURE\",\"errors\":[{\"code\":\"12409\",\"severity\":\"ERROR\",\"message\":\""+msg+"\",\"innerCode\":\"\"}],\"sign\":null,\"sign_method\":null,\"timestamp\":null,\"correlation_id\":null,\"response_id\":\"a40e028c-d566-44fc-a2fa-ee4b8df715dc\",\"data\":null}";
        //新内容写入前缀为OUT|的文件
        FtpWriteUtil.uploadBase64(FileWriteConfig.getFilePath(), outFilename, result);
    }

    private void jtlzServer(String filename, String content) {
        String outFilename = filename.replace("IN-", "OUT-");
        String[] arr = filename.split("-");

        String item_code = arr[3];
        String result = license_issue(item_code, content);

        FtpWriteConnection ftpWriteConnection = ThreadFtpContext.getFtpWriteConnection();
        //新内容写入前缀为OUT|的文件
        FtpWriteUtil.uploadBase64(ftpWriteConnection, FileWriteConfig.getFilePath(), outFilename, result);
    }

    private String license_issue(String item_code,String body) {
        try {
            // 生成 token 的逻辑
            String token = getLicenseToken();

            String pathApi = ip_address + LICENSE_ISSUE.replace("{item_code}", item_code) + token;
            String ret = HttpUtil.post(pathApi, body);
            if(EDIConstants.isPushError(ret)) {
                logger.error("====> 1.fail to JTLZ schedule task job.license_issue.result.err：(item_code={},pathAPI={}); ret = {}", item_code, pathApi, ret);
            }
            return ret;
        } catch (Exception e) {
            logger.error("====> 2.fail to JTLZ schedule task job.license_issue.err：}", e);

            return "{\"ack_code\":\"FAILURE\",\"errors\":[{\"code\":\"12409\",\"severity\":\"ERROR\",\"message\":\"" + e.getMessage() + "\",\"innerCode\":\"\"}],\"sign\":null,\"sign_method\":null,\"timestamp\":null,\"correlation_id\":null,\"response_id\":\"a40e028c-d566-44fc-a2fa-ee4b8df715dc\",\"data\":null}";
        }
    }

    private void jtlzFzServer(String filename, String content){
        String outFilename = filename.replace("IN-","OUT-");
        String[] arr = filename.split("-");
        String type = arr[2];
        String item_code = arr[3];

        String result = license_issue_fz(item_code,content);
        //新内容写入前缀为OUT|的文件
        FtpWriteUtil.uploadBase64(FileWriteConfig.getFilePath(), outFilename, result);
    }

    private String license_issue_fz(String item_code,String body) {
        // 生成 token 的逻辑
        Exception lastException = null;
        String ret = null;
        String pathApi = null;
        try {
            pathApi = ip_address + LICENSE_ABOLISH.replace("{item_code}", item_code) + getLicenseToken();

            ret = HttpUtil.post(pathApi, body);
            return ret;
        } catch (Exception e) {
            lastException = e;
            return "{\"ack_code\":\"FAILURE\",\"errors\":[{\"code\":\"12409\",\"severity\":\"ERROR\",\"message\":\"" + e.getMessage() + "\",\"innerCode\":\"\"}],\"sign\":null,\"sign_method\":null,\"timestamp\":null,\"correlation_id\":null,\"response_id\":\"a40e028c-d566-44fc-a2fa-ee4b8df715dc\",\"data\":null}";
        } finally {
            logger.error("===> {} 京通靓证信息废止定时任务.license_issue_fz.返回报文(api=>{},body={}):{}",
                    (lastException == null ? "成功" : "异常"),
                    pathApi,
                    body,
                    ret, lastException);
        }
    }

    // 生成 token 的方法（示例方法，实际业务逻辑根据需求实现）
    private String generateToken() {
        // 生成 token 的逻辑
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        Exception lastException = null;
        String token = "";
        try {
            JSONObject json = new JSONObject();
            json.put("app_key", app_key);
            json.put("app_secret", app_secret);
            json.put("account", account);
            json.put("password", password);
            String ret = HttpUtil.post(ip_address + LICENSE_LOGIN, json.toJSONString());
            if (StringUtils.isEmpty(ret)) {
                return token;
            }
            JSONObject result = JSONObject.parseObject(ret);
            String ack_code = result.getString("ack_code");
            token = result.getString("access_token");
            if (!"SUCCESS".equals(ack_code) || StringUtils.isEmpty(token)) {
                System.err.println("====> 京通靓证获取token.license_login.access_token：fail");
                return token;
            }
            // 记录 token 的生成时间
            tokenMap = new HashMap<>();
            tokenMap.put(token, Instant.now());
            return token;
        } catch (Exception e) {
            lastException = e;
            return token;
        } finally {
            stopWatch.stop();

            logger.error("====> {} 京通靓证获取token.license_login. 耗时 {} 秒", (lastException == null ? "成功" : "异常"), lastException, stopWatch.getTotalTimeSeconds());
        }
    }


    /**
     * <b> 京通靓证获取token,每次获取存一天，超时重新获取 </b>
     * @author ZXF
     * @create 2024/11/28 0028 17:59
     * @version
     * @注意事项 </b>
     */
    private String getLicenseToken() {
        // 获取当前时间
        Instant now = Instant.now();
        // 检查 Map 中是否有 token
        if (tokenMap.isEmpty()) {
            logger.error("1，generateToken.tokenMap.size={}", tokenMap.size());
            // 如果没有 token，生成新的 token
            return generateToken();
        }

        // 获取最新的 token 及其生成时间
        String latestToken = null;
        Instant latestTime = null;
        for (Map.Entry<String, Instant> entry : tokenMap.entrySet()) {
            if (latestTime == null || entry.getValue().isAfter(latestTime)) {
                latestToken = entry.getKey();
                latestTime = entry.getValue();
            }
        }

        // 检查 token 是否过期
        Duration duration = Duration.between(latestTime, now);
        if (duration.toHours() >= 6) {
            logger.error("2，use token.hour={}", duration.toHours());
            // 如果 token 过期，生成新的 token
            return generateToken();
        } else {
            logger.error("3，use not expired token");
            // 如果 token 没有过期，直接返回
            return latestToken;
        }
    }


    /**
     * <b> 签章业务 </b>
     * @author ZXF
     * @create 2024/11/20 0020 17:33
     * @version
     * @注意事项 </b>
     */
    private Result sendSignature(String stamp_xy, String baseeContent, String file_name){
        //TODO 调用接口
        try {
            String[] xyArr = stamp_xy.split(",");
            JSONObject json = new JSONObject();
            json.put("appId", appid);
            json.put("file", new File(file_name,baseeContent, "pdf",file_name+" 签章"));
            JSONObject seal = new JSONObject();
            seal.put("sealInfo", new SealInfo(sealno,"FALSE"));
            JSONObject ruleInfo = new JSONObject();
            ruleInfo.put("version", "1.0");
            ruleInfo.put("ruleId", "");
            ruleInfo.put("ruleType", "RECTANGLE");//RECTANGLE-坐标签章、KEYWORD-关键字签章、ACROSS-骑缝章签章（三选一）
            ruleInfo.put("rectangleRule", new RectangleRule(xyArr[0],xyArr[1],xyArr[2],xyArr[3],xyArr[3]));//印章坐标:page 页码，left 左，right 右，top 上，bottom 下
            seal.put("ruleInfo", ruleInfo);
            json.put("seal", seal);
            String ret = HttpUtil.post(api+STAMPFILE, json.toJSONString());
            log.info("====> 文书签章定时任务开始.sendSignature.ret：" + ret);
            if (StringUtils.isEmpty(ret)) {
                return Result.fail("接口请求失败：返回空");
            }
            return JSON.parseObject(ret,Result.class);
        } catch (Exception e){
            log.info("====> 签章业务定时任务开始.sendSignature message:"+e.getMessage());
            return Result.fail("接口请求异常："+e.getMessage());
        }
    }

    /**
     * <b> 验章业务 </b>
     * @author ZXF
     * @create 2024/11/20 0020 17:33
     * @version
     * @注意事项 </b>
     */
    private Result verifySignature(String baseeContent, String file_name){
        //TODO 调用接口
        try {
            JSONObject json = new JSONObject();
            json.put("appId", appid);
            json.put("file", new File(file_name,baseeContent, "pdf",file_name+" 签章"));
            String ret = HttpUtil.post(api+VERIFYFILE, json.toJSONString());
            log.info("====> 文书验章定时任务开始.verifySignature.ret：" + ret);
            if (StringUtils.isEmpty(ret)) {
                return Result.fail("接口请求失败：返回空");
            }
            return JSON.parseObject(ret,Result.class);
        } catch (Exception e){
            log.info("====> 文书验章定时任务开始.verifySignature message:"+e.getMessage());
            return Result.fail("接口请求异常："+e.getMessage());
        }
    }

    @Data
    public static class Result {
        private String status;
        private String message;
        private Object data;

        public Result(String status,String message){
            this.status = status;
            this.message = message;
            this.data = null;
        }
        public static Result fail(String message){
            return new Result("300",message);
        }
    }

    @Data
    public static class File {
        private String title;
        private String fileB64;
        private String filePath;
        private String fileType;
        private String templateData;
        private String classify;
        private String fileDesc;

        public File(String title, String fileB64, String fileType,String fileDesc){
            this.title = title;
            this.fileB64 = fileB64;
            this.fileType = fileType;
            this.fileDesc = fileDesc;
        }
    }

    @Data
    public static class RectangleRule {
        private String page;
        private String bottom;
        private String top;
        private String left;
        private String right;
        private String moveType;
        private JSON offset;

        public RectangleRule(String page,String left,String right,String top,String bottom){
            this.page = page;
            this.bottom = bottom;
            this.top = top;
            this.left = left;
            this.right = right;
        }
    }

    @Data
    public static class SealInfo {
        private String sealId;
        private String timeStamp;
        private String algo;

        public SealInfo(String sealId, String timeStamp){
            this.sealId = sealId;
            this.timeStamp = timeStamp;
            this.algo = "SM3withSM2";
        }
    }

    @Data
    public static class SignatureData {
        private String name;
        private String address;
        private String headphoto;
        private String idcardno;
        private String year;
        private String month;
        private String day;
        private String certificateno;
        private String certificatefrom;
        private String isSignature;
        //发证日期
        private String fzDate;
        private String zYear;
        private String zMonth;
        private String zDay;
        private String signatureImg;
        //未签章pdf存储路径
        private String unSignaturePDF;
        //已签章pdf扫描路径
        private String signaturePDF;
    }

    @Data
    public static class OperatorData {
        private String account;//账号
        private String name;
        private String idcardno;
        private String orgName;//机构名称
        private String roleName;//角色名称
        private String regionName;//归属区域
        private String regionCode;
    }
}
