package com.yaoyicloud.render;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.config.ConfigureBuilder;
import com.deepoove.poi.data.ByteArrayPictureRenderData;
import com.deepoove.poi.data.HyperlinkTextRenderData;
import com.deepoove.poi.data.PictureType;
import com.deepoove.poi.data.style.PictureStyle;
import com.deepoove.poi.exception.RenderException;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import com.deepoove.poi.policy.ParagraphRenderPolicy;
import com.deepoove.poi.policy.PictureRenderPolicy;
import com.deepoove.poi.policy.RenderPolicy;
import com.deepoove.poi.policy.TextRenderPolicy;
import com.deepoove.poi.template.ElementTemplate;
import com.deepoove.poi.template.run.RunTemplate;
import com.deepoove.poi.xwpf.BodyContainer;
import com.deepoove.poi.xwpf.BodyContainerFactory;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.poi.xwpf.usermodel.UnderlinePatterns;
import org.apache.poi.xwpf.usermodel.XWPFHyperlinkRun;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;

import javax.imageio.ImageIO;

/**
 * 抽象渲染器
 *
 */
public abstract class AbstractRender {

    /*
    * 导出文件位置
    */
    protected final String cwd;

    /*
     * Docx结果文件路径
     */
    protected String docxResultPath;

    /*
     * html结果文件路径
     */
    protected String htmlResultPath;

    /*
     * pdf结果文件路径
     */
    protected String pdfResultPath;

    public AbstractRender(String cwd) {
        this.cwd = cwd;
    }

    /**
     * Docx 渲染
     *

     * @param templateFileContent 模板内容
     * @return 本地文件目录
     * @throws IOException
     */
    public final String renderDocx(Map<String, Object> dataMap, byte[] templateFileContent,
        ConfigureBuilder builder, String relationId) throws IOException {

        // 注： 报告模板的模板变量按照json序列化的结果命名
        String basicPath = this.getBasicPath();
        String reportImagePath = this.getReportImagePath();
        String label = relationId + "_" + IdUtil.fastSimpleUUID();
        //word导出位置
        String reportTempWordFile = basicPath + "/" + cwd + "/" + label + ".docx";
        // 新增：创建文件夹
        File parentDir = new File(basicPath + "/" + cwd);
        if (!parentDir.exists()) {
            boolean created = parentDir.mkdirs();
            if (!created) {
                throw new IOException("无法创建文件保存目录");
            }
        }

        Configure config = builder.build();
        XWPFTemplate template =
            XWPFTemplate.compile(new ByteArrayInputStream(templateFileContent), config).render(dataMap);
        template.writeToFile(reportTempWordFile);
        template.close();

        this.docxResultPath = reportTempWordFile;
        return this.docxResultPath;
    }

    /**
     * docx转换为pdf
     *
     * @return 本地文件目录
     * @throws IOException
     */
    public final String fromDocxToPdf() throws IOException {
        // 转换 docxResultPath 为 html
        // 加工html
        // 把 html 文件转成 pdf
        fromDocxToHtml();
        this.pdfResultPath = fromHtmlToPdf();
        return this.pdfResultPath;
    }

    /**
     * docx转换为Html
     *
     * @return 本地文件目录
     * @throws IOException
     */
    public final String fromDocxToHtml() throws IOException {
        // 转换 docxResultPath 为 html
        // 加工html
        this.htmlResultPath = cwd + "/1.html";
        return this.htmlResultPath;
    }

