package com.yaoyicloud.template;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.deepoove.poi.data.NumberingFormat;
import com.deepoove.poi.data.NumberingRenderData;
import com.deepoove.poi.data.ParagraphRenderData;
import com.deepoove.poi.data.PictureType;
import com.deepoove.poi.data.Pictures;
import com.deepoove.poi.data.Texts;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import com.deepoove.poi.template.run.RunTemplate;
import com.yaoyicloud.constant.enums.ReportType;
import com.yaoyicloud.tools.OfficeUtil;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STVerticalJc;

import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.config.ConfigureBuilder;
import com.deepoove.poi.policy.RenderPolicy;
import com.deepoove.poi.template.ElementTemplate;
import com.deepoove.poi.util.TableTools;

/**
 * 模板方法模式, 定义一个导出流程的骨架
 *
 * @author lixuesong
 * @date 2024/05/30
 */
public abstract class AbstractReportExporter implements ReportDataProcessor {

    /**
     * 将原始raw数据设置类型为poitl1.12版本超链接数据类型
     * 
     * @param data raw数据集合
     * @param key 模版key
     */
    protected void processLink(Map<String, Object> data, String key) {
        String link = (String) data.get(key);
        if (StrUtil.isNotBlank(link)) {
            data.put(key, Texts.of(link).link(link).create());
        }
    }

    /**
     * 将原始raw数据设置类型为poitl1.12版本图片列表数据类型
     * 
     * @param data raw数据集合
     * @param key 模版key
     */
    protected void processImageListToNumberedRenderData(String key, Map<String, Object> data) {
        List<Map<String, Object>> rawImageDataList = (List<Map<String, Object>>) data.get(key);
        if (rawImageDataList == null || rawImageDataList.isEmpty()) {
            String textKey = key + "Text";
            if (data.containsKey(textKey)) {
                return;
            }
        }

        List<ParagraphRenderData> paragraphImages = new ArrayList<>();
        for (Map<String, Object> imageInfo : rawImageDataList) {
            ParagraphRenderData paragraph = new ParagraphRenderData();
            String base64 = (String) imageInfo.get("base64");
            String fileExtension = (String) imageInfo.get("fileExtension");
            Integer width = (Integer) imageInfo.get("width");
            Integer height = (Integer) imageInfo.get("height");

            PictureType pictureType = determinePictureType(fileExtension);

            paragraph.addPicture(Pictures.ofBase64(base64, pictureType)
                .size(width, height)
                .create());

            paragraphImages.add(paragraph);
        }

        NumberingFormat numberingFormat = new NumberingFormat(1, "");
        NumberingRenderData numberedImages = new NumberingRenderData(numberingFormat, paragraphImages);
        data.put(key, numberedImages);
    }

    /**
     * 图片格式
     * 
     * @param fileExtension raw中的后缀
     * @return
     */
    protected PictureType determinePictureType(String fileExtension) {
        if (fileExtension == null) {
            return PictureType.PNG;
        }

        String ext = fileExtension.toLowerCase();
        String normalizedExt = ext.startsWith(".") ? ext.substring(1) : ext;

        return switch (normalizedExt) {
            case "jpg", "jpeg" -> PictureType.JPEG;
            case "gif" -> PictureType.GIF;
            case "bmp" -> PictureType.BMP;
            case "tiff" -> PictureType.TIFF;
            default -> PictureType.PNG;
        };
    }

    /**
     * 报告导出
     * 
     * @param relationId
     * @param data 数据
     * @param templatePath 模板位置
     * @param reportBastPath pdf导出文件位置
     * @return
     * @throws IOException
     */
    public final String exportReport(Map<String, Object> data, String templatePath,
        String reportBastPath, Long relationId) throws Exception {

        String basicPath = this.getBasicPath();
        String reportImagePath = this.getReportImagePath();
        String label = String.valueOf(relationId) + "_" + IdUtil.fastSimpleUUID();
        // word导出位置
        String reportTempWordFile = basicPath + "/" + label + ".docx";
        // 定义策略
        ConfigureBuilder builder = Configure.builder();
        this.bindConfigure(builder);
        // 渲染数据
        Map<String, Object> processedData = data.entrySet().stream()
            .collect(Collectors.toMap(
                Map.Entry::getKey,
                entry -> entry.getValue() != null ? entry.getValue() : "-"));
        this.renderTemplate(reportTempWordFile, templatePath, builder, processedData);
        // word转pdf
        String html = OfficeUtil.convert(reportTempWordFile, reportImagePath);
        try (BufferedWriter writer2 = new BufferedWriter(new FileWriter("1.html"))) {
            writer2.write(html);
        } catch (IOException e) {
            System.err.println("写入 1.html 文件时发生错误: " + e.getMessage());
            e.printStackTrace();
        }
        String html1 = OfficeUtil.formatHtml(html);
        try (BufferedWriter writer2 = new BufferedWriter(new FileWriter("2.html"))) {
            writer2.write(html1);
        } catch (IOException e) {
            System.err.println("写入 2.html 文件时发生错误: " + e.getMessage());
            e.printStackTrace();
        }
        OfficeUtil.convertHtmlToPdf(html1, reportBastPath, getFontPaths(), reportImagePath, true);
        String html2 = OfficeUtil.formatHtml(html);

        try (BufferedWriter writer2 = new BufferedWriter(new FileWriter("3.html"))) {
            writer2.write(html2);
        } catch (IOException e) {
            System.err.println("写入 3.html 文件时发生错误: " + e.getMessage());
            e.printStackTrace();
        }
        return OfficeUtil.convertHtmlToPdf(html2, reportBastPath, getFontPaths(), reportImagePath, false);
    }

