package com.bcxin.backend.certificateSignatures.impls;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.bcxin.backend.ProviderAbstract;
import com.bcxin.backend.certificateSignatures.CertificateConfigProperty;
import com.bcxin.backend.certificateSignatures.CertificateSignatureProvider;
import com.bcxin.backend.certificateSignatures.impls.values.SignatureData;
import com.bcxin.backend.certificateSignatures.impls.values.SignaturePdfResult;
import com.bcxin.backend.domain.models.SignatureQueuesDTO;
import com.bcxin.backend.domain.repositories.SignatureQueuesRepository;
import com.bcxin.backend.domain.services.impls.ConvertServiceImpl;
import com.bcxin.backend.domain.signature.service.impls.vos.CertificateVo;
import com.bcxin.backend.domain.syncs.dtos.SharedConvertDto;
import io.jsonwebtoken.lang.Collections;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StopWatch;

import javax.transaction.Transactional;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;

public abstract class CertificateSignatureProviderAbstract extends ProviderAbstract implements CertificateSignatureProvider {
    protected final CertificateConfigProperty configProperty;
    protected final SignatureQueuesRepository signatureQueuesRepository;
    protected final JdbcTemplate jdbcTemplate;
    protected final ConvertServiceImpl convertService;

    public CertificateSignatureProviderAbstract(CertificateConfigProperty configProperty,
                                                SignatureQueuesRepository signatureQueuesRepository,
                                                JdbcTemplate jdbcTemplate,
                                                ConvertServiceImpl convertService) {
        this.configProperty = configProperty;
        this.signatureQueuesRepository = signatureQueuesRepository;
        this.jdbcTemplate = jdbcTemplate;
        this.convertService = convertService;
    }

    @Override
    public void step1_prepareData() {
        String urlHead = configProperty.getUrlHead();
        String mobanPDF = configProperty.getMobanPDF();
        String sql =
                "select id,item_securityname,item_documentid,item_idaddress,item_certificateno,item_certificatefrom," +
                        " item_certificatedate,item_headphoto,created,item_signatured_processed_status,author " +
                        " from tlk_certificate where ITEM_signatured_processed_status IN (0) and " +
                        " ITEM_CERTIFICATETYPE='1' and ITEM_isCertified != '0' " +
                        " order by item_certificateDate desc, ITEM_LAST_SIGNATURED_PROCESSED_TIME asc limit 300";
        List<CertificateVo> list = jdbcTemplate.query(sql, new RowMapper<CertificateVo>() {
            @Override
            public CertificateVo mapRow(ResultSet rs, int rowNum) throws SQLException {
                return CertificateVo.create(
                        rs.getString("id"),
                        rs.getString("item_securityname"),
                        rs.getString("item_documentid"),
                        rs.getString("item_idaddress"),
                        rs.getString("item_certificateno"),
                        rs.getString("item_certificatefrom"),
                        rs.getString("item_certificatedate"),
                        rs.getString("item_headphoto"),
                        rs.getString("created"),
                        rs.getString("item_signatured_processed_status"),
                        rs.getString("author")
                );
            }
        });

        getLogger().error("获取到待签章的资格证数量:{}", list.size());
        if (!CollectionUtils.isEmpty(list)) {
            for (CertificateVo certificateVo : list) {
                String securityname = certificateVo.getItem_securityname();
                String documentid = certificateVo.getItem_documentid();
                String IdAddress = certificateVo.getItem_idaddress();
                String certificateno = certificateVo.getItem_certificateno();
                String certificatefrom = certificateVo.getItem_certificatefrom();

                //因为都是实名认证过了, 因此，无需检测身份证格式的有效性
                if (certificateVo.doCheck(urlHead)) {
                    String headphoto = certificateVo.getHeadPhoto();
                    String year = String.valueOf(certificateVo.getYearFromIdCard());
                    String month = String.valueOf(certificateVo.getMonthFromIdCard());
                    String day = String.valueOf(certificateVo.getDayFromIdCard());
                    String certificatedate = certificateVo.getCertificatedate();
                    String json = "{\"name\":\"" + securityname + "\",\"author\":\"" + certificateVo.getAuthor() +"\",\"address\":\"" + IdAddress + "\",\"headphoto\":\"" + headphoto + "\",\"idcardno\":\"" + documentid + "\",\"year\":\"" + year + "\",\"month\":\"" + month + "\",\"day\":\"" + day + "\",\"certificateno\":\"" + certificateno + "\",\"certificatefrom\":\"" + certificatefrom + "\",\"isSignature\":\"1\",\"fzDate\":\"" + certificatedate + "\"}";
                    certificateVo.assignProcessJson(json);
                }
            }

            String moban = mobanPDF;
            for (CertificateVo.ProcessStatus status : CertificateVo.ProcessStatus.values()) {
                Collection<CertificateVo>
                        selectedCertificateVos
                        = list.stream().filter(ii -> ii.getProcessStatus() == status)
                        .collect(Collectors.toList());
                String executeSql = null;
                StringBuilder trace = new StringBuilder();
                trace.append(String.format("执行签章的status=%s;size=%s", status.getText(), selectedCertificateVos.size()));
                try {
                    if (!CollectionUtils.isEmpty(selectedCertificateVos)) {
                        executeSql = status.getSql();
                        String idParams =
                                selectedCertificateVos.stream()
                                        .map(ii -> String.format("'%s'", ii.getId()))
                                        .collect(Collectors.joining(","));
                        executeSql = String.format("%s where id in (%s)", executeSql, idParams);

                        trace.append(String.format("status=%s;sql=%s;", status, executeSql));
                        if (status == CertificateVo.ProcessStatus.SUCCESS) {
                            String insertSql = status.getInsertSql();
                            List<Object[]> params = new ArrayList<>();
                            //INSERT INTO obpm2_security.signature_queues (version,template_url,DATA,STATUS,business_id,createdTime,creator,certificatedate)V
                            for (CertificateVo vi : selectedCertificateVos) {
                                Object[] itemPs = new Object[]{
                                        "2.0",
                                        moban,
                                        vi.getProcessJson(),
                                        0,
                                        vi.getId(),
                                        Timestamp.from(Instant.now()),
                                        "",
                                        vi.getCertificatedate()
                                };
                                params.add(itemPs);
                            }
                            /**
                             * 针对数据是有效的情况, 插入数据库
                             */
                            this.jdbcTemplate.batchUpdate(insertSql, params);
                        }

                        /**
                         * 更改状态; 包括: 数据都是有效的情况
                         */
                        this.jdbcTemplate.batchUpdate(executeSql);
                    }
                } catch (Exception ex) {
                    getLogger().error("执行SQL({})发生异常", executeSql, ex);
                } finally {
                    getLogger().error("跟踪资格证执行情况:{}", trace);
                }
            }
        }
    }

