package com.lingyue.graph.service;

import com.lingyue.graph.dto.KnowledgeGraphDTO;
import com.lingyue.graph.dto.KnowledgeGraphDTO.*;
import com.lingyue.graph.entity.GraphNode;
import com.lingyue.graph.entity.GraphRelation;
import com.lingyue.graph.repository.GraphNodeRepository;
import com.lingyue.graph.repository.GraphRelationRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 知识图谱服务
 * 
 * 提供知识图谱的查询和可视化数据转换功能
 * 
 * @author lingyue
 * @since 2026-01-21
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class KnowledgeGraphService {
    
    private final GraphNodeRepository nodeRepository;
    private final GraphRelationRepository relationRepository;
    
    // 类型到显示名称的映射
    private static final Map<String, String> TYPE_NAMES = Map.ofEntries(
            Map.entry("person", "人物"),
            Map.entry("org", "机构"),
            Map.entry("loc", "地点"),
            Map.entry("location", "地点"),
            Map.entry("date", "日期"),
            Map.entry("number", "数值"),
            Map.entry("money", "金额"),
            Map.entry("data", "数据"),
            Map.entry("concept", "概念"),
            Map.entry("device", "设备"),
            Map.entry("term", "术语"),
            Map.entry("entity", "实体"),
            Map.entry("other", "其他")
    );
    
    // 类型到图标的映射
    private static final Map<String, String> TYPE_ICONS = Map.ofEntries(
            Map.entry("person", "👤"),
            Map.entry("org", "🏢"),
            Map.entry("loc", "📍"),
            Map.entry("location", "📍"),
            Map.entry("date", "📅"),
            Map.entry("number", "🔢"),
            Map.entry("money", "💰"),
            Map.entry("data", "📊"),
            Map.entry("concept", "💡"),
            Map.entry("device", "🔧"),
            Map.entry("term", "📝"),
            Map.entry("entity", "🏷️"),
            Map.entry("other", "📌")
    );
    
    // 类型到颜色的映射
    private static final Map<String, String> TYPE_COLORS = Map.ofEntries(
            Map.entry("person", "#1890ff"),
            Map.entry("org", "#52c41a"),
            Map.entry("loc", "#fa8c16"),
            Map.entry("location", "#fa8c16"),
            Map.entry("date", "#722ed1"),
            Map.entry("number", "#13c2c2"),
            Map.entry("money", "#52c41a"),
            Map.entry("data", "#13c2c2"),
            Map.entry("concept", "#722ed1"),
            Map.entry("device", "#eb2f96"),
            Map.entry("term", "#2f54eb"),
            Map.entry("entity", "#1890ff"),
            Map.entry("other", "#8c8c8c")
    );
    
    /**
     * 获取文档的知识图谱
     */
    public KnowledgeGraphDTO getDocumentGraph(String documentId) {
        // 1. 获取文档的所有节点
        List<GraphNode> nodes = nodeRepository.findByDocumentId(documentId);
        if (nodes.isEmpty()) {
            return null;
        }
        
        // 2. 获取节点ID集合
        Set<String> nodeIds = nodes.stream()
                .map(GraphNode::getId)
                .collect(Collectors.toSet());
        
        // 3. 获取节点间的关系
        List<GraphRelation> relations = new ArrayList<>();
        for (String nodeId : nodeIds) {
            List<GraphRelation> nodeRelations = relationRepository.findByNodeId(nodeId);
            for (GraphRelation rel : nodeRelations) {
                // 只保留两端都在当前节点集中的关系
                if (nodeIds.contains(rel.getFromNodeId()) && nodeIds.contains(rel.getToNodeId())) {
                    relations.add(rel);
                }
            }
        }
        
        // 4. 去重关系
        relations = relations.stream()
                .collect(Collectors.collectingAndThen(
                        Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(GraphRelation::getId))),
                        ArrayList::new));
        
        // 5. 计算每个节点的关联数量
        Map<String, Integer> relationCounts = new HashMap<>();
        for (GraphRelation rel : relations) {
            relationCounts.merge(rel.getFromNodeId(), 1, Integer::sum);
            relationCounts.merge(rel.getToNodeId(), 1, Integer::sum);
        }
        
        // 6. 构建节点 DTO
        Map<String, GraphNode> nodeMap = nodes.stream()
                .collect(Collectors.toMap(GraphNode::getId, n -> n));
        
        List<NodeDTO> nodeDTOs = nodes.stream()
                .map(node -> buildNodeDTO(node, relationCounts.getOrDefault(node.getId(), 0)))
                .collect(Collectors.toList());
        
        // 7. 构建边 DTO
        List<EdgeDTO> edgeDTOs = relations.stream()
                .map(rel -> buildEdgeDTO(rel, nodeMap))
                .collect(Collectors.toList());
        
        // 8. 统计信息
        GraphStats stats = buildGraphStats(nodes, relations);
        
        return KnowledgeGraphDTO.builder()
                .documentId(documentId)
                .title("标记要素关系图谱")
                .nodes(nodeDTOs)
                .edges(edgeDTOs)
                .stats(stats)
                .build();
    }
    
    /**
     * 获取用户的全局知识图谱（跨文档）
     */
    public KnowledgeGraphDTO getUserGraph(String userId, int limit) {
        // 获取用户的所有节点
        List<GraphNode> allNodes = nodeRepository.findByUserId(userId);
        
        // 限制数量（取关联最多的）
        if (allNodes.size() > limit) {
            // TODO: 按重要性排序后截取
            allNodes = allNodes.subList(0, limit);
        }
        
        if (allNodes.isEmpty()) {
            return KnowledgeGraphDTO.builder()
                    .title("我的知识图谱")
                    .nodes(Collections.emptyList())
                    .edges(Collections.emptyList())
                    .stats(GraphStats.builder()
                            .totalNodes(0)
                            .totalEdges(0)
                            .nodesByType(Collections.emptyMap())
                            .edgesByType(Collections.emptyMap())
                            .build())
                    .build();
        }
        
        Set<String> nodeIds = allNodes.stream()
                .map(GraphNode::getId)
                .collect(Collectors.toSet());
        
        List<GraphRelation> relations = new ArrayList<>();
        for (String nodeId : nodeIds) {
            List<GraphRelation> nodeRelations = relationRepository.findByNodeId(nodeId);
            for (GraphRelation rel : nodeRelations) {
                if (nodeIds.contains(rel.getFromNodeId()) && nodeIds.contains(rel.getToNodeId())) {
                    relations.add(rel);
                }
            }
        }
        
        // 去重
        relations = relations.stream()
                .collect(Collectors.collectingAndThen(
                        Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(GraphRelation::getId))),
                        ArrayList::new));
        
        Map<String, Integer> relationCounts = new HashMap<>();
        for (GraphRelation rel : relations) {
            relationCounts.merge(rel.getFromNodeId(), 1, Integer::sum);
            relationCounts.merge(rel.getToNodeId(), 1, Integer::sum);
        }
        
        Map<String, GraphNode> nodeMap = allNodes.stream()
                .collect(Collectors.toMap(GraphNode::getId, n -> n));
        
        List<NodeDTO> nodeDTOs = allNodes.stream()
                .map(node -> buildNodeDTO(node, relationCounts.getOrDefault(node.getId(), 0)))
                .collect(Collectors.toList());
        
        List<EdgeDTO> edgeDTOs = relations.stream()
                .map(rel -> buildEdgeDTO(rel, nodeMap))
                .collect(Collectors.toList());
        
        GraphStats stats = buildGraphStats(allNodes, relations);
        
        return KnowledgeGraphDTO.builder()
                .title("我的知识图谱")
                .nodes(nodeDTOs)
                .edges(edgeDTOs)
                .stats(stats)
                .build();
    }
    
    /**
     * 获取实体列表（按类型分组）
     * 
     * 优化：批量获取关系，避免 N+1 查询
     */
    public List<EntityGroupDTO> getEntityListGroupedByType(String documentId, String filterType) {
        List<GraphNode> nodes = nodeRepository.findByDocumentId(documentId);
        
        if (filterType != null && !filterType.isEmpty() && !"all".equals(filterType)) {
            nodes = nodes.stream()
                    .filter(n -> filterType.equalsIgnoreCase(n.getType()))
                    .collect(Collectors.toList());
        }
        
        if (nodes.isEmpty()) {
            return Collections.emptyList();
        }
        
        // 按类型分组
        Map<String, List<GraphNode>> nodesByType = nodes.stream()
                .collect(Collectors.groupingBy(n -> n.getType() != null ? n.getType().toLowerCase() : "other"));
        
        // 构建节点ID到节点的映射（用于快速查找）
        Map<String, GraphNode> nodeMap = nodes.stream()
                .collect(Collectors.toMap(GraphNode::getId, n -> n));
        Set<String> nodeIds = nodeMap.keySet();
        
        // 批量获取所有相关关系（一次查询）
        // 注意：这里假设文档的节点数量不会太多，实际生产环境可能需要分批处理
        List<GraphRelation> allRelations = new ArrayList<>();
        for (GraphNode node : nodes) {
            allRelations.addAll(relationRepository.findByNodeId(node.getId()));
        }
        
        // 去重并只保留两端都在当前节点集中的关系
        Set<String> seenRelIds = new HashSet<>();
        List<GraphRelation> filteredRelations = allRelations.stream()
                .filter(r -> seenRelIds.add(r.getId()))
                .filter(r -> nodeIds.contains(r.getFromNodeId()) || nodeIds.contains(r.getToNodeId()))
                .collect(Collectors.toList());
        
        // 计算每个节点的关联数和关联实体预览
        Map<String, List<RelatedEntityDTO>> relatedEntitiesMap = new HashMap<>();
        Map<String, Integer> relationCountMap = new HashMap<>();
        
        for (GraphRelation rel : filteredRelations) {
            // 处理 from 节点
            if (nodeIds.contains(rel.getFromNodeId())) {
                relationCountMap.merge(rel.getFromNodeId(), 1, Integer::sum);
                
                // 添加关联实体预览（限制3个）
                GraphNode otherNode = nodeMap.get(rel.getToNodeId());
                if (otherNode != null) {
                    List<RelatedEntityDTO> related = relatedEntitiesMap.computeIfAbsent(
                            rel.getFromNodeId(), k -> new ArrayList<>());
                    if (related.size() < 3) {
                        related.add(RelatedEntityDTO.builder()
                                .id(otherNode.getId())
                                .name(otherNode.getName())
                                .relationType(rel.getRelationType())
                                .build());
                    }
                }
            }
            
            // 处理 to 节点
            if (nodeIds.contains(rel.getToNodeId())) {
                relationCountMap.merge(rel.getToNodeId(), 1, Integer::sum);
                
                GraphNode otherNode = nodeMap.get(rel.getFromNodeId());
                if (otherNode != null) {
                    List<RelatedEntityDTO> related = relatedEntitiesMap.computeIfAbsent(
                            rel.getToNodeId(), k -> new ArrayList<>());
                    if (related.size() < 3) {
                        related.add(RelatedEntityDTO.builder()
                                .id(otherNode.getId())
                                .name(otherNode.getName())
                                .relationType(rel.getRelationType())
                                .build());
                    }
                }
            }
        }
        
        // 构建分组列表
        List<EntityGroupDTO> groups = new ArrayList<>();
        
        for (Map.Entry<String, List<GraphNode>> entry : nodesByType.entrySet()) {
            String type = entry.getKey();
            List<GraphNode> typeNodes = entry.getValue();
            
            List<EntityListItemDTO> items = typeNodes.stream()
                    .map(node -> EntityListItemDTO.builder()
                            .id(node.getId())
                            .name(node.getName())
                            .type(type)
                            .typeName(TYPE_NAMES.getOrDefault(type, type))
                            .icon(TYPE_ICONS.getOrDefault(type, "📌"))
                            .color(TYPE_COLORS.getOrDefault(type, "#8c8c8c"))
                            .occurrenceCount(1) // TODO: 计算实际出现次数
                            .relationCount(relationCountMap.getOrDefault(node.getId(), 0))
                            .relatedEntities(relatedEntitiesMap.getOrDefault(node.getId(), Collections.emptyList()))
                            .build())
                    .collect(Collectors.toList());
            
            groups.add(EntityGroupDTO.builder()
                    .type(type)
                    .typeName(TYPE_NAMES.getOrDefault(type, type))
                    .color(TYPE_COLORS.getOrDefault(type, "#8c8c8c"))
                    .count(items.size())
                    .entities(items)
                    .build());
        }
        
        // 按预定义顺序排序
        List<String> typeOrder = List.of("entity", "concept", "data", "number", "money", "person", "org", "loc", "location", "date", "device", "term", "other");
        groups.sort((a, b) -> {
            int indexA = typeOrder.indexOf(a.getType());
            int indexB = typeOrder.indexOf(b.getType());
            if (indexA < 0) indexA = 999;
            if (indexB < 0) indexB = 999;
            return indexA - indexB;
        });
        
        return groups;
    }
    
    /**
     * 获取实体详情
     */
    public EntityDetailDTO getEntityDetail(String entityId) {
        GraphNode node = nodeRepository.selectById(entityId);
        if (node == null) {
            return null;
        }
        
        String type = node.getType() != null ? node.getType().toLowerCase() : "other";
        
        // 获取所有关联实体
        List<GraphRelation> relations = relationRepository.findByNodeId(entityId);
        List<RelatedEntityDTO> allRelations = new ArrayList<>();
        
        for (GraphRelation rel : relations) {
            String otherId = rel.getFromNodeId().equals(entityId) ? rel.getToNodeId() : rel.getFromNodeId();
            GraphNode otherNode = nodeRepository.selectById(otherId);
            if (otherNode != null) {
                allRelations.add(RelatedEntityDTO.builder()
                        .id(otherNode.getId())
                        .name(otherNode.getName())
                        .relationType(rel.getRelationType())
                        .build());
            }
        }
        
        // 获取出现位置
        List<OccurrenceDTO> occurrences = new ArrayList<>();
        if (node.getPosition() instanceof Map) {
            @SuppressWarnings("unchecked")
            Map<String, Object> pos = (Map<String, Object>) node.getPosition();
            occurrences.add(OccurrenceDTO.builder()
                    .documentId(node.getDocumentId())
                    .position(pos)
                    .build());
        }
        
        // 提取上下文
        if (node.getMetadata() instanceof Map) {
            @SuppressWarnings("unchecked")
            Map<String, Object> meta = (Map<String, Object>) node.getMetadata();
            String context = (String) meta.get("context");
            if (context != null && !occurrences.isEmpty()) {
                occurrences.get(0).setContext(context);
            }
        }
        
        return EntityDetailDTO.builder()
                .id(node.getId())
                .name(node.getName())
                .type(type)
                .typeName(TYPE_NAMES.getOrDefault(type, type))
                .value(node.getValue())
                .icon(TYPE_ICONS.getOrDefault(type, "📌"))
                .color(TYPE_COLORS.getOrDefault(type, "#8c8c8c"))
                .occurrenceCount(1)
                .allRelations(allRelations)
                .occurrences(occurrences)
                .metadata(node.getMetadata() instanceof Map ? (Map<String, Object>) node.getMetadata() : null)
                .build();
    }
    
    /**
     * 搜索实体
     */
    public List<EntityListItemDTO> searchEntities(String keyword, String documentId, int limit) {
        List<GraphNode> nodes;
        
        if (documentId != null && !documentId.isEmpty()) {
            nodes = nodeRepository.findByDocumentId(documentId);
        } else {
            // 全局搜索需要另外的查询方法
            // TODO: 添加全局搜索的 Repository 方法
            nodes = new ArrayList<>();
        }
        
        // 过滤匹配的节点
        String lowerKeyword = keyword.toLowerCase();
        List<GraphNode> matchedNodes = nodes.stream()
                .filter(n -> n.getName() != null && n.getName().toLowerCase().contains(lowerKeyword))
                .limit(limit)
                .collect(Collectors.toList());
        
        return matchedNodes.stream()
                .map(node -> {
                    String type = node.getType() != null ? node.getType().toLowerCase() : "other";
                    return EntityListItemDTO.builder()
                            .id(node.getId())
                            .name(node.getName())
                            .type(type)
                            .typeName(TYPE_NAMES.getOrDefault(type, type))
                            .icon(TYPE_ICONS.getOrDefault(type, "📌"))
                            .color(TYPE_COLORS.getOrDefault(type, "#8c8c8c"))
                            .build();
                })
                .collect(Collectors.toList());
    }
    
    // ==================== 辅助方法 ====================
    
    private NodeDTO buildNodeDTO(GraphNode node, int relationCount) {
        String type = node.getType() != null ? node.getType().toLowerCase() : "other";
        
        // 根据关联数量计算节点大小
        int size = 40 + Math.min(relationCount * 5, 30);
        
        return NodeDTO.builder()
                .id(node.getId())
                .name(node.getName())
                .type(type)
                .icon(TYPE_ICONS.getOrDefault(type, "📌"))
                .color(TYPE_COLORS.getOrDefault(type, "#8c8c8c"))
                .size(size)
                .relationCount(relationCount)
                .occurrenceCount(1)
                .value(node.getValue())
                .documentIds(node.getDocumentId() != null ? List.of(node.getDocumentId()) : Collections.emptyList())
                .position(node.getPosition() instanceof Map ? (Map<String, Object>) node.getPosition() : null)
                .metadata(node.getMetadata() instanceof Map ? (Map<String, Object>) node.getMetadata() : null)
                .build();
    }
    
    private EdgeDTO buildEdgeDTO(GraphRelation relation, Map<String, GraphNode> nodeMap) {
        GraphNode fromNode = nodeMap.get(relation.getFromNodeId());
        GraphNode toNode = nodeMap.get(relation.getToNodeId());
        
        String label = relation.getRelationType();
        if (relation.getMetadata() instanceof Map) {
            @SuppressWarnings("unchecked")
            Map<String, Object> meta = (Map<String, Object>) relation.getMetadata();
            String originalType = (String) meta.get("originalType");
            if (originalType != null) {
                label = originalType;
            }
        }
        
        return EdgeDTO.builder()
                .id(relation.getId())
                .source(relation.getFromNodeId())
                .sourceName(fromNode != null ? fromNode.getName() : null)
                .target(relation.getToNodeId())
                .targetName(toNode != null ? toNode.getName() : null)
                .relationType(relation.getRelationType())
                .label(label)
                .weight(1.0)
                .metadata(relation.getMetadata() instanceof Map ? (Map<String, Object>) relation.getMetadata() : null)
                .build();
    }
    
    private GraphStats buildGraphStats(List<GraphNode> nodes, List<GraphRelation> relations) {
        Map<String, Integer> nodesByType = nodes.stream()
                .collect(Collectors.groupingBy(
                        n -> n.getType() != null ? n.getType().toLowerCase() : "other",
                        Collectors.collectingAndThen(Collectors.counting(), Long::intValue)));
        
        Map<String, Integer> edgesByType = relations.stream()
                .collect(Collectors.groupingBy(
                        r -> r.getRelationType() != null ? r.getRelationType() : "unknown",
                        Collectors.collectingAndThen(Collectors.counting(), Long::intValue)));
        
        return GraphStats.builder()
                .totalNodes(nodes.size())
                .totalEdges(relations.size())
                .nodesByType(nodesByType)
                .edgesByType(edgesByType)
                .build();
    }
}
