package com.qingyun.service.compare.strategy;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.github.difflib.patch.DeltaType;
import com.itextpdf.text.pdf.BaseFont;
import com.qingyun.common.config.QingYunConfig;
import com.qingyun.common.exception.ServiceException;
import com.qingyun.common.utils.DateUtils;
import com.qingyun.common.utils.StringUtils;
import com.qingyun.service.compare.*;
import com.qingyun.service.domain.ContractCompareResult;
import com.qingyun.service.domain.ContractCompareTask;
import com.qingyun.service.mapper.ContractCompareResultMapper;
import com.qingyun.service.mapper.ContractCompareTaskMapper;
import com.qingyun.service.utils.PdfUtil;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.profile.pegdown.Extensions;
import com.vladsch.flexmark.util.ast.Node;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.junit.jupiter.api.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;


@Component
@Order(2)
public class ImageComparisonStrategy extends AbstractFileComparisonStrategy implements Ordered {

    @Autowired
    private ContractCompareResultMapper compareResultMapper;

    @Autowired
    private ContractCompareTaskMapper contractCompareTaskMapper;

    @Autowired
    private FetchAllPagesContent fetchAllPagesContent;

    @Autowired
    private PdfDiffWithPosition pdfDiffWithPosition;

    @Autowired
    private PdfUtil pdfUtil;

    /**
     * 提取文件内容（如文本、结构等）
     * @param task 任务
     */
    @Override
    protected void doExtract(ContractCompareTask task) {
        // 调用orc 获取md文本，保存为md文件
        try  {
            File imgFile01 = pdfUtil.downloadFile(task.getFirstContractUrl(), UUID.randomUUID().toString() + ".png");
            String firstContent = fetchAllPagesContent.extractImgPagesContent(imgFile01);

            File imgFile02 = pdfUtil.downloadFile(task.getSecondContractUrl(), UUID.randomUUID().toString() + ".png");
            String secondContent = fetchAllPagesContent.extractImgPagesContent(imgFile02);

            // 将内容生成pdf文件
            if (StrUtil.isNotBlank(firstContent)) {
                List<String> firstContractLines = Arrays.asList(firstContent.split("\n"));
                // 去除空行
                firstContractLines = firstContractLines.stream().filter(StrUtil::isNotBlank).collect(Collectors.toList());
                firstContent = String.join("\n", firstContractLines);
                // 去除掉md文档的语法文字
                firstContent = removeMarkdownSyntax(firstContent);
                String newFilePath = writePdf(firstContent, task.getFirstContractName());
                task.setFirstContractUrl(newFilePath);
                task.setFirstContractContent(firstContent);
                // 改动文件名称，添加.md后缀
                String firstContractName = task.getFirstContractName();
                if (firstContractName.contains(".png") || firstContractName.contains(".jpg") || firstContractName.contains(".jpeg")) {
                    String newName = firstContractName.substring(0, firstContractName.lastIndexOf(".")) + ".pdf";
                    task.setFirstContractName(newName);
                }
            }
            if (StrUtil.isNotBlank(secondContent)) {
                List<String> secondContractLines = Arrays.asList(secondContent.split("\n"));
                // 去除空行
                secondContractLines = secondContractLines.stream().filter(StrUtil::isNotBlank).collect(Collectors.toList());
                secondContent = String.join("\n", secondContractLines);
                // 去除掉md文档的语法文字
                secondContent = removeMarkdownSyntax(secondContent);
                String newFilePath = writePdf(secondContent, task.getSecondContractName());
                task.setSecondContractUrl(newFilePath);
                task.setSecondContractContent(secondContent);
                String secondContractName = task.getSecondContractName();
                if (secondContractName.contains(".png") || secondContractName.contains(".jpg") || secondContractName.contains(".jpeg")) {
                    String newName = secondContractName.substring(0, secondContractName.lastIndexOf(".")) + ".pdf";
                    task.setSecondContractName(newName);
                }
            }

            File firstFile = null;
            File secondFile = null;

            firstFile = pdfUtil.downloadFile(task.getFirstContractUrl(), task.getFirstContractName());
            PDDocument firstDocument = Loader.loadPDF(firstFile);
            if (firstDocument == null) {
                throw new ServiceException("解析PDF文件失败");
            }
//            // 判断是否是扫描件pdf
//            if (PdfScanChecker.isScannedPdf(firstDocument)) {
//                // 扫描件pdf -> orc识别 -> 重组成新的文字pdf
//
//            }
            task.setFirstPageNum(firstDocument.getNumberOfPages());
            if (firstDocument.getNumberOfPages() > 0) {
                task.setFirstContractContent(firstDocument.getPage(0).getContents().toString());
                task.setFirstWidth(firstDocument.getPage(0).getMediaBox().getWidth());
                task.setFirstHeight(firstDocument.getPage(0).getMediaBox().getHeight());
            }
            secondFile = pdfUtil.downloadFile(task.getSecondContractUrl(), task.getSecondContractName());
            PDDocument secondDocument = Loader.loadPDF(secondFile);
            if (secondDocument == null) {
                throw new ServiceException("解析PDF文件失败");
            }
            task.setSecondPageNum(secondDocument.getNumberOfPages());
            if (secondDocument.getNumberOfPages() > 0) {
                task.setSecondWidth(secondDocument.getPage(0).getMediaBox().getWidth());
                task.setSecondHeight(secondDocument.getPage(0).getMediaBox().getHeight());
            }
            firstDocument.close();
            secondDocument.close();

        } catch (Exception e) {
            log.error("文本提取失败", e);
            updateTaskStatus(task, 4, "文本提取失败");
            throw new ServiceException("文本提取失败");
        }
        updateTaskStatus(task, 2, "文本提取完成");
    }

