package com.bcxin.backend.domain.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StopWatch;

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 final Logger logger = LoggerFactory.getLogger(PDFUtils.class);
    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);
        }
        String formStr = JSON.toJSONString(formData);
        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);
        //加载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(180,135, BaseRendererBuilder.PageSizeUnits.MM);

            builder.run();
        }
    }

    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) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        String uploadFilePath = null;
        JSONObject json = new JSONObject();

        StringBuilder sb = new StringBuilder();

        try {
            String outFilePath = rootPath + pdfPath;
            //读取临时目录上的文件到ftp对应目录
            File outFile = new File(outFilePath);
            File[] fo = outFile.listFiles();

            if (fo == null || fo.length == 0) {
                logger.error("所在目录({})的文件列表为空", outFile);
                return null;
            }

            List<String> successList = new ArrayList<>();
            List<String> failList = new ArrayList<>();

            uploadFilePath = (StrUtil.isEmpty(FileModeConfig.getFilePath()) ? "" : FileModeConfig.getFilePath()) + "/uploads/" + pdfPath.split("/uploads/")[1];
            List<UploadResult> list = FtpUtil.upload(uploadFilePath, fo);


            //当连接断了，返回值置null，重新上传一遍
            if (list == null) {
                sb.append(String.format("获取到的uploadFilePath=%s;list=null;",uploadFilePath));
                uploadFilePath = (StrUtil.isEmpty(FileModeConfig.getFilePath()) ? "" : FileModeConfig.getFilePath()) + "/uploads/" + pdfPath.split("/uploads/")[1];
                list = FtpUtil.upload(uploadFilePath, fo);

                sb.append(String.format("重新获取的uploadFilePath=%s;",uploadFilePath));
            }

            if (!CollectionUtils.isEmpty(list)) {
                sb.append(String.format("重新获取的uploadFilePath.list=%s;",list.size()));
                for (UploadResult result : list) {
                    if (result.isResult()) {
                        logger.info("======>文件上传FTP成功({})", result.getFileName());
                        //成功的文件把文件名称加入到successFile中,到这里说明这次循环成功了
                        successList.add(result.getFileName());
                    } else {
                        logger.error("======>文件上传FTP失败({})", result.getFileName());
                        failList.add(result.getFileName());
                    }
                }

                String successFile = String.join(",", successList);
                String failFile = String.join(",", failList);
                json.put("success", successFile);
                json.put("fail", failFile);
            }else {
                sb.append("重新获取的uploadFilePath.list=EMPTY;");
            }
        } catch (Exception e) {
            logger.error("======>上传ftp失败:{}", uploadFilePath, e);
        } finally {
            stopWatch.stop();
            logger.error("执行uploadFtpFile的跟踪信息:{}; 总耗时{}秒", sb, stopWatch.getTotalTimeSeconds());
        }

        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==null || 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();
    }
}
