package com.ruoyi.common.utils.office.doc;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ReflectUtil;
import com.lowagie.text.Font;
import com.lowagie.text.pdf.BaseFont;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.project.business.domain.vo.MaterialImageVO;
import fr.opensagres.poi.xwpf.converter.pdf.PdfConverter;
import fr.opensagres.poi.xwpf.converter.pdf.PdfOptions;
import fr.opensagres.xdocreport.converter.ConverterTypeTo;
import fr.opensagres.xdocreport.converter.ConverterTypeVia;
import fr.opensagres.xdocreport.converter.Options;
import fr.opensagres.xdocreport.core.XDocReportException;
import fr.opensagres.xdocreport.document.IXDocReport;
import fr.opensagres.xdocreport.document.images.IImageProvider;
import fr.opensagres.xdocreport.document.registry.XDocReportRegistry;
import fr.opensagres.xdocreport.itext.extension.font.ITextFontRegistry;
import fr.opensagres.xdocreport.template.IContext;
import fr.opensagres.xdocreport.template.TemplateEngineKind;
import fr.opensagres.xdocreport.template.formatter.FieldsMetadata;

import java.awt.*;
import java.io.*;
import java.nio.file.Files;
import java.util.*;
import java.util.List;
import java.util.function.Consumer;
import javax.servlet.http.HttpServletResponse;

import fr.opensagres.xdocreport.template.formatter.NullImageBehaviour;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.openxml4j.util.ZipSecureFile;
import org.apache.poi.xwpf.usermodel.XWPFDocument;


@Slf4j
public class CommonDocUtils {

    private IXDocReport report;

    private IContext context;

    private FieldsMetadata metadata;

    public static final String GEN_DIRECTORY = "wordTemp";

    /***************************************** ↓初始文件加载↓ ********************************************************/