    @Override
    protected List<DiffRegion> doCompare(ContractCompareTask task) {
        // TODO: 对比两张图片提取出的文本
//        String firstContractContent = task.getFirstContractContent();
//        String secondContractContent = task.getSecondContractContent();
//        // md文本按 \n 分割成行
//        List<String> firstContractLines = Arrays.asList(firstContractContent.split("\n"));
//        List<String> secondContractLines = Arrays.asList(secondContractContent.split("\n"));
//
//        //组装结果
//        List<DiffRegion> regions = new ArrayList<>();
//        int maxLines = Math.max(firstContractLines.size(), secondContractLines.size());
//
//        for (int i = 0; i < maxLines; i++) {
//            String firstLine = i < firstContractLines.size() ? firstContractLines.get(i) : "";
//            String secondLine = i < secondContractLines.size() ? secondContractLines.get(i) : "";
//
//            // 行号从1开始计算
//            int srcLineNo = i + 1;
//            int dstLineNo = i + 1;
//
//            // 如果两行内容相同，则跳过
//            if (firstLine.equals(secondLine)) {
//                continue;
//            }
//            // 生成字符级差异
//            List<DiffRow> charDiffs = DiffRowGenerator.create()
//                .showInlineDiffs(false)
//                .inlineDiffByWord(false) // 字符级而不是单词级
//                .ignoreWhiteSpaces(true)
//                .build()
//                .generateDiffRows(
//                    Arrays.asList(firstLine.split("(?!^)")),
//                    Arrays.asList(secondLine.split("(?!^)"))
//                );
//            // 每行的字符级差异
//            for (DiffRow charDiff : charDiffs) {
//                switch (charDiff.getTag()) {
//                    case EQUAL:
//                        break;
//                    case DELETE:
//                        regions.add(buildRegion("delete", charDiff.getOldLine(), charDiff.getNewLine(), srcLineNo, dstLineNo));
//                        break;
//                    case INSERT:
//                        regions.add(buildRegion("insert", charDiff.getOldLine(), charDiff.getNewLine(), srcLineNo, dstLineNo));
//                        break;
//                    case CHANGE:
//                        regions.add(buildRegion("change", charDiff.getOldLine(), charDiff.getNewLine(), srcLineNo, dstLineNo));
//                        break;
//                }
//            }
//        }
        return pdfDiffWithPosition.compare(task);
    }