    protected abstract String getReportImagePath();

    protected abstract List<String> getFontPaths();

    /**
     * 获取模板地址
     *
     * @param reportType 类型
     * @return {@link String} 模板地址
     */
    protected abstract String getTemplate(ReportType reportType, String basicPath);

    /**
     * 配置绑定
     *
     * @param builder POI configureBuilder
     * @return {@link String} 模板地址
     */
    protected abstract void bindConfigure(ConfigureBuilder builder);

    protected abstract String imagePath();

    /**
     * 渲染数据
     *
     * @param templatePath 模板路径
     * @param builder 配置builder
     * @param data 数据
     * @throws IOException io异常
     */
    protected abstract void renderTemplate(String reportTempWordFile, String templatePath, ConfigureBuilder builder,
        Map<String, Object> data) throws IOException;

    protected abstract String getBasicPath() throws IOException;

    protected RenderPolicy customHackLoopTableRenderPolicy() {
        RenderPolicy policy = new LoopRowTableRenderPolicy() {
            @Override
            public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
                super.render(eleTemplate, data, template);
                // 2. 获取当前表格
                RunTemplate runTemplate = (RunTemplate) eleTemplate;
                XWPFTable table = (XWPFTable) runTemplate.getRun().getParent();

                // 3. 遍历表格行，检查字段是否为空，并替换为 "-"
                List<XWPFTableRow> rows = table.getRows();
                for (XWPFTableRow row : rows) {
                    List<XWPFTableCell> cells = row.getTableCells();
                    for (XWPFTableCell cell : cells) {
                        String text = cell.getText();
                        if (text == null || text.trim().isEmpty()) {
                            cell.setText("-"); // 替换空值为 "-"
                        }
                    }
                }
            }
        };
        return policy;
    }

    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;
            }
        };
    }

    protected RenderPolicy getScoreRenderPolicy() {
        RenderPolicy policy = new LoopRowTableRenderPolicy() {
            @SuppressWarnings("checkstyle:NestedForDepth")
            @Override
            public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
                // 检查数据是否为空
                if (data == null || (data instanceof Collection && ((Collection<?>) data).isEmpty())) {
                    // 数据为空时，删除整个表格
                    // removeTemplateTable(eleTemplate, template);
                    return;
                }

                super.render(eleTemplate, data, template);
                XWPFDocument doc = template.getXWPFDocument();
                for (XWPFTable table : doc.getTables()) {
                    boolean isTargetTable = false;
                    // 判断是否为目标表格：通过检查表格第一行是否包含货物明细相关表头关键词
                    List<String> headerKeywords = List.of("删除");
                    XWPFTableRow firstRow = table.getRow(0);
                    if (firstRow != null) {
                        for (XWPFTableCell cell : firstRow.getTableCells()) {
                            for (XWPFParagraph para : cell.getParagraphs()) {
                                for (XWPFRun run : para.getRuns()) {
                                    String text = run.text().trim();
                                    if (headerKeywords.contains(text)) {
                                        isTargetTable = true;
                                        break;
                                    }
                                }
                                if (isTargetTable) {
                                    break;
                                }
                            }
                            if (isTargetTable) {
                                break;
                            }
                        }
                    }
                    if (isTargetTable) {
                        // 删除表头（假设表头是第一行）
                        if (table.getRows().size() > 0) {
                            table.removeRow(0);
                        }
                        // 合并第一列相同内容单元格并居中
                        mergeAndCenterFirstColumn(table);
                    }
                }
            }

            private void mergeAndCenterFirstColumn(XWPFTable table) {
                if (table.getNumberOfRows() < 2) {
                    return;
                }

                int startRow = 0;
                String currentValue = getCellText(table.getRow(0)).get(0);

                for (int i = 1; i < table.getNumberOfRows(); i++) {
                    List<String> rowValues = getCellText(table.getRow(i));
                    if (rowValues.isEmpty()) {
                        continue;
                    }

                    String firstCellValue = rowValues.get(0);
                    if (firstCellValue.equals(currentValue)) {
                        // 继续查找相同值的行
                        continue;
                    } else {
                        // 合并从startRow到i-1行的第一列单元格
                        if (i - 1 > startRow) {
                            TableTools.mergeCellsVertically(table, 0, startRow, i - 1);
                            // 设置合并后单元格内容居中
                            setCellCenterAlignment(table.getRow(startRow).getCell(0));
                        }
                        startRow = i;
                        currentValue = firstCellValue;
                    }
                }

                // 处理最后一组相同值
                if (table.getNumberOfRows() - 1 > startRow) {
                    TableTools.mergeCellsVertically(table, 0, startRow, table.getNumberOfRows() - 1);
                    setCellCenterAlignment(table.getRow(startRow).getCell(0));
                }
            }
        };
        return policy;
    }

    private List<String> getCellText(XWPFTableRow row) {
        List<String> texts = new ArrayList<>();
        if (row == null) {
            return texts;
        }
        for (XWPFTableCell cell : row.getTableCells()) {
            StringBuilder sb = new StringBuilder();
            for (XWPFParagraph para : cell.getParagraphs()) {
                for (XWPFRun run : para.getRuns()) {
                    sb.append(run.text());
                }
            }
            texts.add(sb.toString().trim());
        }
        return texts;
    }

    private void setCellCenterAlignment(XWPFTableCell cell) {
        CTTc cttc = cell.getCTTc();
        CTTcPr ctPr = cttc.addNewTcPr();
        ctPr.addNewVAlign().setVal(STVerticalJc.CENTER);
    }
}
