package com.yeejoin.amos.boot.module.ys.biz.utils;

import com.aspose.words.Document;
import com.aspose.words.License;
import com.aspose.words.SaveFormat;
import com.yeejoin.amos.boot.module.ys.api.dto.ByteArrayMultipartFile;
import com.yeejoin.amos.component.feign.model.FeignClientResult;
import com.yeejoin.amos.feign.systemctl.Systemctl;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import org.typroject.tyboot.core.restful.exception.instance.BadRequest;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;

@Slf4j
public class WordTemplateUtils {

    public static final String BASE_PACKAGE_PATH = "/templates";
    private static WordTemplateUtils wordTemplateUtils;
    private final Configuration configuration;

    private WordTemplateUtils() {
        configuration = new Configuration(Configuration.VERSION_2_3_23);
    }

    public static synchronized WordTemplateUtils getInstance() {
        if (wordTemplateUtils == null) {
            wordTemplateUtils = new WordTemplateUtils();
        }
        return wordTemplateUtils;
    }

    /**
     * 创建doc并写入内容
     *
     * @param templatePath doc模板文件路径
     * @param dataMap      内容
     * @param template     模板
     * @return doc文件
     */
    public static File createDoc(String templatePath, Map<String, ?> dataMap, Template template) throws TemplateException, IOException {
        // templatePath在后缀之前加上UUID是为了防止并发时多个线程使用同一个模板文件而导致生成的Word文档内容不一致
        int i = templatePath.lastIndexOf(".");
        templatePath = UUID.randomUUID() + templatePath.substring(i);

        if (templatePath.endsWith(".ftl")) {
            templatePath = templatePath.replace(".ftl", ".doc");
        }
        File docFile = new File(templatePath);
        try (
                // 这个地方不能使用FileWriter因为需要指定编码类型否则生成的Word文档会因为有无法识别的编码而无法打开
                Writer writer = new OutputStreamWriter(Files.newOutputStream(docFile.toPath()), StandardCharsets.UTF_8)
        ) {
            template.process(escapeSpecialCharacters(dataMap), writer);
        }
        return docFile;
    }

    public File fillAndConvertDocFile(String templatePath, String targetFileName, Map<String, ?> map, int saveFormat) throws Exception {
        // 指定模板所在包路径
        configuration.setClassForTemplateLoading(this.getClass(), BASE_PACKAGE_PATH);

        // 获取模板, 生成Word文档
        Template freemarkerTemplate = configuration.getTemplate(templatePath, "UTF-8");
        File docFile = createDoc(templatePath, map, freemarkerTemplate);

        // 转换Word文档
        File converedFile = converDocFile(docFile.getAbsolutePath(), targetFileName, saveFormat);

        // 删除临时文件
        Files.deleteIfExists(docFile.toPath());

        return converedFile;
    }

    /**
     * word转换
     *
     * @param docPath    word文件路径
     * @param targetPath 转换后文件路径
     * @param saveFormat 目标文件类型 取自 com.aspose.words.SaveFormat
     */
    private File converDocFile(String docPath, String targetPath, int saveFormat) throws Exception {
        // 验证License 若不验证则转化出的pdf文档会有水印产生
        if (!getLicense()) {
            throw new RuntimeException("验证License失败");
        }

        if (StringUtils.isEmpty(docPath)) {
            throw new FileNotFoundException("文档文件不存在");
        }

        try (
                InputStream inputStream = Files.newInputStream(Paths.get(docPath));
                OutputStream outputStream = Files.newOutputStream(Paths.get(targetPath));
        ) {
            File targetFile = new File(targetPath);
            Document doc = new Document(inputStream);
            doc.save(outputStream, saveFormat);
            return targetFile;
        }
    }


    /**
     * 获取License
     *
     * @return boolean
     */
    private boolean getLicense() {
        boolean result = false;
        try {
            String s = "<License><Data><Products><Product>Aspose.Total for Java</Product><Product>Aspose.Words for Java</Product></Products><EditionType>Enterprise</EditionType><SubscriptionExpiry>20991231</SubscriptionExpiry><LicenseExpiry>20991231</LicenseExpiry><SerialNumber>8bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7</SerialNumber></Data><Signature>sNLLKGMUdF0r8O1kKilWAGdgfs2BvJb/2Xp8p5iuDVfZXmhppo+d0Ran1P9TKdjV4ABwAgKXxJ3jcQTqE/2IRfqwnPf8itN8aFZlV3TJPYeD3yWE7IT55Gz6EijUpC7aKeoohTb4w2fpox58wWoF3SNp6sK6jDfiAUGEHYJ9pjU=</Signature></License>";
            ByteArrayInputStream is = new ByteArrayInputStream(s.getBytes());
            License license = new License();
            license.setLicense(is);
            result = true;
        } catch (Exception e) {
            log.error("获取License失败", e);
        }
        return result;
    }