    @Override
    protected ContractCompareResult buildResult(ContractCompareTask task, DiffRegion change) {
//        // TODO: 构建图片比对结果（注意：图片无精确坐标，可能需模拟或标记区域）
//        ContractCompareResult result = new ContractCompareResult();
//        result.setTaskId(task.getTaskId());
//        // 内容
//        result.setBaseDiffContent(change.getSourceText());
//        result.setCompareDiffContent(change.getTargetText());
//        // 页码
//        result.setBasePageNum(change.getSourcePage());
//        result.setComparePageNum(change.getTargetPage());
//        // 行号
//        result.setBaseLineNum(change.getSourceLine());
//        result.setCompareLineNum(change.getTargetLine());
//        // 差异类型
//        // 这里主要以目标文本为视角
//        // 如果是 change 那么源文本是change，目标文本是change
//        // 如果是 insert 那么源文本是delete，目标文本是insert
//        // 如果是 delete 那么源文本是insert，目标文本是delete
//        if ("change".equals(change.getChangeType())) {
//            result.setBaseDiffType("change");
//            result.setCompareDiffType("change");
//        } else if ("insert".equals(change.getChangeType())) {
//            result.setBaseDiffType("delete");
//            result.setCompareDiffType("insert");
//        } else if ("delete".equals(change.getChangeType())) {
//            result.setBaseDiffType("insert");
//            result.setCompareDiffType("delete");
//        }
//        return result;

        ContractCompareResult contractCompareResult = new ContractCompareResult();
        contractCompareResult.setTaskId(task.getTaskId());

        contractCompareResult.setBaseDiffContent(StringUtils.isNotBlank(change.getSourceText()) ? change.getSourceText() : "");
        contractCompareResult.setCompareDiffContent(StringUtils.isNotBlank(change.getTargetText()) ? change.getTargetText() : "");

        // 源文本的变动类型，要根据目标的变动类型来处理，change - change , delete - insert, insert - delete
        if (Objects.equals(change.getChangeType(), DeltaType.CHANGE.name())) {
            contractCompareResult.setBaseDiffType("change");
            contractCompareResult.setCompareDiffType("change");
        } else if (Objects.equals(change.getChangeType(), DeltaType.DELETE.name())) {
            contractCompareResult.setBaseDiffType("insert");
            contractCompareResult.setCompareDiffType("delete");
        } else if (Objects.equals(change.getChangeType(), DeltaType.INSERT.name())) {
            contractCompareResult.setBaseDiffType("delete");
            contractCompareResult.setCompareDiffType("insert");
        }

        if (change.getSourceBox() != null) {
            BoundingBox sourceBox = change.getSourceBox();
            contractCompareResult.setBaseBoxArea(
                sourceBox.getX() + "," + sourceBox.getY() + "," + sourceBox.getWidth() + "," + sourceBox.getHeight());

            contractCompareResult.setBasePageNum(change.getSourcePage());
        } else {
            // 没有源文本，则取目标文本的定位
            BoundingBox targetBox = change.getTargetBox();
            contractCompareResult.setBaseBoxArea(
                targetBox.getX() + "," + targetBox.getY() + "," + targetBox.getWidth() + "," + targetBox.getHeight());
            contractCompareResult.setBasePageNum(change.getTargetPage());
        }
        if (change.getTargetBox() != null) {
            BoundingBox targetBox = change.getTargetBox();
            contractCompareResult.setCompareBoxArea(
                targetBox.getX() + "," + targetBox.getY() + "," + targetBox.getWidth() + "," + targetBox.getHeight());
            contractCompareResult.setComparePageNum(change.getTargetPage());
        } else {
            // 没有目标文本，则取源文本的定位
            BoundingBox sourceBox = change.getSourceBox();
            contractCompareResult.setCompareBoxArea(
                sourceBox.getX() + "," + sourceBox.getY() + "," + sourceBox.getWidth() + "," + sourceBox.getHeight());
            contractCompareResult.setComparePageNum(change.getSourcePage());
        }
        contractCompareResult.setCreateTime(new Date());
        return contractCompareResult;
    }

    @Override
    protected void saveResults(List<ContractCompareResult> results) {
        compareResultMapper.insertBatch(results);
    }

    @Override
    protected void updateTaskStatus(ContractCompareTask task, int status, String msg) {
        task.setStatus(status);
        task.setErrorMsg(msg);
        contractCompareTaskMapper.updateById(task);
    }

