package com.lingyue.graph.neo4j;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.neo4j.driver.Driver;
import org.neo4j.driver.Result;
import org.neo4j.driver.Session;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Repository;

import java.util.*;

/**
 * Neo4j 关系仓库
 * 
 * 提供图关系的 CRUD 操作
 * 
 * @author lingyue
 * @since 2026-01-21
 */
@Slf4j
@Repository
@RequiredArgsConstructor
@ConditionalOnProperty(name = "neo4j.enabled", havingValue = "true")
public class Neo4jRelationRepository {
    
    private final Driver driver;
    
    /**
     * 创建关系
     * 
     * @param fromNodeId 源节点ID
     * @param toNodeId 目标节点ID
     * @param relationType 关系类型
     * @param properties 关系属性
     * @return 关系ID
     */
    public String createRelation(String fromNodeId, String toNodeId, String relationType, 
                                  Map<String, Object> properties) {
        // 动态关系类型需要使用 APOC 或字符串拼接
        String cypher = String.format(
                "MATCH (a {id: $fromId}), (b {id: $toId}) " +
                "MERGE (a)-[r:%s]->(b) " +
                "SET r += $props " +
                "RETURN id(r) AS relId",
                sanitizeRelationType(relationType)
        );
        
        Map<String, Object> params = new HashMap<>();
        params.put("fromId", fromNodeId);
        params.put("toId", toNodeId);
        params.put("props", properties != null ? properties : Collections.emptyMap());
        
        try (Session session = driver.session()) {
            Result result = session.run(cypher, params);
            if (result.hasNext()) {
                long relId = result.next().get("relId").asLong();
                return String.valueOf(relId);
            }
        } catch (Exception e) {
            log.error("创建关系失败: from={}, to={}, type={}, error={}", 
                    fromNodeId, toNodeId, relationType, e.getMessage());
            throw new RuntimeException("Neo4j 创建关系失败", e);
        }
        
        return null;
    }
    
    /**
     * 批量创建关系
     * 
     * @param relations 关系列表，每个包含 fromId, toId, type, properties
     */
    public void batchCreateRelations(List<Map<String, Object>> relations) {
        if (relations == null || relations.isEmpty()) {
            return;
        }
        
        // 按关系类型分组处理（因为 Cypher 不支持动态关系类型）
        Map<String, List<Map<String, Object>>> byType = new HashMap<>();
        for (Map<String, Object> rel : relations) {
            String type = (String) rel.getOrDefault("type", "RELATED");
            byType.computeIfAbsent(type, k -> new ArrayList<>()).add(rel);
        }
        
        try (Session session = driver.session()) {
            for (Map.Entry<String, List<Map<String, Object>>> entry : byType.entrySet()) {
                String relationType = sanitizeRelationType(entry.getKey());
                List<Map<String, Object>> typeRelations = entry.getValue();
                
                String cypher = String.format(
                        "UNWIND $relations AS rel " +
                        "MATCH (a {id: rel.fromId}), (b {id: rel.toId}) " +
                        "MERGE (a)-[r:%s]->(b) " +
                        "SET r += rel.properties " +
                        "RETURN count(r) AS count",
                        relationType
                );
                
                Result result = session.run(cypher, Map.of("relations", typeRelations));
                log.info("批量创建关系完成: type={}, count={}", 
                        relationType, result.single().get("count").asInt());
            }
        }
    }
    
    /**
     * 查找节点的所有关系
     */
    public List<Map<String, Object>> findByNodeId(String nodeId) {
        String cypher = 
                "MATCH (n {id: $nodeId})-[r]-(m) " +
                "RETURN id(r) AS relId, type(r) AS relationType, " +
                "       startNode(r).id AS fromId, endNode(r).id AS toId, " +
                "       properties(r) AS properties";
        
        List<Map<String, Object>> relations = new ArrayList<>();
        
        try (Session session = driver.session()) {
            Result result = session.run(cypher, Map.of("nodeId", nodeId));
            while (result.hasNext()) {
                org.neo4j.driver.Record record = result.next();
                Map<String, Object> rel = new HashMap<>();
                rel.put("id", String.valueOf(record.get("relId").asLong()));
                rel.put("type", record.get("relationType").asString());
                rel.put("fromId", record.get("fromId").asString());
                rel.put("toId", record.get("toId").asString());
                rel.put("properties", record.get("properties").asMap());
                relations.add(rel);
            }
        }
        
        return relations;
    }
    
