package com.qingyun.service.compare;

import com.github.difflib.DiffUtils;
import com.github.difflib.patch.AbstractDelta;
import com.github.difflib.patch.DeltaType;
import com.github.difflib.patch.Patch;
import com.qingyun.common.exception.ServiceException;
import com.qingyun.service.domain.ContractCompareTask;
import com.qingyun.service.utils.PdfUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@Service
@AllArgsConstructor
public class PdfDiffWithPosition {

    private final PdfUtil pdfUtil;

    public List<DiffRegion> compare(ContractCompareTask contractCompareTask)  {
        File tempFile1 = null;
        File tempFile2 = null;
        try {
            tempFile1 = pdfUtil.downloadFile(contractCompareTask.getFirstContractUrl(), contractCompareTask.getFirstContractName());
            tempFile2 = pdfUtil.downloadFile(contractCompareTask.getSecondContractUrl(), contractCompareTask.getSecondContractName());
            //PDDocument doc1 = Loader.loadPDF(tempFile1);
            //PDDocument doc2 = Loader.loadPDF(tempFile2);

            // 提取摘要
//            List<String> summary1 = PageFingerprinter.extractPageSummaries(doc1);
//            List<String> summary2 = PageFingerprinter.extractPageSummaries(doc2);
//
//            // 判断是否可比
//            if (!DocumentStructureComparator.isComparable(summary1, summary2)) {
//                throw new ServiceException("文档结构差异过大，不适合直接比对！");
//            }

            // 1. 加载文档并提取带位置信息的文本
            List<TextElement> textElements1 = extractTextWithPosition(tempFile1);
            List<TextElement> textElements2 = extractTextWithPosition(tempFile2);

            // 新：词级
            List<TokenWithPosition> tokens1 = extractTokens(textElements1);
            List<TokenWithPosition> tokens2 = extractTokens(textElements2);

            List<String> words1 = new ArrayList<>();
            List<String> words2 = new ArrayList<>();

            for (TokenWithPosition t : tokens1) words1.add(t.token);
            for (TokenWithPosition t : tokens2) words2.add(t.token);

            // 3. 执行差异检测
            Patch<String> patch = DiffUtils.diff(words1, words2);

            // 4. 处理差异并记录位置信息
            List<ChangeLocation> mergedChanges = new ArrayList<>();
            ChangeLocation currentMerge = null;

            for (AbstractDelta<String> delta : patch.getDeltas()) {

                if (delta.getType() == DeltaType.CHANGE) {
                    List<TextElement> sourceElements = mapTokensToElements(
                        tokens1, delta.getSource().getPosition(),
                        delta.getSource().getLines().size(), textElements1);
                    List<TextElement> targetElements = mapTokensToElements(
                        tokens2, delta.getTarget().getPosition(),
                        delta.getTarget().getLines().size(), textElements2);

                    if (currentMerge == null) {
                        // 开始新的合并
                        currentMerge = new ChangeLocation(
                            delta.getSource().getLines(),
                            delta.getTarget().getLines(),
                            DeltaType.CHANGE,
                            sourceElements,
                            targetElements
                        );
                    } else if (canMerge(currentMerge, sourceElements, targetElements)) {
                        // 合并到当前记录
                        currentMerge.sourceText.addAll(delta.getSource().getLines());
                        currentMerge.targetText.addAll(delta.getTarget().getLines());
                        currentMerge.sourceElements.addAll(sourceElements);
                        currentMerge.targetElements.addAll(targetElements);
                    } else {
                        // 保存当前合并并开始新记录
                        mergedChanges.add(currentMerge);
                        currentMerge = new ChangeLocation(
                            delta.getSource().getLines(),
                            delta.getTarget().getLines(),
                            DeltaType.CHANGE,
                            sourceElements,
                            targetElements
                        );
                    }
                } else {
                    // 非 CHANGE 类型直接添加
                    if (currentMerge != null) {
                        mergedChanges.add(currentMerge);
                        currentMerge = null;
                    }
                    mergedChanges.add(new ChangeLocation(
                        delta.getSource().getLines(),
                        delta.getTarget().getLines(),
                        delta.getType(),
                        mapTokensToElements(tokens1, delta.getSource().getPosition(),
                            delta.getSource().getLines().size(), textElements1),
                        mapTokensToElements(tokens2, delta.getTarget().getPosition(),
                            delta.getTarget().getLines().size(), textElements2)
                    ));
                }
            }

            // 添加剩余的合并记录
            if (currentMerge != null) {
                mergedChanges.add(currentMerge);
            }

            // 5. 输出差异位置信息（供前端使用）
            List<DiffRegion> diffRegions = new ArrayList<>();
            for (ChangeLocation change : mergedChanges) {
                if (isMeaninglessChange(change.sourceText) && isMeaninglessChange(change.targetText)) {
                    continue; // 忽略无意义的变化
                }

                DiffRegion region = new DiffRegion();
                region.changeType = change.changeType.name();

                // 拼接文本内容
                if (!change.sourceText.isEmpty()) {
                    region.sourceText = String.join("", change.sourceText);
                }
                if (!change.targetText.isEmpty()) {
                    region.targetText = String.join("", change.targetText);
                }

                // 源文档的包围框
                if (!change.sourceElements.isEmpty()) {
                    TextElement firstSource = change.sourceElements.get(0);
                    TextElement lastSource = change.sourceElements.get(change.sourceElements.size() - 1);
                    region.sourceBox = new BoundingBox();
                    region.sourceBox.x = firstSource.x;
                    region.sourceBox.y = firstSource.y;
                    region.sourceBox.width = lastSource.x + lastSource.width - firstSource.x;
                    region.sourceBox.height = firstSource.height;
                    region.sourcePage = firstSource.pageNumber;
                }

                // 目标文档的包围框
                if (!change.targetElements.isEmpty()) {
                    TextElement firstTarget = change.targetElements.get(0);
                    TextElement lastTarget = change.targetElements.get(change.targetElements.size() - 1);
                    region.targetBox = new BoundingBox();
                    region.targetBox.x = firstTarget.x;
                    region.targetBox.y = firstTarget.y;
                    region.targetBox.width = lastTarget.x + lastTarget.width - firstTarget.x;
                    region.targetBox.height = firstTarget.height;
                    region.targetPage = firstTarget.pageNumber;
                }

                diffRegions.add(region);
            }

            return diffRegions;
        } catch (IOException e) {
            log.error("PDF 文件读取失败", e);
            throw new ServiceException("PDF文件读取失败");
        } catch (ServiceException e) {
            log.error(e.getMessage());
            throw e; // Rethrow the exception
        } finally {
            if (tempFile1 != null && tempFile1.exists() ) {
                try {
                    Files.deleteIfExists(tempFile1.toPath());
                } catch (IOException ignored) {
                }
            }
            if (tempFile2 != null && tempFile2.exists()) {
                try {
                    Files.deleteIfExists(tempFile2.toPath());
                } catch (IOException ignored) {
                }
            }
        }
    }


