package com.yanzhu.module.ai.service.knowledge.splitter;

import cn.hutool.core.util.StrUtil;
import dev.langchain4j.data.document.Document;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 语义化文本切片器
 *
 * <p>功能特点：
 * <ul>
 *   <li>优先在段落边界（双换行）处切分</li>
 *   <li>其次在句子边界（句号、问号、感叹号）处切分</li>
 *   <li>避免在句子中间截断，保持语义完整性</li>
 *   <li>支持中英文标点符号识别</li>
 * </ul>
 *
 * @author runzhen
 */
@Slf4j
public class SemanticTextSplitter implements com.yanzhu.module.ai.framework.ai.compat.TextSplitter {

    /**
     * 分段的最大 Token 数
     */
    private final int chunkSize;

    /**
     * 段落重叠大小（用于保持上下文连贯性）
     */
    private final int chunkOverlap;

    /**
     * 段落分隔符（按优先级排序）
     */
    private static final List<String> PARAGRAPH_SEPARATORS = Arrays.asList(
            "\n\n\n",    // 三个换行
            "\n\n",      // 双换行
            "\n"         // 单换行
    );

    /**
     * 句子结束标记（中英文标点）
     */
    private static final Pattern SENTENCE_END_PATTERN = Pattern.compile(
            "[。！？.!?]+[\\s\"'）)】\\]]*"
    );

    /**
     * Token 估算器
     */
    private final MarkdownQaSplitter.TokenEstimator tokenEstimator;

    public SemanticTextSplitter(int chunkSize, int chunkOverlap) {
        this.chunkSize = chunkSize;
        this.chunkOverlap = Math.min(chunkOverlap, chunkSize / 2); // 重叠不超过一半
        this.tokenEstimator = new SimpleTokenEstimator();
    }

    public SemanticTextSplitter(int chunkSize) {
        this(chunkSize, 50); // 默认重叠 50 个 Token
    }

    @Override
    public List<Document> apply(List<Document> documents) {
        List<Document> result = new ArrayList<>();
        for (Document document : documents) {
            String text = document.text();
            if (StrUtil.isEmpty(text)) {
                continue;
            }
            for (String chunk : splitTextRecursive(text)) {
                result.add(new Document(chunk));
            }
        }
        return result;
    }

    /**
     * 切分文本为字符串片段
     * 
     * @param text 文本内容
     * @return 切分后的字符串片段列表
     */
    protected List<String> splitText(String text) {
        if (StrUtil.isEmpty(text)) {
            return Collections.emptyList();
        }
        return splitTextRecursive(text);
    }

    /**
     * 切分文本（递归策略）
     *
     * @param text 待切分文本
     * @return 切分后的文本块列表
     */
    private List<String> splitTextRecursive(String text) {
        List<String> chunks = new ArrayList<>();

        // 如果文本不超过限制，直接返回
        int textTokens = tokenEstimator.estimate(text);
        if (textTokens <= chunkSize) {
            chunks.add(text.trim());
            return chunks;
        }

        // 尝试按不同分隔符切分
        List<String> splits = null;
        String usedSeparator = null;
        for (String separator : PARAGRAPH_SEPARATORS) {
            if (text.contains(separator)) {
                splits = Arrays.asList(text.split(Pattern.quote(separator)));
                usedSeparator = separator;
                break;
            }
        }

        // 如果没有找到段落分隔符，按句子切分
        if (splits == null || splits.size() == 1) {
            splits = splitBySentences(text);
            usedSeparator = ""; // 句子切分不需要分隔符
        }

        // 合并小片段
        chunks = mergeSplits(splits, usedSeparator);
        return chunks;
    }

    /**
     * 按句子切分
     *
     * @param text 待切分文本
     * @return 句子列表
     */
    private List<String> splitBySentences(String text) {
        // 使用正则表达式匹配句子结束位置
        List<String> sentences = new ArrayList<>();
        int lastEnd = 0;
        Matcher matcher = SENTENCE_END_PATTERN.matcher(text);
        while (matcher.find()) {
            String sentence = text.substring(lastEnd, matcher.end()).trim();
            if (StrUtil.isNotEmpty(sentence)) {
                sentences.add(sentence);
            }
            lastEnd = matcher.end();
        }

        // 添加剩余部分
        if (lastEnd < text.length()) {
            String remaining = text.substring(lastEnd).trim();
            if (StrUtil.isNotEmpty(remaining)) {
                sentences.add(remaining);
            }
        }
        return sentences.isEmpty() ? Collections.singletonList(text) : sentences;
    }