    public static String templateToPdf(String pdfName, String wordPath, Map<String, Object> placeholders) {
        // word转pdf
        File pdfFile;
        try {
            pdfFile = wordToPdf(pdfName, wordPath, placeholders);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        // 上传pdf至文件服务器
        String url = uploadFile(pdfFile);

        // 删除临时文件
        try {
            Files.deleteIfExists(pdfFile.toPath());
        } catch (IOException e) {
            log.error("删除临时文件失败：{}", e);
        }
        return url;
    }

    public static void templateToPdfDownload(String pdfName, String wordPath, Map<String, Object> placeholders, HttpServletResponse response) {
        // word转pdf
        File pdfFile;
        try {
            pdfFile = wordToPdf(pdfName, wordPath, placeholders);
        } catch (Exception e) {
            log.error("模板转pdf失败:", e);
            throw new BadRequest("模板转pdf失败");
        }

        try {
            byte[] bytes = file2byte(pdfFile);
            String docTitle = pdfFile.getName();
            FileExporter.exportFile(FileExporter.FileType.valueOf("pdf"), docTitle, bytes, response);
        } catch (Exception e) {
            log.error("文档导出失败:", e);
        } finally {
            try {
                Files.deleteIfExists(pdfFile.toPath());
            } catch (Exception e) {
                log.error("文件找不到，删除失败:", e);
            }
        }
    }


    private static byte[] file2byte(File file) {
        try {
            FileInputStream in = new FileInputStream(file);
            //当文件没有结束时，每次读取一个字节显示
            byte[] data = new byte[in.available()];
            in.read(data);
            in.close();
            return data;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 上传文件至文件服务器
     *
     * @param file 文件
     */
    private static String uploadFile(File file) {
        Assert.notNull(file, "文件不能为空");

        MultipartFile multipartFile = new ByteArrayMultipartFile("file", "file.pdf", "application/pdf", file2byte(file));
        FeignClientResult<Map<String, String>> result = Systemctl.fileStorageClient.updateCommonFile(multipartFile);
        String urlString = "";
        if (result != null) {
            for (String s : result.getResult().keySet()) {
                urlString = s;
            }
        }
        return urlString;
    }


    /**
     * word 转 pdf
     *
     * @param wordPath word文件路径
     */
    private static File wordToPdf(String pdfName, String wordPath, Map<String, Object> placeholders) throws Exception {
        Assert.hasText(wordPath, "word文件路径不能为空");

        WordTemplateUtils instance = WordTemplateUtils.getInstance();

        return instance.fillAndConvertDocFile(wordPath, pdfName, placeholders, SaveFormat.PDF);
    }

    /**
     * 下载填充模板的字段，特殊符号转义
     */
    public static Map<String, Object> escapeSpecialCharacters(Map<String, ?> inputMap) {
        Map<String, Object> escapedMap = new HashMap<>();
        for (Map.Entry<String, ?> entry : inputMap.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            if (value instanceof String) {
                escapedMap.put(key, escapeValue((String) value));
            } else if (value instanceof Map) {
                escapedMap.put(key, escapeSpecialCharacters((Map<String, Object>) value));
            } else if (value instanceof List) {
                escapedMap.put(key, escapeList((List<?>) value));
            } else {
                escapedMap.put(key, value);
            }
        }
        return escapedMap;
    }

    private static List<Object> escapeList(List<?> inputList) {
        List<Object> escapedList = new ArrayList<>();
        for (Object value : inputList) {
            if (value instanceof String) {
                escapedList.add(escapeValue((String) value));
            } else if (value instanceof Map) {
                escapedList.add(escapeSpecialCharacters((Map<String, Object>) value));
            } else if (value instanceof List) {
                escapedList.add(escapeList((List<?>) value));
            } else {
                escapedList.add(value);
            }
        }
        return escapedList;
    }

    private static String escapeValue(String value) {
        if (value == null) {
            return null;
        }
        return value.replace("&", "&amp;")
                .replace("<", "&lt;")
                .replace(">", "&gt;")
                .replace("\"", "&quot;")
                .replace("'", "&apos;")
                .replace("(", "&#40;")
                .replace(")", "&#41;");

    }
}