    // 提取带位置信息的文本
    private List<TextElement> extractTextWithPosition(File pdfFile) throws IOException {
        try (PDDocument document = Loader.loadPDF(pdfFile)) {
            PositionTextStripper stripper = new PositionTextStripper();
            stripper.setSortByPosition(true);
            stripper.setStartPage(1);
            stripper.setEndPage(document.getNumberOfPages());
            stripper.getText(document);
            return stripper.getTextElements();
        }
    }

    /**
     * 合并判断方法
     *
     * @param prev
     * @param sourceElements
     * @param targetElements
     * @return
     */
    private boolean canMerge(ChangeLocation prev, List<TextElement> sourceElements, List<TextElement> targetElements) {
        if (prev.sourceElements.isEmpty() || prev.targetElements.isEmpty() || sourceElements.isEmpty() || targetElements.isEmpty())
            return false;

        TextElement prevSourceLast = prev.sourceElements.get(prev.sourceElements.size() - 1);
        TextElement currSourceFirst = sourceElements.get(0);

        TextElement prevTargetLast = prev.targetElements.get(prev.targetElements.size() - 1);
        TextElement currTargetFirst = targetElements.get(0);

        boolean sourceContinuous = isContinuous(prevSourceLast, currSourceFirst);
        boolean targetContinuous = isContinuous(prevTargetLast, currTargetFirst);

        return sourceContinuous && targetContinuous;
    }

    private boolean isContinuous(TextElement prev, TextElement curr) {
        return prev.pageNumber == curr.pageNumber &&
            Math.abs(prev.y - curr.y) < 2 &&
            (curr.x - (prev.x + prev.width)) < 5;
    }

    private boolean isMeaninglessChange(List<String> textList) {
        return textList.stream().allMatch(s -> s.trim().isEmpty());
    }

    private List<TextElement> mapTokensToElements(
        List<TokenWithPosition> tokens, int start, int size,
        List<TextElement> elements) {

        if (start < 0 || size <= 0 || start >= tokens.size()) return new ArrayList<>();

        int end = Math.min(start + size, tokens.size());
        List<TextElement> result = new ArrayList<>();

        for (int i = start; i < end; i++) {
            TokenWithPosition t = tokens.get(i);
            // 找到包含该 token 的 TextElement（简单匹配页码 + 坐标重叠）
            for (TextElement e : elements) {
                if (e.pageNumber == t.page &&
                    e.x <= t.x && (e.x + e.width) >= (t.x + t.width) &&
                    Math.abs(e.y - t.y) < 1) {
                    result.add(e);
                    break;
                }
            }
        }
        return result;
    }


    private List<TokenWithPosition> extractTokens(List<TextElement> elements) {
        List<TokenWithPosition> tokens = new ArrayList<>();
        int globalIdx = 0;

        for (TextElement e : elements) {
            String text = e.text;
            float charWidth = e.width / Math.max(text.length(), 1);
            float offsetX = 0;

            Pattern pattern = Pattern.compile("\\S+|\\s+");
            Matcher matcher = pattern.matcher(text);
            while (matcher.find()) {
                String token = matcher.group();
                float tokenWidth = token.length() * charWidth;
                tokens.add(new TokenWithPosition(
                    token,
                    e.pageNumber,
                    e.x + offsetX,
                    e.y,
                    tokenWidth,
                    e.height,
                    globalIdx++
                ));
                offsetX += tokenWidth;
            }
        }
        return tokens;
    }
}