    /**
     * 合并切分后的小片段
     *
     * @param splits 切分后的片段列表
     * @param separator 片段间的分隔符
     * @return 合并后的文本块列表
     */
    private List<String> mergeSplits(List<String> splits, String separator) {
        List<String> chunks = new ArrayList<>();
        List<String> currentChunks = new ArrayList<>();
        int currentLength = 0;

        for (String split : splits) {
            if (StrUtil.isEmpty(split)) {
                continue;
            }
            int splitTokens = tokenEstimator.estimate(split);
            // 如果单个片段就超过限制，进一步递归切分
            if (splitTokens > chunkSize) {
                // 先保存当前累积的块
                if (!currentChunks.isEmpty()) {
                    String chunkText = String.join(separator, currentChunks);
                    chunks.add(chunkText.trim());
                    currentChunks.clear();
                    currentLength = 0;
                }
                // 递归切分大片段
                if (!separator.isEmpty()) {
                    // 如果是段落分隔符，尝试按句子切分
                    chunks.addAll(splitTextRecursive(split));
                } else {
                    // 如果已经是句子级别，强制按字符切分
                    chunks.addAll(forceSplitLongText(split));
                }
                continue;
            }
            // 计算加上分隔符的 Token 数
            int separatorTokens = StrUtil.isEmpty(separator) ? 0 : tokenEstimator.estimate(separator);
            // 如果加上这个片段会超过限制
            if (!currentChunks.isEmpty() && currentLength + splitTokens + separatorTokens > chunkSize) {
                // 保存当前块
                String chunkText = String.join(separator, currentChunks);
                chunks.add(chunkText.trim());

                // 处理重叠：保留最后几个片段
                currentChunks = getOverlappingChunks(currentChunks, separator);
                currentLength = estimateTokens(currentChunks, separator);
            }
            // 添加当前片段
            currentChunks.add(split);
            currentLength += splitTokens + separatorTokens;
        }

        // 添加最后一块
        if (!currentChunks.isEmpty()) {
            String chunkText = String.join(separator, currentChunks);
            chunks.add(chunkText.trim());
        }
        return chunks;
    }

    /**
     * 获取重叠的片段（用于保持上下文）
     *
     * @param chunks 当前片段列表
     * @param separator 片段间的分隔符
     * @return 重叠的片段列表
     */
    private List<String> getOverlappingChunks(List<String> chunks, String separator) {
        if (chunkOverlap == 0 || chunks.isEmpty()) {
            return new ArrayList<>();
        }

        // 从后往前取片段，直到达到重叠大小
        List<String> overlapping = new ArrayList<>();
        int tokens = 0;
        for (int i = chunks.size() - 1; i >= 0; i--) {
            String chunk = chunks.get(i);
            int chunkTokens = tokenEstimator.estimate(chunk);
            if (tokens + chunkTokens > chunkOverlap) {
                break;
            }
            // 添加到重叠列表前端
            overlapping.add(0, chunk);
            tokens += chunkTokens + (StrUtil.isEmpty(separator) ? 0 : tokenEstimator.estimate(separator));
        }
        return overlapping;
    }

    /**
     * 估算片段列表的总 Token 数
     *
     * @param chunks 片段列表
     * @param separator 片段间的分隔符
     * @return 总 Token 数
     */
    private int estimateTokens(List<String> chunks, String separator) {
        int total = 0;
        for (int i = 0; i < chunks.size(); i++) {
            total += tokenEstimator.estimate(chunks.get(i));
            if (i < chunks.size() - 1 && StrUtil.isNotEmpty(separator)) {
                total += tokenEstimator.estimate(separator);
            }
        }
        return total;
    }

    /**
     * 强制切分长文本（当语义切分失败时）
     *
     * @param text 待切分文本
     * @return 切分后的文本块列表
     */
    private List<String> forceSplitLongText(String text) {
        List<String> chunks = new ArrayList<>();
        int charsPerChunk = (int) (chunkSize * 0.8); // 保守估计
        for (int i = 0; i < text.length(); i += charsPerChunk) {
            int end = Math.min(i + charsPerChunk, text.length());
            String chunk = text.substring(i, end);
            chunks.add(chunk.trim());
        }
        log.warn("文本过长，已强制按字符切分，可能影响语义完整性");
        return chunks;
    }

    /**
     * 简单的 Token 估算器实现
     */
    private static class SimpleTokenEstimator implements MarkdownQaSplitter.TokenEstimator {

        @Override
        public int estimate(String text) {
            if (StrUtil.isEmpty(text)) {
                return 0;
            }

            int chineseChars = 0;
            int englishWords = 0;
            // 简单统计中英文
            for (char c : text.toCharArray()) {
                if (c >= 0x4E00 && c <= 0x9FA5) {
                    chineseChars++;
                }
            }
            // 英文单词估算
            String[] words = text.split("\\s+");
            for (String word : words) {
                if (word.matches(".*[a-zA-Z].*")) {
                    englishWords++;
                }
            }
            return chineseChars + (int) (englishWords * 1.3);
        }
    }

}
