package com.lingyue.graph.service;

import com.lingyue.graph.service.NerToBlockService.TextElementDTO;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 文档块生成服务
 * 
 * 职责：
 * 1. 将纯文本拆分为段落块
 * 2. 识别标题、列表等结构
 * 3. 结合 NER 结果生成完整的 Block 结构
 * 
 * @author lingyue
 * @since 2026-01-21
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class DocumentBlockGeneratorService {
    
    private final NerToBlockService nerToBlockService;
    
    // 标题模式：一、二、三... 或 1. 2. 3...
    private static final Pattern HEADING_PATTERN_CN = Pattern.compile("^[一二三四五六七八九十]+、.+");
    private static final Pattern HEADING_PATTERN_NUM = Pattern.compile("^(\\d+\\.)+\\s*.+");
    private static final Pattern SUBHEADING_PATTERN = Pattern.compile("^(\\d+\\.\\d+)\\s+.+");
    
    // 列表模式
    private static final Pattern BULLET_PATTERN = Pattern.compile("^[•●○▪\\-\\*]\\s+.+");
    private static final Pattern ORDERED_PATTERN = Pattern.compile("^\\d+[.、)）]\\s+.+");
    
    /**
     * 将纯文本和 NER 实体生成完整的 Block 结构
     * 
     * @param documentId 文档ID
     * @param text 纯文本内容
     * @param entities NER 提取的实体列表
     * @return Block 列表
     */
    public List<BlockDTO> generateBlocks(String documentId, String text, List<Map<String, Object>> entities) {
        if (text == null || text.isEmpty()) {
            return Collections.emptyList();
        }
        
        // 1. 将文本拆分为段落
        List<ParagraphInfo> paragraphs = splitIntoParagraphs(text);
        
        // 2. 为每个段落生成 Block
        List<BlockDTO> blocks = new ArrayList<>();
        String rootBlockId = UUID.randomUUID().toString().replace("-", "");
        
        // 创建根块（page）
        BlockDTO rootBlock = new BlockDTO();
        rootBlock.setBlockId(rootBlockId);
        rootBlock.setDocumentId(documentId);
        rootBlock.setBlockType("page");
        rootBlock.setBlockIndex(0);
        rootBlock.setChildren(new ArrayList<>());
        blocks.add(rootBlock);
        
        // 为每个段落创建 Block
        int blockIndex = 1;
        for (ParagraphInfo para : paragraphs) {
            if (para.getText().trim().isEmpty()) {
                continue; // 跳过空段落
            }
            
            BlockDTO block = new BlockDTO();
            block.setBlockId(UUID.randomUUID().toString().replace("-", ""));
            block.setDocumentId(documentId);
            block.setParentId(rootBlockId);
            block.setBlockIndex(blockIndex++);
            block.setBlockType(detectBlockType(para.getText()));
            block.setCharStart(para.getCharStart());
            block.setCharEnd(para.getCharEnd());
            
            // 筛选属于该段落的实体
            List<Map<String, Object>> paragraphEntities = filterEntitiesForParagraph(
                    entities, para.getCharStart(), para.getCharEnd());
            
            // 将实体偏移转换为段落内偏移
            List<Map<String, Object>> localizedEntities = localizeEntities(
                    paragraphEntities, para.getCharStart());
            
            // 生成 TextElement 列表
            List<TextElementDTO> elements = nerToBlockService.convertToTextElements(
                    para.getText(), localizedEntities);
            block.setElements(elements);
            
            blocks.add(block);
            rootBlock.getChildren().add(block.getBlockId());
        }
        
        log.info("生成文档块完成: documentId={}, blockCount={}", documentId, blocks.size());
        return blocks;
    }
    
    /**
     * 将文本拆分为段落
     */
    private List<ParagraphInfo> splitIntoParagraphs(String text) {
        List<ParagraphInfo> paragraphs = new ArrayList<>();
        
        // 按换行符拆分
        String[] lines = text.split("\n");
        int currentPos = 0;
        StringBuilder currentParagraph = new StringBuilder();
        int paragraphStart = 0;
        
        for (String line : lines) {
            if (line.trim().isEmpty()) {
                // 空行，结束当前段落
                if (currentParagraph.length() > 0) {
                    ParagraphInfo para = new ParagraphInfo();
                    para.setText(currentParagraph.toString());
                    para.setCharStart(paragraphStart);
                    para.setCharEnd(currentPos);
                    paragraphs.add(para);
                    currentParagraph = new StringBuilder();
                }
                paragraphStart = currentPos + line.length() + 1;
            } else {
                // 检查是否是新段落的开始（标题、列表等）
                if (isNewParagraphStart(line) && currentParagraph.length() > 0) {
                    // 保存当前段落
                    ParagraphInfo para = new ParagraphInfo();
                    para.setText(currentParagraph.toString());
                    para.setCharStart(paragraphStart);
                    para.setCharEnd(currentPos);
                    paragraphs.add(para);
                    
                    currentParagraph = new StringBuilder();
                    paragraphStart = currentPos;
                }
                
                if (currentParagraph.length() > 0) {
                    currentParagraph.append(" "); // 多行合并时用空格连接
                }
                currentParagraph.append(line.trim());
            }
            
            currentPos += line.length() + 1; // +1 for newline
        }
        
        // 处理最后一个段落
        if (currentParagraph.length() > 0) {
            ParagraphInfo para = new ParagraphInfo();
            para.setText(currentParagraph.toString());
            para.setCharStart(paragraphStart);
            para.setCharEnd(text.length());
            paragraphs.add(para);
        }
        
        return paragraphs;
    }
    
    /**
     * 判断是否是新段落的开始
     */
    private boolean isNewParagraphStart(String line) {
        String trimmed = line.trim();
        return HEADING_PATTERN_CN.matcher(trimmed).matches()
                || HEADING_PATTERN_NUM.matcher(trimmed).matches()
                || SUBHEADING_PATTERN.matcher(trimmed).matches()
                || BULLET_PATTERN.matcher(trimmed).matches()
                || ORDERED_PATTERN.matcher(trimmed).matches();
    }
    
    /**
     * 检测块类型
     */
    private String detectBlockType(String text) {
        String trimmed = text.trim();
        
        // 标题检测
        if (HEADING_PATTERN_CN.matcher(trimmed).matches()) {
            // 一、二、三... 格式的一级标题
            return "heading1";
        }
        
        Matcher numMatcher = HEADING_PATTERN_NUM.matcher(trimmed);
        if (numMatcher.matches()) {
            // 计算数字层级
            String prefix = trimmed.split("\\s")[0];
            int dotCount = (int) prefix.chars().filter(c -> c == '.').count();
            if (dotCount <= 1) return "heading1";
            if (dotCount == 2) return "heading2";
            return "heading3";
        }
        
        if (SUBHEADING_PATTERN.matcher(trimmed).matches()) {
            return "heading2";
        }
        
        // 列表检测
        if (BULLET_PATTERN.matcher(trimmed).matches()) {
            return "bullet";
        }
        
        if (ORDERED_PATTERN.matcher(trimmed).matches()) {
            return "ordered";
        }
        
        // 默认为普通段落
        return "text";
    }
    
    /**
     * 筛选属于指定段落范围的实体
     */
    private List<Map<String, Object>> filterEntitiesForParagraph(List<Map<String, Object>> entities,
                                                                   int paragraphStart, int paragraphEnd) {
        if (entities == null) {
            return Collections.emptyList();
        }
        
        List<Map<String, Object>> result = new ArrayList<>();
        
        for (Map<String, Object> entity : entities) {
            int charStart = getEntityCharStart(entity);
            int charEnd = getEntityCharEnd(entity);
            
            // 实体完全在段落范围内
            if (charStart >= paragraphStart && charEnd <= paragraphEnd) {
                result.add(entity);
            }
        }
        
        return result;
    }
    
    /**
     * 将实体的全局偏移转换为段落内偏移
     */
    private List<Map<String, Object>> localizeEntities(List<Map<String, Object>> entities, int offset) {
        List<Map<String, Object>> result = new ArrayList<>();
        
        for (Map<String, Object> entity : entities) {
            Map<String, Object> localized = new HashMap<>(entity);
            
            Object positionObj = entity.get("position");
            if (positionObj instanceof Map) {
                @SuppressWarnings("unchecked")
                Map<String, Object> posMap = new HashMap<>((Map<String, Object>) positionObj);
                int charStart = getIntValue(posMap, "charStart", 0);
                int charEnd = getIntValue(posMap, "charEnd", 0);
                posMap.put("charStart", charStart - offset);
                posMap.put("charEnd", charEnd - offset);
                localized.put("position", posMap);
            }
            
            result.add(localized);
        }
        
        return result;
    }
    
    private int getEntityCharStart(Map<String, Object> entity) {
        Object positionObj = entity.get("position");
        if (positionObj instanceof Map) {
            @SuppressWarnings("unchecked")
            Map<String, Object> posMap = (Map<String, Object>) positionObj;
            return getIntValue(posMap, "charStart", -1);
        }
        return -1;
    }
    
    private int getEntityCharEnd(Map<String, Object> entity) {
        Object positionObj = entity.get("position");
        if (positionObj instanceof Map) {
            @SuppressWarnings("unchecked")
            Map<String, Object> posMap = (Map<String, Object>) positionObj;
            return getIntValue(posMap, "charEnd", -1);
        }
        return -1;
    }
    
    private int getIntValue(Map<String, Object> map, String key, int defaultValue) {
        Object value = map.get(key);
        if (value instanceof Number) {
            return ((Number) value).intValue();
        }
        return defaultValue;
    }
    
    // ==================== 数据传输对象 ====================
    
    @Data
    public static class BlockDTO {
        private String blockId;
        private String documentId;
        private String parentId;
        private List<String> children;
        private Integer blockIndex;
        private String blockType;
        private Integer charStart;
        private Integer charEnd;
        private List<TextElementDTO> elements;
    }
    
    @Data
    private static class ParagraphInfo {
        private String text;
        private int charStart;
        private int charEnd;
    }
}