    /**
     * 查找两个节点之间的关系
     */
    public List<Map<String, Object>> findBetween(String fromNodeId, String toNodeId) {
        String cypher = 
                "MATCH (a {id: $fromId})-[r]->(b {id: $toId}) " +
                "RETURN id(r) AS relId, type(r) AS relationType, properties(r) AS properties";
        
        List<Map<String, Object>> relations = new ArrayList<>();
        
        try (Session session = driver.session()) {
            Result result = session.run(cypher, Map.of("fromId", fromNodeId, "toId", toNodeId));
            while (result.hasNext()) {
                org.neo4j.driver.Record record = result.next();
                Map<String, Object> rel = new HashMap<>();
                rel.put("id", String.valueOf(record.get("relId").asLong()));
                rel.put("type", record.get("relationType").asString());
                rel.put("fromId", fromNodeId);
                rel.put("toId", toNodeId);
                rel.put("properties", record.get("properties").asMap());
                relations.add(rel);
            }
        }
        
        return relations;
    }
    
    /**
     * 按文档查找所有关系
     */
    public List<Map<String, Object>> findByDocumentId(String documentId) {
        String cypher = 
                "MATCH (a)-[r]->(b) " +
                "WHERE a.documentId = $documentId AND b.documentId = $documentId " +
                "RETURN id(r) AS relId, type(r) AS relationType, " +
                "       a.id AS fromId, a.name AS fromName, " +
                "       b.id AS toId, b.name AS toName, " +
                "       properties(r) AS properties";
        
        List<Map<String, Object>> relations = new ArrayList<>();
        
        try (Session session = driver.session()) {
            Result result = session.run(cypher, Map.of("documentId", documentId));
            while (result.hasNext()) {
                org.neo4j.driver.Record record = result.next();
                Map<String, Object> rel = new HashMap<>();
                rel.put("id", String.valueOf(record.get("relId").asLong()));
                rel.put("type", record.get("relationType").asString());
                rel.put("fromId", record.get("fromId").asString());
                rel.put("fromName", record.get("fromName").asString());
                rel.put("toId", record.get("toId").asString());
                rel.put("toName", record.get("toName").asString());
                rel.put("properties", record.get("properties").asMap());
                relations.add(rel);
            }
        }
        
        return relations;
    }
    
    /**
     * 删除关系
     */
    public void deleteById(long relationId) {
        String cypher = "MATCH ()-[r]->() WHERE id(r) = $relId DELETE r";
        
        try (Session session = driver.session()) {
            session.run(cypher, Map.of("relId", relationId));
            log.info("删除关系: relId={}", relationId);
        }
    }
    
    /**
     * 删除两节点间的所有关系
     */
    public void deleteBetween(String fromNodeId, String toNodeId) {
        String cypher = "MATCH (a {id: $fromId})-[r]->(b {id: $toId}) DELETE r";
        
        try (Session session = driver.session()) {
            session.run(cypher, Map.of("fromId", fromNodeId, "toId", toNodeId));
            log.info("删除关系: from={}, to={}", fromNodeId, toNodeId);
        }
    }
    
    /**
     * 统计关系数量
     */
    public long countByDocumentId(String documentId) {
        String cypher = 
                "MATCH (a {documentId: $documentId})-[r]->(b {documentId: $documentId}) " +
                "RETURN count(r) AS count";
        
        try (Session session = driver.session()) {
            Result result = session.run(cypher, Map.of("documentId", documentId));
            if (result.hasNext()) {
                return result.next().get("count").asLong();
            }
        }
        
        return 0;
    }
    
    /**
     * 获取关系类型统计
     */
    public Map<String, Long> getRelationTypeStats(String documentId) {
        String cypher = 
                "MATCH (a {documentId: $documentId})-[r]->(b {documentId: $documentId}) " +
                "RETURN type(r) AS relationType, count(r) AS count";
        
        Map<String, Long> stats = new HashMap<>();
        
        try (Session session = driver.session()) {
            Result result = session.run(cypher, Map.of("documentId", documentId));
            while (result.hasNext()) {
                org.neo4j.driver.Record record = result.next();
                stats.put(record.get("relationType").asString(), record.get("count").asLong());
            }
        }
        
        return stats;
    }
    
    /**
     * 清理关系类型名称（Neo4j 关系类型有限制）
     */
    private String sanitizeRelationType(String type) {
        if (type == null || type.isEmpty()) {
            return "RELATED";
        }
        // 只保留字母、数字和下划线，转大写
        return type.toUpperCase()
                .replaceAll("[^A-Z0-9_]", "_")
                .replaceAll("^[0-9]", "_$0"); // 不能以数字开头
    }
}