    @Override
    public boolean supports(String fileExtension) {
        return Arrays.asList("png", "jpg", "jpeg").contains(fileExtension);
    }

    @Override
    public int getOrder() {
        return 2;
    }

    /**
     * 将 Markdown 字符串写入指定文件
     *
     * @param content Markdown 内容
     * @param fileName 文件名
     * @return 文件相对路径
     */
    public String writeMarkdown(String content, String fileName) {

        String needUploadName = DateUtils.datePath() + "/" + IdUtil.fastUUID() + "/" +
            fileName.substring(0, fileName.lastIndexOf(".")) + ".md";
        String filePath = QingYunConfig.getUploadPath() + "/" + needUploadName;
        Path targetPath = Paths.get(filePath);
        // 确保父目录存在
        try {
            if (targetPath.getParent() != null) {
                Files.createDirectories(targetPath.getParent());
            }
        } catch (IOException e) {
            throw new RuntimeException("无法创建目录：" + targetPath, e);
        }

        // 使用 try-with-resources 自动关闭流，指定 UTF-8 编码避免中文乱码
        try (BufferedWriter writer = Files.newBufferedWriter(targetPath, StandardCharsets.UTF_8)) {
            writer.write(content);
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("写入 Markdown 文件失败：" + targetPath, e);
        }
        return "/profile/upload/" + needUploadName;
    }

    /**
     * 将md文本内容写入 PDF 文件
     *
     * @param content  文本内容
     * @param fileName 文件名
     * @return 文件相对路径
     */
    public String writePdf(String content, String fileName) {
        String needUploadName = DateUtils.datePath() + "/" + IdUtil.fastUUID() + "/" +
            fileName.substring(0, fileName.lastIndexOf(".")) + ".pdf";
        String filePath = QingYunConfig.getUploadPath() + "/" + needUploadName;
        Path targetPath = Paths.get(filePath);

        // 确保父目录存在
        try {
            if (targetPath.getParent() != null) {
                Files.createDirectories(targetPath.getParent());
            }
        } catch (IOException e) {
            throw new RuntimeException("无法创建目录：" + targetPath, e);
        }

        // 使用 PDFBox 将文本内容写入 PDF 文件
        try {
            PDDocument document = new PDDocument();
            PDPage page = new PDPage();
            document.addPage(page);

            PDPageContentStream contentStream = new PDPageContentStream(document, page);

            InputStream fontStream = Thread.currentThread().getContextClassLoader()
                .getResourceAsStream("fonts/SimSun.ttf");
            // 加载默认字体以支持中文
            PDFont font = PDType0Font.load(document, fontStream);

            contentStream.beginText();
            contentStream.setFont(font, 10);
            contentStream.newLineAtOffset(20, 750);

            // 将文本按行分割并写入 PDF
            String[] lines = content.split("\n");
            for (String line : lines) {
                contentStream.showText(line);
                contentStream.newLineAtOffset(0, -12); // 每行间隔15个单位
            }

            contentStream.endText();
            contentStream.close();

            // 保存文档
            document.save(targetPath.toFile());
            document.close();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("写入 PDF 文件失败：" + targetPath, e);
        }
        return "/profile/upload/" + needUploadName;
    }

    /**
     * 将 Markdown 转换为带样式的 HTML
     */
    private String mdToHtml(String markdown) {
        com.vladsch.flexmark.util.data.MutableDataSet options = new com.vladsch.flexmark.util.data.MutableDataSet();
        Parser parser = Parser.builder(options).build();

        HtmlRenderer renderer = HtmlRenderer.builder(options).build();

        Node document = parser.parse(markdown);
        return renderer.render(document); // 输出 HTML 字符串
    }