    /**
     * 从类路径加载
     *
     * @param path 文件路径
     * @return
     */
    public CommonDocUtils loadFromClasspath(String path) {
        try (InputStream is = this.getClass().getResourceAsStream(path)) {
            report = XDocReportRegistry.getRegistry().loadReport(is, UUID.randomUUID().toString(), TemplateEngineKind.Freemarker );
            report.setFieldsMetadata(new FieldsMetadata());
            context = report.createContext();
            metadata = report.createFieldsMetadata();
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (XDocReportException e) {
            throw new RuntimeException(e);
        }
        return this;
    }

    /***************************************** ↓文件内容修改↓ ********************************************************/

    /**
     * 需要使用word的添加域-邮件合并-mergefield
     *
     * @param data
     * @return
     */
    public CommonDocUtils replaceData(Map<String, Object> data) {
        if (CollectionUtil.isEmpty(data)) {
            return this;
        }
        FieldsMetadata metadata = report.createFieldsMetadata();
        data.keySet().forEach(key -> {
            Object value = data.get(key);
            context.put(key, value);
            if (value instanceof List){
                metadata.addFieldAsList(key);
            } else if (value instanceof IImageProvider){
                metadata.addFieldAsImage(key, NullImageBehaviour.RemoveImageTemplate);
            }
        });
        try {
            metadata.load( "images", MaterialImageVO.class, true );
            metadata.addFieldAsImage("preview");
        } catch (XDocReportException e) {
            throw new RuntimeException(e);
        }
        return this;
    }

    /**
     * 替换表格数据
     *
     * @param data
     * @return
     */
    public CommonDocUtils replaceTableData(Map<String, List<List<String>>> data) {
        if (CollectionUtil.isEmpty(data)) {
            return this;
        }
        data.keySet().forEach(key -> {
            try {
                metadata.load(key, CommonTableRow.class, true );
                List<List<String>> tableData = data.get(key);
                List<CommonTableRow> rows = new ArrayList<>();
                for (int i=0;i<tableData.size();i++){
                    List<String> rowData = tableData.get(i);
                    CommonTableRow row = new CommonTableRow();
                    rows.add(row);
                    for (int j=0;j<rowData.size();j++){
                        ReflectUtil.setFieldValue(row, "col" + j, rowData.get(j));
                    }
                }
                context.put(key, rows);
            } catch (XDocReportException e) {
                throw new RuntimeException(e);
            }
        });

        return this;
    }

    /***************************************** ↓获取最终文件↓ ********************************************************/
//
//    /**
//     * 将word转存为pdf
//     * @param path pdf文件路径
//     */
//    public void saveToPdf(String path) {
//        DocumentConverter converter = null;
//        try {
//            converter = SpringContextHolder.getBean(DocumentConverter.class);
//        } catch (Exception e){
//            throw new ServiceErrorException("未找到有效的office程序，无法生成pdf文件");
//        }
//
//        String basePath = path.substring(0, path.lastIndexOf("/"));
//        //指定文件上传的目录
//        File file1 = new File(basePath);
//        //如果目录不存在 递归创建
//        if(!file1.exists()){
//            file1.mkdirs();
//        }
//        File temp = null;
//        try {
//            temp = File.createTempFile("temp", ".tmp");
//        } catch (IOException e) {
//            throw new RuntimeException(e);
//        }
//        try(OutputStream tempOut = new FileOutputStream(temp);
//            InputStream tempIn = new FileInputStream(temp);
//            OutputStream out = new FileOutputStream(path);){
//            long start = System.currentTimeMillis();
//            report.process(context, tempOut);
//
//            long wordEnd = System.currentTimeMillis();
    //            final DocumentFormat targetFormat = DefaultDocumentFormatRegistry.getFormatByExtension("pdf");
    //            converter.convert(tempIn).to(out).as(targetFormat).execute();
    //
    //            temp.delete();
    //            long pdfEnd = System.currentTimeMillis();
    //            log.info("PDF文件生成成功 路径：{} word生成耗时：{}ms pdf转换耗时：{}ms", path, wordEnd-start, pdfEnd-wordEnd);
    //        } catch (Exception e) {
    //            throw new RuntimeException(e);
    //        }
    //    }

    /***************************************** ↓获取最终文件↓ ********************************************************/

    /**
     * 下载文件
     */
    public void download() {
        HttpServletResponse response = ServletUtils.getResponse();
        response.setContentType("application/octet-stream");
        File temp = null;
        try {
            temp = File.createTempFile("temp", ".tmp");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        ZipSecureFile.setMinInflateRatio(0);
        try(OutputStream tempOut = new FileOutputStream(temp);
            OutputStream out = response.getOutputStream();){
            report.process(context, tempOut);
            PdfOptions pdfOptions = PdfOptions.create();
            Options options = Options.getTo(ConverterTypeTo.PDF).via(ConverterTypeVia.XWPF).subOptions(pdfOptions);
            report.convert(context, options, out);
            temp.delete();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void downloadDocx() {
        HttpServletResponse response = ServletUtils.getResponse();
        response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
        //response.setHeader("Content-Disposition", "attachment; filename=\"123.docx\""); // 设置文件名
        try (OutputStream out = response.getOutputStream()) {
            // 直接将报告内容写入输出流，不进行转换
            // 调用报告处理方法并记录日志
            report.process(context, out);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public File genFile(String quotationNo) {
        createWordDirectory();
        String filePath =
            getGenDirectory() + File.separator + "报价_" + quotationNo + "_" + DateUtils.parseDateToStr(
                "yyyyMMddHHmmssSSS", new Date())
                + ".docx";
        File f = new File(filePath);
        try (OutputStream out = Files.newOutputStream(f.toPath())) {
            report.process(context, out);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return f;
    }

    private static void createWordDirectory() {
        File file = new File(getGenDirectory());
        if (!file.exists()) {
            file.mkdirs();
        }
    }

    private static String getGenDirectory() {
        return GEN_DIRECTORY + File.separator + DateUtils.dateTime();
    }

}