    /**
     * html转换为pdf
     *
     * @return 本地文件目录
     * @throws IOException
     */
    public final String fromHtmlToPdf() throws IOException {
        // 转换 htmlResultPath 为 pdf
        this.pdfResultPath = cwd + "/1.pdf";
        return this.pdfResultPath;
    }
    protected abstract String getBasicPath() throws IOException;
    protected abstract String getReportImagePath();
    protected RenderPolicy indicatorsRenderPolicy() {
        return new LoopRowTableRenderPolicy() {
            @Override
            public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
                // 处理数据中的null值
                Object processedData = processData(data);
                // 调用父类渲染处理后的数据
                super.render(eleTemplate, processedData, template);
            }

            private Object processData(Object data) {
                if (data == null) {
                    return null;
                }

                if (data instanceof List) {
                    // 处理List类型数据
                    return ((List<?>) data).stream()
                            .map(this::processItem)
                            .collect(Collectors.toList());
                }

                return data;
            }

            private Object processItem(Object item) {
                if (item == null) {
                    return "-";
                }

                // 如果元素是Map，处理Map中的null值
                if (item instanceof Map) {
                    Map<?, ?> map = (Map<?, ?>) item;
                    return map.entrySet().stream()
                            .collect(Collectors.toMap(
                                    Map.Entry::getKey,
                                    e -> e.getValue() == null ? "-" : e.getValue()
                            ));
                }

                return item;
            }
        };
    }
    /**
     * 这些render policy类都应当是共享的 重要设计假设: data的类型cast都可以建立在json通用反序列化后的基本类型基础上。
     */
    protected RenderPolicy pictureRenderPolicy() {
        return new PictureRenderPolicy() {
            @Override
            public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
                @SuppressWarnings("unchecked")
                Map<String, Object> mpData = (Map<String, Object>) data;
                String filename = mpData.getOrDefault("fileName", "").toString().trim();
                String url = mpData.getOrDefault("fileUri", "").toString();
                float targetWidth = 456.5f;

                if (StrUtil.isBlank(filename)) {
                    // uri render when no filename
                    TextRenderPolicy.Helper.renderTextRun(((RunTemplate) eleTemplate).getRun(),
                            new HyperlinkTextRenderData(url, url));
                } else if (filename.endsWith(".pdf")) {
                    // pdf render, replace data with bytestream
                    PDDocument document = null;
                    try {
                        document = Loader.loadPDF(new URL(url).openStream().readAllBytes());
                        PDFRenderer renderer = new PDFRenderer(document);

                        XWPFRun run = ((RunTemplate) eleTemplate).getRun();
                        BodyContainer bodyContainer = BodyContainerFactory.getBodyContainer(run);
                        // 每页一张图片
                        for (int i = 0; i < document.getNumberOfPages(); i++) {
                            // pdf 转 jpeg
                            BufferedImage image = renderer.renderImageWithDPI(i, 150);
                            ByteArrayOutputStream stream = new ByteArrayOutputStream();
                            ImageIO.write(image, "jpg", stream);

                            // 准备docx元素
                            if (i == 0) {
                                run.setText("", 0);
                            } else {
                                run = bodyContainer.insertNewParagraph(run).createRun();
                            }

                            // 准备POI-TL数据
                            ByteArrayPictureRenderData picData =
                                    new ByteArrayPictureRenderData(stream.toByteArray(), PictureType.JPEG);
                            Pair<Integer, Integer> targetSize =
                                    calculateTargetSize(image.getWidth(), image.getHeight(), targetWidth);
                            PictureStyle style = new PictureStyle();
                            style.setWidth(targetSize.getKey());
                            style.setHeight(targetSize.getValue());
                            picData.setPictureStyle(style);
                            PictureRenderPolicy.Helper.renderPicture(run, picData);
                        }
                    } catch (Exception e) {
                        throw new RenderException(
                                "AttachmentRenderPolicy for " + eleTemplate + " error: " + e.getMessage(), e);
                    } finally {
                        if (document != null) {
                            try {
                                document.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                } else {
                    BufferedImage image = null;
                    ByteArrayOutputStream stream = null;
                    try {
                        image = ImageIO.read(new URL(url));
                        stream = new ByteArrayOutputStream();
                        ImageIO.write(image, getFileExtension(filename), stream);
                    } catch (IOException e) {
                        throw new RenderException(
                                "AttachmentRenderPolicy for " + eleTemplate + " error: " + e.getMessage(), e);
                    }

                    ByteArrayPictureRenderData picData =
                            new ByteArrayPictureRenderData(stream.toByteArray(), PictureType.suggestFileType(filename));
                    Pair<Integer, Integer> targetSize = calculateTargetSize(image.getWidth(), image.getHeight(), targetWidth);
                    PictureStyle style = new PictureStyle();
                    style.setWidth(targetSize.getKey());
                    style.setHeight(targetSize.getValue());
                    picData.setPictureStyle(style);
                    super.render(eleTemplate, picData, template);
                }
            }

            /**
             * 计算目标尺寸（提取为内部方法）
             */
            private Pair<Integer, Integer> calculateTargetSize(int originWidth, int originHeight, float targetWidth) {
                float targetHeight = (originHeight * targetWidth) / originWidth;
                if (targetHeight > 645) {
                    targetHeight = 645;
                    targetWidth = (originWidth * targetHeight) / originHeight;
                }
                return Pair.of(Math.round(targetWidth), Math.round(targetHeight));
            }

            /**
             * 获取文件扩展名
             */
            private String getFileExtension(String filename) {
                int dotIndex = filename.lastIndexOf('.');
                return dotIndex > 0 ? filename.substring(dotIndex + 1).toLowerCase() : "jpg";
            }
        };
    }

    protected  RenderPolicy hyperlinkRenderPolicy() {
        return new ParagraphRenderPolicy() {
            @Override
            public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
                // 获取当前运行的
                XWPFParagraph paragraph = ((RunTemplate) eleTemplate).getRun().getParagraph();
                int pos = paragraph.getRuns().indexOf(((RunTemplate) eleTemplate).getRun());
                paragraph.removeRun(pos);
                paragraph.removeRun(pos);
                if (data instanceof String) {
                    XWPFHyperlinkRun hyperlinkRun = paragraph.createHyperlinkRun(data.toString());
                    hyperlinkRun.setText(data.toString());
                    hyperlinkRun.setColor("0000FF");
                    hyperlinkRun.setUnderline(UnderlinePatterns.SINGLE);
                    hyperlinkRun.setFontFamily("思源黑体 Medium");
                    hyperlinkRun.setFontSize(10.5);

                }
            }
        };
    }
}
