package com.bcxin.backend.domain.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.bcxin.backend.core.utils.FtpUtils;
import com.bcxin.backend.domain.configs.FileModeConfig;
import com.bcxin.backend.domain.utils.ftp.FtpUtil;
import com.bcxin.backend.domain.utils.ftp.UploadResult;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.openhtmltopdf.extend.FSSupplier;
import com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder;
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
import net.sf.json.JSONObject;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.helper.W3CDom;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.FileTime;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class PDFUtils {

    private static Map<String, String> tempHtmlMap = Maps.newHashMap();

    //初次加载字体流缓存
    private static FSSupplier<InputStream> fSSupplier = null;
    public static void generatePdfByOpenhtmltopdf(String tempUrl, String pdfPath, Object formData) throws IOException {
        String htmlContent = tempHtmlMap.get(tempUrl);
        if (StringUtils.isEmpty(htmlContent)) {
            htmlContent = HttpUtil.get(tempUrl);
            tempHtmlMap.put(tempUrl, htmlContent);
        }
        System.err.println("====> 电子签章（无章PDF生成）定时任务开始：generatePdfByOpenhtmltopdf.htmlContent:"+htmlContent);
        String formStr = JSON.toJSONString(formData);
        System.err.println("====> 电子签章（无章PDF生成）定时任务开始：generatePdfByOpenhtmltopdf.formStr:"+formStr);
        Map<String,String> map = com.alibaba.fastjson.JSONObject.parseObject(formStr,Map.class);
        String zDay = map.get("zDay").substring(0,2);
        htmlContent = htmlContent.replace("${zYear}",map.get("zYear")).replace("${zMonth}",map.get("zMonth")).replace("${zDay}",zDay);
        htmlContent = format(htmlContent, formData);
        System.err.println("====> 电子签章（无章PDF生成）定时任务开始：generatePdfByOpenhtmltopdf.htmlContent:"+htmlContent);
        //加载html文件
        org.jsoup.nodes.Document document = Jsoup.parse(htmlContent);
        document.outputSettings().syntax(org.jsoup.nodes.Document.OutputSettings.Syntax.html);
        //将转换后的pdf文件放到临时目录
        try (OutputStream os = new FileOutputStream(pdfPath)) {
            PdfRendererBuilder builder = new PdfRendererBuilder();
            // 优化渲染过程，减少不必要的处理步骤，从而加快PDF生成的速度
            //  builder.useFastMode();
            builder.withUri(pdfPath);
            builder.toStream(os);
            builder.withW3cDocument(new W3CDom().fromJsoup(document), "");
            //引入指定字体，注意字体名需要和css样式中指定的字体名相同
            builder.useFont(getFSSupplier("STSongti-SC-Regular.TTF"), "STSongti-SC-Regular");
            builder.useDefaultPageSize(160,120, BaseRendererBuilder.PageSizeUnits.MM);

            builder.run();
            System.err.println("====> 电子签章（无章PDF生成）定时任务开始：generatePdfByOpenhtmltopdf.content try--------");
        } catch (IOException e) {
            System.err.println("====> 电子签章（无章PDF生成）定时任务开始：generatePdfByOpenhtmltopdf.error:"+e.getMessage());
            e.printStackTrace();
            throw new IOException("create openhtmltopdf failed!");
        }
        System.err.println("====> 电子签章（无章PDF生成）定时任务开始：generatePdfByOpenhtmltopdf.content end---------");
    }

    private static String format(String content, Object formData) {
        JSONObject data = JSONObject.fromObject(formData);
        String pattern = "\\$\\{(.+?)\\}";
        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(content);
        StringBuffer sb = new StringBuffer();
        while (m.find()) {
            String key = m.group(1);
            Object value = data.get(key);
            m.appendReplacement(sb, value == null ? "" : value.toString());
        }
        m.appendTail(sb);
        return sb.toString();
    }

    private static FSSupplier<InputStream> getFSSupplier(String path) {
        if (fSSupplier == null) {
            fSSupplier = new FSSupplier<InputStream>() {
                @Override
                public InputStream supply() {
                    try {
                        Resource resource = new ClassPathResource(path);
                        return resource.getInputStream();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    return null;
                }
            };
        }
        return fSSupplier;
    }

    /**
     * <b> done地址文件转存到ftp </b>
     * 需要注意
     * 1.文件初始存在服务器目录上：PropertyUtil.getPath()
     * 2.文件转存ftp目录上：FileModeConfig.getFilePath()
     * 3.只要ftp完成文件转存就删除queue、done目录对应文件节省空间
     *
     * @author ZXF
     * @create 2022/11/11 0011 14:07
     * @version
     * @注意事项 </b>
     */
    public static String uploadFtpFile(String pdfPath,String rootPath) {
        String outFilePath = rootPath + pdfPath;
//        String inFilePath = rootPath + pdfPath.replace("/out", "/in");
        //读取临时目录上的文件到ftp对应目录
        File outFile = new File(outFilePath);
//        File inFile = new File(inFilePath);
        File[] fo = outFile.listFiles();
        List<String> successList = new ArrayList<>();
        List<String> failList = new ArrayList<>();
        if(fo.length==0){
            System.err.println("=======>上传ftp 文件：out目录没有有效文件<=======");
            return null;
        }
        JSONObject json = new JSONObject();
        try {
            long a = System.currentTimeMillis();
            List<UploadResult> list = FtpUtil.upload((StrUtil.isEmpty(FileModeConfig.getFilePath()) ? "" : FileModeConfig.getFilePath()) + "/uploads/" + pdfPath.split("/uploads/")[1]
                    , fo);
            //当连接断了，返回值置null，重新上传一遍
            if(list == null){
                System.err.println("=======>上传ftp 文件：连接断了，重新上传一遍<=======");
                list = FtpUtil.upload((StrUtil.isEmpty(FileModeConfig.getFilePath()) ? "" : FileModeConfig.getFilePath()) + "/uploads/" + pdfPath.split("/uploads/")[1]
                        , fo);
            }
            System.err.println("=======>上传ftp 文件："+fo.length+" 个，耗时："+(System.currentTimeMillis()-a)+" 毫秒<=======");
            for (UploadResult result:list) {
                if (result.isResult()) {
                    System.err.println("=======>上传ftp成功："+result.getFileName()+"<=======");
                    //成功的文件把文件名称加入到successFile中,到这里说明这次循环成功了
                    successList.add(result.getFileName());
                }else{
                    System.err.println("=======>上传ftp失败："+result.getFileName()+"<=======");
                    failList.add(result.getFileName());
                }
            }
            /*for (File file : fo) {
                try (FileInputStream fis = new FileInputStream(file)) {
                    long a = System.currentTimeMillis();
                    boolean uploadResult = FtpUtils.bcxUploadFileBatch(FileModeConfig.getHost(),
                            FileModeConfig.getUserName(),
                            FileModeConfig.getPassword(),
                            FileModeConfig.getPort(),
                            (StrUtil.isEmpty(FileModeConfig.getFilePath()) ? "" : FileModeConfig.getFilePath()) + "/uploads/" + pdfPath.split("/uploads/")[1],
                            fis);
                    if (!uploadResult) {
                        System.err.println("=======>上传ftp失败："+file.getName()+"<=======");
                        failList.add(file.getName());
                        continue;
                    }
                    System.err.println("=======>上传ftp成功："+file.getName()+"，耗时："+(System.currentTimeMillis()-a)+"<=======");
                    //成功的文件把文件名称加入到successFile中,到这里说明这次循环成功了
                    successList.add(file.getName());
                } catch (IOException e) {
                    System.err.println("=======>上传ftp异常："+file.getName()+"，ERR："+e.getMessage()+"<=======");
                    failList.add(file.getName());
                }
            }*/
            String successFile = String.join(",", successList);
            String failFile = String.join(",", failList);
            json.put("success",successFile);
            json.put("fail",failFile);
        }catch (Exception e){
            System.err.println("=======>上传ftp失败了："+e.getMessage()+"<=======");
        }/*finally {
            try {
                //无论成功还是失败，最后都要清除in和out目录下的所有文件
                FileUtils.cleanDirectory(inFile);
                FileUtils.cleanDirectory(outFile);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }*/
        return json.toString();
    }

    /**
     * <b> done地址文件转存到/uploads/pdf/dianziqianzhang/日期（2024-10-21） </b>
     * 需要注意
     * 1.文件初始存在服务器目录上：PropertyUtil.getPath()
     * 2.文件转存ftp目录上：FileModeConfig.getFilePath()
     * 3.只要日期目录完成文件转存就删除queue、done目录对应文件节省空间
     *
     * @author ZXF
     * @create 2022/11/11 0011 14:07
     * @version
     * @注意事项 </b>
     */
    public static String uploadDayFile(String pdfPath,String rootPath) {

        String outFilePath = rootPath + pdfPath;
        String dayFilePath = outFilePath.replace("/out", "/"+DateUtil.today());
        File dayFile = new File(dayFilePath);
        if (!dayFile.exists()) {
            dayFile.mkdirs();
        }
        //读取临时目录上的文件到ftp对应目录
        File outFile = new File(outFilePath);
        File[] fo = outFile.listFiles();
        List<String> successList = new ArrayList<>();
        List<String> failList = new ArrayList<>();
        if(fo.length==0){
            System.err.println("=======>上传ftp 文件：out目录没有有效文件<=======");
            return null;
        }

        JSONObject json = new JSONObject();
        try {
            Path filePath;FileTime lastModifiedTime;Instant lastModifiedInstant;Instant now;Duration duration;
            for(int i = 0; i < fo.length; i++){
                String fileName = fo[i].getName();
                String oPath = outFilePath + "/" + fileName;
                filePath = Paths.get(oPath);
                lastModifiedTime = Files.getLastModifiedTime(filePath);
                lastModifiedInstant = Instant.ofEpochMilli(lastModifiedTime.toMillis());
                now = Instant.now();
                duration = Duration.between(lastModifiedInstant, now);
                //大于1分钟没有进行变更的文件才剪切
                if (duration.toMinutes() > 1) {
                    Files.move(Paths.get(oPath), Paths.get(dayFilePath + "/" + fileName), StandardCopyOption.REPLACE_EXISTING);
                    successList.add(fileName);
                }
            }
            String successFile = String.join(",", successList);
            String failFile = String.join(",", failList);
            json.put("success",successFile);
            json.put("fail",failFile);
        }catch (Exception e){
            System.err.println("=======>上传ftp失败了："+e.getMessage()+"<=======");
        }
        return json.toString();
    }
}