    /**
     * 根据queue的Id到getTempPDF目录下生成PDF
     * data.setUnSignaturePDF(tempPDF + "/" + System.currentTimeMillis() + "queueId_" + queue.getId() + ".temp");
     */
    @Override
    public void step2_generatePdf() {
        Page<SignatureQueuesDTO> pageQueues = signatureQueuesRepository.pageQueues(0, PageRequest.of(0, 100));
        List<SignatureQueuesDTO> queues = pageQueues.getContent();
        if (Collections.isEmpty(queues)) {
            getLogger().info("====> 电子签章定时任务开始.createPDF.queues.无业务数据 ---------------------");
            return;
        }
        getLogger().info("====> 电子签章定时任务开始.createPDF.queues.size()=" + queues.size());
        for (SignatureQueuesDTO queuesDTO : queues) {
            generatePdfAndUpdateStatus(queuesDTO);
        }
    }

    /**
     * 执行签章操作
     */
    @Override
    public void step3_doSignature() {
        String tempPDF = configProperty.getTempPDF();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("tempUrl", tempPDF);
        jsonObject.put("formData", 230);

        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        try {
            //到文件夹剪切一个文件到out目录
            SharedConvertDto sharedConvertDto = new SharedConvertDto();
            String tempUrl = jsonObject.getString("tempUrl");
            String formData = jsonObject.getString("formData");
            sharedConvertDto.setTempUrl(tempUrl);
            sharedConvertDto.setFormData(formData);
            /**
             * 原本的逻辑是： 如果out目录底下有in目录中的文件，则删除in目录中的对应文件
             * todo: 这部分可能存在不严谨问题
             */
            new Thread(() -> {
                try {
                    //先检索in目录存在的删除
                    convertService.delInPDF(sharedConvertDto);
                } catch (Exception e) {
                    getLogger().error("====> 电子签章定时任务开始.signQuicklyExCGI签章前比对in文件删除：", e);
                }
            }).start();

            //剪切文件到in目录下
            convertService.existsPdfCopy(sharedConvertDto);

            //每次盖章前先清除0kb文件
            //convertService.delInPDF(tempPDF);

            //先触发对需要签章目录下文件进行签章
            activateXMPSignQuicklyExCGI();

            stopWatch.stop();
            getLogger().info("====> 电子签章定时任务开始.signQuicklyExCGI.签章接口耗时：{} 秒", stopWatch.getTotalTimeSeconds());
        } catch (Exception e) {
            getLogger().error("资格证签章(tempPDF={})执行异常", tempPDF, e);
        }
    }