    /**
     * 将 HTML 渲染为 PDF 并保存到指定文件
     */
    private void htmlToPdf(String html, File outputFile) throws Exception {
        try (OutputStream os = Files.newOutputStream(outputFile.toPath())) {
            ITextRenderer renderer = new ITextRenderer();

            // 1. 注册中文字体（关键！解决中文乱码）
            // 假设 SimSun.ttf 放在 resources/fonts 目录下（需提前放入）
            InputStream fontStream = getClass().getClassLoader()
                .getResourceAsStream("fonts/SimSun.ttf");
            if (fontStream == null) {
                throw new RuntimeException("未找到中文字体文件：fonts/SimSun.ttf");
            }

            ITextFontResolver fontResolver = renderer.getFontResolver();
            // 注册宋体（支持中文，BaseFont.IDENTITY_H 表示 Unicode 编码）
            fontResolver.addFont("fonts/SimSun.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);

            // 2. 加载 CSS 样式（控制 PDF 外观）
            String css = "@page { size: A4; margin: 2cm; } " +
                         "body { font-family: 'SimSun', serif; line-height: 1.8; font-size: 12pt; } " +
                         "h1 { color: #2c3e50; font-size: 18pt; border-bottom: 2px solid #34495e; padding-bottom: 0.3em; } " +
                         "h2 { color: #34495e; font-size: 16pt; margin: 1em 0 0.8em; } " +
                         "p { margin: 0.8em 0; text-align: justify; } " +
                         "pre { background: #f8f8f8; padding: 1em; border-radius: 4px; overflow-x: auto; } " +
                         "code { font-family: 'Consolas', monospace; font-size: 10pt; } " +
                         "ul, ol { margin: 0.8em 0; padding-left: 2em; } " +
                         "li { margin: 0.3em 0; } " +
                         "a { color: #2980b9; text-decoration: none; } " +
                         "a:hover { text-decoration: underline; }";

            // 3. 渲染 HTML 到 PDF
            renderer.setDocumentFromString(html); // 关联 HTML 和 CSS
            renderer.layout(); // 计算布局
            renderer.createPDF(os); // 输出 PDF 到流
        }
    }

    /**
     * 构造 DiffRegion
     */
    private DiffRegion buildRegion(String type,
                                   String srcText, String dstText,
                                   Integer srcLine, Integer dstLine) {
        DiffRegion r = new DiffRegion();
        r.setChangeType(type);
        r.setSourceText(srcText);
        r.setTargetText(dstText);
        r.setSourceLine(srcLine);
        r.setTargetLine(dstLine);
        r.setSourcePage(1);
        r.setTargetPage(1);
        r.setSourceBox(null);
        r.setTargetBox(null);
        return r;
    }

    public String removeMarkdownSyntax(String markdown) {
        // 1. 保留换行符（\n）作为分隔符
        String[] lines = markdown.split("\n", -1);
        StringBuilder result = new StringBuilder();

        for (String line : lines) {
            // 2. 移除各种Markdown语法
            String cleanLine = line
                // 移除标题标记 (## 标题 → 标题)
                .replaceAll("^#{1,6}\\s*", "")
                // 移除列表标记 (* item, - item, 1. item)
                .replaceAll("^\\s*([-*+]|[0-9]+\\.)\\s+", "")
                // 移除引用标记 (> 引用)
                .replaceAll("^>+\\s*", "")
                // 移除代码块标记 (``` 和 `)
                .replaceAll("`{3}.*", "")  // 代码块开始标记
                .replaceAll("`", "")       // 行内代码标记
                // 移除粗体和斜体标记 (**bold** → bold)
                .replaceAll("\\*{1,2}(.*?)\\*{1,2}", "$1")
                .replaceAll("_{1,2}(.*?)_{1,2}", "$1")
                // 移除链接和图片标记 ([text](url) → text
                .replaceAll("!?\\[([^\\]]*)\\]\\([^\\)]+\\)", "$1")
                // 移除表格标记 (| header | → header)
                .replaceAll("\\|\\s*:?[-]+\\s*", "") // 表头分隔行
                .replaceAll("\\|", " ")              // 表格分隔符
                // 移除HTML标签（可选）
                .replaceAll("<[^>]+>", "")
                // 移除水平线 (---)
                .replaceAll("^[-*]{3,}\\s*$", "")
                // 移除任务列表标记 ([x])
                .replaceAll("\\[x\\]|\\[\\s\\]", "")
                // 移除尾随空格
                .trim();

            result.append(cleanLine).append("\n");
        }

        return result.toString();
    }
}