    /**
     * 将已签章的文件上传到百保盾监管系统的目录并更改证书信息表及状态
     */
    @Override
    public void step4_moveOutDocument2BusinessDirAndFinishSignature() {
        String tempPDF = configProperty.getTempPDF();
        StopWatch topWatch = new StopWatch();
        topWatch.start();
        String tracId = UUID.randomUUID().toString();
        try {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            String pdfFile = "/uploads/pdf/dianziqianzhang/out";
            SharedConvertDto sharedConvertDto = new SharedConvertDto();
            sharedConvertDto.setTempUrl(pdfFile);
            sharedConvertDto.setFormData(null);
            String result = convertService.existsScanSignature(sharedConvertDto);
            if (StringUtils.isEmpty(result)) {
                getLogger().error("====> 电子签章定时任务开始(tempUrl={}).scan.result：空", sharedConvertDto.getTempUrl());
                return;
            }
            stopWatch.stop();
            getLogger().error("====> {} 电子签章上传至FTP-结束耗时：{} 秒", tracId, stopWatch.getTotalTimeSeconds());

            stopWatch.start();
            JSONObject json = JSONObject.parseObject(result);
            Object success = json.get("success");
            Object fail = json.get("fail");
            List<String> failList = null;
            String successFile = "";
            String failFile = "";
            if (success != null) {
                successFile = (String) success;
            }
            if (fail != null) {
                failFile = (String) fail;
                if (StringUtils.isNotEmpty(failFile)) {
                    String[] failS = failFile.replace(".pdf", "").split("queueId_")[1].split(",");
                    if (failS.length > 0) {
                        failList = Arrays.asList(failS);
                    }
                }
            }
            String fileNames = successFile + failFile;
            /**
             * todo: 待优化
             */
            new Thread(() -> {
                try {
                    //只要ftp有跑过的都要比对删除
                    convertService.delSetFtpFile(tempPDF, fileNames);
                } catch (Exception e) {
                    getLogger().error("====> {}电子签章定时任务开始.scan.ftp操作过的文件删除：", tracId, e);
                }
            }).start();

            if (StringUtils.isNotEmpty(successFile)) {
                //成功
                String[] successSplit = successFile.split(",");
                getLogger().error("{} 成功签章的数量:{}",tracId, successSplit.length);

                Arrays.stream(successSplit).parallel()
                        .forEach(string -> {
                            String successFilePath = pdfFile + "/" + string;
                            String id = string.split("queueId_")[1].replace(".pdf", "");
                            String business_id = signatureQueuesRepository.getBuId(id);
                            String updateSql = "UPDATE tlk_certificate SET ITEM_SIGNATURED_PROCESSED_STATUS=\"2\",LASTMODIFIED=NOW(),ITEM_LAST_SIGNATURED_PROCESSED_TIME=NOW(),ITEM_CERTIFICATEDATE=IFNULL(ITEM_CERTIFICATEDATE,NOW()),ITEM_LAST_SIGNATURED_PROCESSED_RESULT=\"完成\",ITEM_ATTACHMENT=\"" + successFilePath + "\" WHERE ID=\"" + business_id + "\";";
                            jdbcTemplate.update(updateSql);
                            signatureQueuesRepository.updateSignature(1, new Date(), "成功", id);
                        });
            }

            if (failList != null) {
                getLogger().error("{} 签章失败的数量:{}", tracId, failList.size());
                signatureQueuesRepository.updateSignatures(2, new Date(), "未成功签章", failList);
            }

            stopWatch.stop();
            getLogger().warn("====> {} 电子签章定时任务开始.scan.50个PDF已签章文件存FTP耗时：{} 秒", tracId, stopWatch.getTotalTimeSeconds());
        } catch (Exception e) {
            getLogger().error("====> {} 电子签章定时任务开始.scan.updateSql error：", tracId, e);
        } finally {
            topWatch.stop();
            getLogger().error("{} step4_moveOutDocument2BusinessDirAndFinishSignature 总签章耗时：{} 秒", tracId, topWatch.getTotalTimeSeconds());
        }
    }

    @Override
    public void step5_doneForExceptionData() {
        String updateSql =
                "update obpm2_security.signature_queues set status = '0' where status = '3' and TIMESTAMPDIFF(HOUR, last_processed_time, NOW()) > 3";
        jdbcTemplate.update(updateSql);
    }

    /**
     * 生成资格证签章的时候; 无效再次判断头像是否存在, 因为加入队列的时候, 系统已经判断
     *
     * @param queue
     */
    @Transactional
    protected void generatePdfAndUpdateStatus(SignatureQueuesDTO queue) {
        try {
            SignaturePdfResult result = generateSignaturePdf(queue);
            if(!result.isIgnored()) {
                String message = result.getMessage();
                if (StringUtils.isNotEmpty(result.getResult())) {
                    if (StringUtils.isBlank(message)) {
                        message = "PDF已生成待签章";
                    }
                    //成功
                    signatureQueuesRepository.updateSignature(3, new Date(), message, queue.getId());
                } else {
                    if (StringUtils.isBlank(message)) {
                        message = String.format("PDF生成失败:%s", result);
                    }
                    //失败
                    signatureQueuesRepository.updateSignature(2, new Date(), message, queue.getId());
                }

                getLogger().error(
                        "完成PDF的生成(id={},businessId={},状态={}, 详情={})",
                        queue.getBusiness_id(), queue.getBusiness_id(), StringUtils.isNotEmpty(result.getResult()) ? "成功" : "失败",
                        result
                );
            }
        } catch (Exception e) {
            getLogger().error("签章异常(queueId={},签章数据={})PDF的生成", queue.getId(), queue.getData(), e);
            //数据异常，删除签章记录，同时更新业务表的状态
            signatureQueuesRepository.deleteById(queue.getId());
            String updateSql = "UPDATE tlk_certificate SET ITEM_SIGNATURED_PROCESSED_STATUS=\"3\",ITEM_LAST_SIGNATURED_PROCESSED_TIME=NOW(),ITEM_LAST_SIGNATURED_PROCESSED_RESULT=\"签章失败：" + e.getMessage() + "\" WHERE ID=\"" + queue.getBusiness_id() + "\";";
            jdbcTemplate.update(updateSql);
        }
    }

    protected boolean activateXMPSignQuicklyExCGI() {
        return true;
    }

    /**
     * 该逻辑默认为北京的逻辑
     * 其他差异化的省份, 将从此进行override
     * @param queue
     * @return
     * @throws IOException
     */
    protected SignaturePdfResult generateSignaturePdf(SignatureQueuesDTO queue) throws IOException {
        String tempPDF = configProperty.getTempPDF();
        String urlHead = configProperty.getUrlHead();
        SignatureData data =
                JSONObject.parseObject(queue.getData(), SignatureData.class);

        getLogger().info("====> 电子签章定时任务开始.createPDF.accept.单次业务：001：" + queue.getData());
        //先将文件命名为.temp
        data.setUnSignaturePDF(tempPDF + "/" + System.currentTimeMillis() + "queueId_" + queue.getId() + ".temp");
        String toDay = data.getFzDate();
        if (StringUtils.isEmpty(toDay)) {
            toDay = DateUtil.today();
        }

        data.setZYear(toDay.split("-")[0]);
        data.setZMonth(toDay.split("-")[1]);
        data.setZDay(toDay.split("-")[2]);
        data.setHeadphoto(data.getHeadphoto().startsWith("http") ? data.getHeadphoto() : urlHead + data.getHeadphoto());
        SharedConvertDto sharedConvertDto = new SharedConvertDto();
        sharedConvertDto.setTempUrl(queue.getTemplate_url());
        sharedConvertDto.setFormData(data);
        //最终输出为.pdf文件，主要是为了避免pdf 0kb时就被剪切走
        String result = convertService.execConvertHtmltopdfForTemp(sharedConvertDto);

        return SignaturePdfResult.create(result, "");
    }
}
