"""
===========================================
图谱检索模块 - GraphRetrieval
===========================================
"""
import logging
import time
import os
from typing import Optional, List, Dict, Tuple, Any, Literal
# 导入拆分后的模块
from graph_search.query_intent import is_aggregate_query
from graph_search.cypher_validator import CypherValidator
from graph_search.cypher_fixer import parse_cypher_error,force_undirected_relationships,fix_cypher_based_on_error,fix_common_cypher_errors
from graph_search.entry_node_handler import filter_and_sort_entry_nodes,format_entry_nodes
from graph_search.result_formatter import format_entry_nodes_as_results
from graph_search.aggregate_handler import extract_aggregate_functions, is_aggregate_result_zero
from graph_search.init_prompts import *
# 加载 .env 文件中的环境变量
try:
    from dotenv import load_dotenv
    load_dotenv()
except ImportError:
    # 如果 python-dotenv 未安装，跳过加载（环境变量可能已通过其他方式设置）
    pass

logger = logging.getLogger(__name__)

# ========== ========== 导入依赖 ========== ==========
# 注意：需要安装以下包
# pip install neo4j langchain langchain-community neo4j-graphrag
from langchain_core.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate
from neo4j_graphrag.retrievers.text2cypher import extract_cypher


class GraphRetrieval:
    """
    知识图谱检索模块（带完整验证）
    
    功能说明：
    1. 生成Cypher查询语句（支持错误反馈自动修正）
    2. 多层验证：语法、Schema、Dry Run、LLM语义验证
    3. 多轮生成和修正机制（错误反馈给生成函数）
    4. 降级策略：模板匹配 -> LLM生成 -> 简单查询
    """
    def __init__(self,
        driver,  # Neo4j driver（GraphDatabase.driver实例）
        neo4j_schema: str,  # Neo4j schema字符串（从graph_service.py传递，由Neo4jGraph获取）
        llm = None,  # LLM实例（用于生成和验证Cypher，如ChatTongyi）
        allowed_node_types: List[str] = None,  # 允许的节点类型列表（从graph_service.py传递）
        allowed_relationship_types: List[str] = None,  # 允许的关系类型列表（从graph_service.py传递）
    ):
        """
        初始化图谱检索器
        """
        self.driver = driver
        self.neo4j_schema = neo4j_schema  # 完整schema（字符串格式，从graph_service.py传递）
        self.llm = llm
        # Dry Run验证
        self.skip_dry_run_validation = False
        # ========== 配置选项：Cypher生成最大尝试次数 ==========
        # 配置方式：在 .env 文件中添加 CYPHER_MAX_ATTEMPTS=2 或 CYPHER_MAX_ATTEMPTS=3
        self.default_max_attempts = int(os.getenv("CYPHER_MAX_ATTEMPTS", "2"))
        logger.info(f"GraphRetrieval: Cypher生成默认最大尝试次数: {self.default_max_attempts}（通过 .env 文件或环境变量 CYPHER_MAX_ATTEMPTS 配置）")

        # ========== 保存最后使用的Cypher（用于调试） ==========
        self.last_cypher = None
        
        # ========== 初始化验证器 ==========
        self.validator = CypherValidator(
            driver=self.driver,
            neo4j_schema=self.neo4j_schema,
            allowed_node_types=allowed_node_types,
            allowed_relationship_types=allowed_relationship_types
        )
    
    def _force_undirected_relationships(self, cypher: str) -> str:
        """包装函数，调用模块中的函数"""
        return force_undirected_relationships(cypher)
    
    def _parse_cypher_error(self, error_message: str, cypher: str) -> Dict[str, Any]:
        """包装函数，调用模块中的函数"""
        return parse_cypher_error(error_message, cypher)
    
    def _fix_cypher_based_on_error(self, cypher: str, error_info: Dict[str, Any]) -> Optional[str]:
        """包装函数，调用模块中的函数"""
        return fix_cypher_based_on_error(cypher, error_info)
    
    def _fix_common_cypher_errors(self, cypher: str) -> str:
        """包装函数，调用模块中的函数"""
        return fix_common_cypher_errors(cypher)
    
    # ========== ========== 规则验证层 ========== ==========
    
    def validate_cypher_syntax(self, cypher: str) -> Tuple[bool, Optional[str]]:
        """包装函数，调用验证器"""
        return self.validator.validate_syntax(cypher)
    
    def validate_cypher_schema(self, cypher: str) -> Tuple[bool, Optional[str]]:
        """包装函数，调用验证器的schema验证"""
        return self.validator.validate_schema(cypher)
    
    def dry_run_cypher(self, cypher: str) -> Tuple[bool, Optional[str], Optional[Dict[str, Any]]]:
        """包装函数，调用验证器"""
        return self.validator.dry_run(cypher, parse_error_func=self._parse_cypher_error)
    
    # ========== ========== Cypher生成（支持错误反馈） ========== ==========
    
    async def generate_cypher(
        self,
        query: str,
        entry_nodes: dict,
        previous_cypher: Optional[str] = None,
        validation_errors: Optional[List[str]] = None,
        is_aggregate: bool = False,
        error_type: Optional[str] = None,  # 'syntax_error' | 'schema_error' | 'empty_result'
        query_classification: Optional[Dict[str, Any]] = None,  # 从重写中获得的分类信息（可选）
    ) -> Optional[str]:
        """
        生成Cypher语句（支持错误反馈，自动修正）
        """
        from graph_search.query_intent import (
            has_time_in_query, 
            get_current_date_info,
            classify_time_aggregate_type
        )
        
        is_correction = validation_errors and previous_cypher
        
        # ========== 判断是否包含时间信息（优先使用分类信息） ==========
        if query_classification:
            has_time = query_classification.get("has_time", False)
            # 如果分类信息中有 time_type，直接使用；否则根据 has_time 和 is_aggregate 判断
            if is_aggregate and has_time:
                time_aggregate_type = query_classification.get("time_type")
                # 如果 time_type 为 None，尝试自动识别
                if time_aggregate_type is None:
                    time_aggregate_type = classify_time_aggregate_type(query)
            else:
                time_aggregate_type = None
            logger.info(f"使用分类信息: has_time={has_time}, time_aggregate_type={time_aggregate_type}")
        else:
            # 如果没有分类信息，使用原有逻辑
            has_time = has_time_in_query(query)
            time_aggregate_type = None
            if is_aggregate and has_time:
                time_aggregate_type = classify_time_aggregate_type(query)
            logger.info(f"使用自动识别: has_time={has_time}, time_aggregate_type={time_aggregate_type}")
        
        # ========== 获取系统时间信息（如果需要） ==========
        current_date_info = ""
        current_date_info_section = ""
        date_info = {}
        current_month_int = None  # 整数类型的月份（用于 prompt 中的 {current_month:02d} 格式化）
        current_month_formatted = ""  # 格式化的月份字符串（如 "01", "12"）
        if has_time:
            date_info = get_current_date_info()
            current_date_info = "\n".join([f"- {k}: {v}" for k, v in date_info.items()])
            current_date_info_section = f"系统时间信息:\n{current_date_info}\n\n"
            # 获取整数类型的月份（用于 prompt 模板中的 {current_month:02d} 格式化）
            current_month_int = int(date_info.get('current_month', 1))
            # 同时提供格式化好的字符串版本（备用）
            current_month_formatted = f"{current_month_int:02d}"
        
        # ========== 过滤和排序入口节点（按标签内排序和限制） ==========
        # 注意：如果 entry_nodes 已经在 search 方法中过滤过，这里再次过滤是幂等的（结果相同）
        entry_nodes = filter_and_sort_entry_nodes(
            entry_nodes, 
            max_nodes_per_label=5
        )
        
        # ========== 格式化入口节点信息 ==========

        entry_nodes_str = format_entry_nodes(entry_nodes)

        # ========== 路径模板占位说明 ==========
        # 注意：路径模板生成功能已移除，LLM 需要根据 schema 自行选择合理的路径
        path_templates_str = "（请根据 schema 和入口节点信息，自行选择合理的查询路径。）"

        # ========== 根据统计 / 普通查询，构造查询意图提示 ==========
        if is_aggregate:
            # 统计类：强制要求使用聚合函数，返回简洁聚合结果
            query_intent_hint = (
                "【查询类型】统计类问题，请根据用户问题和入口节点，生成包含聚合函数的 Cypher 查询，"
                "例如使用 COUNT、SUM、AVG、MAX、MIN 等，返回精简的聚合结果（如总数、平均值、排名等），"
                "不要返回大量明细记录。\n"
            )
        else:
            # 明细类：正常检索，用于后续 RAG
            query_intent_hint = (
                "【查询类型】检索类问题，请根据用户问题和入口节点，生成返回明细节点/关系的 Cypher 查询，"
                "用于后续的图谱问答检索。\n"
            )

        full_query = query_intent_hint + query
        
        # ========== 根据是否有错误，选择不同的prompt ==========
        if validation_errors and previous_cypher:
            # ========== 修正模式：包含错误信息和之前的Cypher ==========
            errors_str = "\n".join([f"  - {err}" for err in validation_errors])
            
            # ========== 根据错误类型选择对应的修正 prompt（统计类和明细类都使用相同的分类） ==========
            if error_type == 'syntax_error':
                # 语法错误：使用专门的语法修正 prompt
                prompt = correct_syntax_cypher_prompt.format_prompt(
                    schema=self.neo4j_schema,
                    entry_nodes=entry_nodes_str,
                    query=full_query,
                    errors=errors_str,
                    cypher=previous_cypher
                )
            elif error_type == 'schema_error':
                # Schema错误：使用专门的 Schema 修正 prompt
                prompt = correct_schema_cypher_prompt.format_prompt(
                    schema=self.neo4j_schema,
                    entry_nodes=entry_nodes_str,
                    query=full_query,
                    errors=errors_str,
                    cypher=previous_cypher
                )
            elif error_type == 'empty_result':
                # 空结果错误：使用专门的空结果修正 prompt
                prompt = correct_empty_result_cypher_prompt.format_prompt(
                    schema=self.neo4j_schema,
                    entry_nodes=entry_nodes_str,
                    query=full_query,
                    errors=errors_str,
                    cypher=previous_cypher
                )
            else:
                # 未分类错误：使用通用修正 prompt（向后兼容）
                # 统计类使用 correct_aggregate_cypher_prompt，明细类使用 correct_cypher_prompt
                if is_aggregate:
                    prompt = correct_aggregate_cypher_prompt.format_prompt(
                        schema=self.neo4j_schema,
                        entry_nodes=entry_nodes_str,
                        query=full_query,
                        errors=errors_str,
                        cypher=previous_cypher
                    )
                else:
                    prompt = correct_cypher_prompt.format_prompt(
                        schema=self.neo4j_schema,
                        entry_nodes=entry_nodes_str,
                        query=full_query,
                        errors=errors_str,
                        cypher=previous_cypher
                    )
        else:
            # ========== 初始生成模式 ==========
            if is_aggregate:
                # 统计类：根据是否带时间选择不同的prompt
                if has_time:
                    # 时间统计查询：根据时间段类型选择对应的prompt
                    if time_aggregate_type == "single":
                        # 单时间段统计
                        prompt = generate_single_time_aggregate_cypher_prompt.format_prompt(
                            schema=self.neo4j_schema,
                            entry_nodes=entry_nodes_str,
                            query=full_query,
                            current_date_info=current_date_info,
                            current_year=date_info.get("current_year", ""),
                            current_month=current_month_int,  # 传递整数，支持 :02d 格式化
                            last_year=date_info.get("last_year", ""),
                            next_year=date_info.get("next_year", ""),
                            next_month_start=date_info.get("next_month_start", ""),
                        )
                    elif time_aggregate_type == "dual":
                        # 双时间段统计（同比/环比）
                        # 计算上月（last_month）和前年（last_last_year，用于多时间段）
                        last_month = ""
                        if current_month_formatted:
                            month_int = int(current_month_formatted)
                            year_int = int(date_info.get("current_year", "2025"))
                            if month_int == 1:
                                last_month = f"{year_int - 1}-12"
                            else:
                                last_month = f"{year_int}-{month_int - 1:02d}"
                        
                        prompt = generate_dual_time_aggregate_cypher_prompt.format_prompt(
                            schema=self.neo4j_schema,
                            entry_nodes=entry_nodes_str,
                            query=full_query,
                            current_date_info=current_date_info,
                            current_year=date_info.get("current_year", ""),
                            current_month=current_month_int,  # 传递整数，支持 :02d 格式化
                            last_year=date_info.get("last_year", ""),
                            last_month=last_month,
                        )
                    else:  # multi
                        # 多时间段统计
                        # 计算前年（last_last_year）
                        last_last_year = str(int(date_info.get("current_year", "2025")) - 2) if date_info else ""
                        prompt = generate_multi_time_aggregate_cypher_prompt.format_prompt(
                            schema=self.neo4j_schema,
                            entry_nodes=entry_nodes_str,
                            query=full_query,
                            current_date_info=current_date_info,
                            current_year=date_info.get("current_year", ""),
                            last_year=date_info.get("last_year", ""),
                            last_last_year=last_last_year,
                        )
                else:
                    # 普通统计查询：使用原有prompt
                    prompt = generate_aggregate_cypher_prompt.format_prompt(
                        schema=self.neo4j_schema, # 使用完整schema
                        entry_nodes=entry_nodes_str,
                        query=full_query,
                        path_templates=path_templates_str,
                    )
            else:
                # 普通查询：支持时间过滤（在prompt中已包含时间处理说明）
                prompt = generate_cypher_prompt.format_prompt(
                    schema=self.neo4j_schema,  # 使用完整schema
                    entry_nodes=entry_nodes_str,
                    query=full_query,
                    path_templates=path_templates_str,
                    current_date_info=current_date_info if has_time else "",
                    current_year=date_info.get("current_year", "") if has_time else "",
                    current_month=current_month_int if has_time else 1,  # 传递整数，支持 :02d 格式化
                    next_year=date_info.get("next_year", "") if has_time else "",
                    next_month_start=date_info.get("next_month_start", "") if has_time else "",
                    current_date_info_section=current_date_info_section,
                )

        
        try:
            # ========== 调用LLM生成Cypher ==========
            # prompt可能是字符串或ChatPromptTemplate对象
            if isinstance(prompt, str):
                query_text = prompt
            elif hasattr(prompt, 'messages'):
                # 如果是ChatPromptTemplate格式，转换为字符串
                messages = prompt.messages
                query_text = "\n".join([
                    msg.content for msg in messages 
                    if hasattr(msg, 'content') and msg.content
                ])
            else:
                query_text = str(prompt)
            
            llm_output = await self.llm.ainvoke(query_text)
            
            # ========== 提取Cypher语句 ==========
            content = llm_output.content if hasattr(llm_output, 'content') else str(llm_output)
            
            # ========== 【新增】过滤 Qwen 的 <think> 标签 ==========
            import re
            content = re.sub(r'<think>.*?</think>', '', content, flags=re.DOTALL).strip()

            # 优先尝试从标记中提取（如果 LLM 按照格式输出）
            cypher = None
            if "【Cypher查询语句】" in content or "Cypher查询语句" in content:
                # 查找标记后的内容
                markers = ["【Cypher查询语句】", "Cypher查询语句", "Cypher:", "Cypher语句："]
                for marker in markers:
                    marker_pos = content.find(marker)
                    if marker_pos != -1:
                        # 提取标记后的内容
                        cypher_text = content[marker_pos + len(marker):].strip()
                        # 使用 extract_cypher 提取
                        cypher = extract_cypher(cypher_text)
                        if cypher:
                            break
            
            # 如果标记提取失败，使用原有的 extract_cypher（会从整个内容中提取）
            if not cypher:
                cypher = extract_cypher(content)
            
            if cypher:
                # ========== 后处理：修复常见语法错误 ==========
                cypher = self._fix_common_cypher_errors(cypher)
                
                # ========== 强制转换为无方向关系（确保所有关系都是无方向的） ==========
                # 即使prompt中已要求无方向关系，也要强制转换作为安全措施
                cypher = self._force_undirected_relationships(cypher)
                
                # 打印更全面的 Cypher 日志，便于调试（长度做一个上限，避免日志爆炸）
                preview = cypher if len(cypher) <= 1000 else cypher[:1000] + "..."
                logger.info(f"Cypher生成完整语句:\n{preview}")
            else:
                logger.warning(f"Cypher生成结果为空，LLM原始输出: {str(content)[:500]}...")
            return cypher
        except Exception as e:
            logger.error(f"LLM生成Cypher失败: {e}")
            return None
    
    # ========== ========== 多轮生成和验证 ========== ==========
    
    async def generate_and_validate_cypher(
        self,
        query: str,
        entry_nodes: dict,
        max_attempts: int = 3,
        is_aggregate: bool = False,
        query_classification: Optional[Dict[str, Any]] = None,  # 从重写中获得的分类信息（可选）
    ) -> Tuple[Optional[str], List[str]]:
        """
        生成和验证Cypher（完整流程：多轮生成+验证+自动修正）
        
        工作流程：
        1. 生成Cypher语句
        2. 验证Cypher语句（语法+LLM语义验证）
        3. 如果有错误，将错误反馈给生成函数，重新生成
        4. 关系方向校正
        5. 返回最终Cypher
        """
        errors_log = []
        previous_cypher = None
        error_type = None  # 当前错误类型：'syntax_error' | 'schema_error' | 'empty_result'
        
        for attempt in range(1, max_attempts + 1):
            # ========== 1. 生成/修正Cypher ==========
            cypher = await self.generate_cypher(
                query=query,
                entry_nodes=entry_nodes,
                previous_cypher=previous_cypher,
                validation_errors=errors_log if attempt > 1 else None,
                is_aggregate=is_aggregate,
                error_type=error_type,  # 传递错误类型，用于选择对应的修正 prompt
                query_classification=query_classification,  # 传递分类信息
            )
            
            if not cypher:
                errors_log.append(f"第{attempt}轮: Cypher生成失败")
                continue
            
            # ========== 2. 快速规则验证（必须通过） ==========
            syntax_valid, syntax_error = self.validate_cypher_syntax(cypher)
            
            if not syntax_valid:
                errors_log.append(f"语法错误: {syntax_error}")
                error_type = 'syntax_error'  # 标记为语法错误
                previous_cypher = cypher
                continue
            
            # ========== 3. Schema验证（在Dry Run之前执行） ==========
            schema_valid, schema_error = self.validate_cypher_schema(cypher)
            
            if not schema_valid:
                errors_log.append(f"Schema错误: {schema_error}")
                error_type = 'schema_error'
                previous_cypher = cypher
                continue
            
            # ========== 4. Dry Run验证（可选，可通过环境变量跳过） ==========
            if self.skip_dry_run_validation:
                dry_run_valid, dry_run_error, error_info = True, None, None  # 跳过验证
            else:
                dry_run_valid, dry_run_error, error_info = self.dry_run_cypher(cypher)
            
            if not dry_run_valid:
                # ========== 从 error_info 中提取错误类型 ==========
                if error_info and error_info.get('error_type'):
                    error_type = error_info['error_type']  # 'syntax_error' 或 'schema_error'
                else:
                    # 如果 error_info 中没有 error_type，根据错误信息推断
                    if 'Schema错误' in dry_run_error or 'schema' in dry_run_error.lower():
                        error_type = 'schema_error'
                    else:
                        error_type = 'syntax_error'
                
                # ========== 对于其他错误，尝试基于实际错误信息进行智能修复 ==========
                if error_info:
                    fixed_cypher = self._fix_cypher_based_on_error(cypher, error_info)
                    if fixed_cypher and fixed_cypher != cypher:
                        logger.info(f"[智能修复] 基于Dry Run错误信息自动修复Cypher")
                        logger.debug(f"[智能修复] 原始: {cypher[:200]}...")
                        logger.debug(f"[智能修复] 修复后: {fixed_cypher[:200]}...")
                        # 重新验证修复后的Cypher
                        cypher = fixed_cypher
                        error_type = None  # 重置错误类型，重新验证
                        # 重新进行验证（从语法验证开始）
                        continue
                
                # 如果无法自动修复，记录错误并继续
                error_detail = f"执行计划错误: {dry_run_error}"
                if error_info and error_info.get("error_position"):
                    pos = error_info["error_position"]
                    error_detail += f" (位置: 第{pos.get('line', '?')}行, 第{pos.get('column', '?')}列)"
                errors_log.append(error_detail)
                previous_cypher = cypher
                continue
            
            # ========== 所有验证通过 ==========
            # 注意：关系方向校正已移除，因为prompt中已明确要求使用无方向关系
            self.last_cypher = cypher  # 保存最后使用的Cypher
            return cypher, errors_log
        
        # ========== 所有尝试都失败 ==========
        logger.error(f"GraphRetrieval: Cypher生成失败，所有{max_attempts}轮尝试均失败")
        return None, errors_log

    async def search(
            self,
            query: str,
            entry_nodes: dict,
            max_attempts: int = None,
            query_type: Literal["auto", "aggregate", "detail"] = "auto",
            query_classification: Optional[Dict[str, Any]] = None,
    ) -> List[Dict[str, Any]]:
        """
        执行图谱检索（完整流程）
        """
        search_start = time.time()
        logger.info("GraphRetrieval.search: 开始图谱检索")

        # ========== 0. 过滤入口节点 ==========
        entry_nodes = filter_and_sort_entry_nodes(entry_nodes, max_nodes_per_label=5)
        logger.info("输出过滤后的入口节点")
#        logger.info(entry_nodes[0].get("名称"))
#        logger.info(entry_nodes)


        # ========== 1. 判断是否为聚合查询 ==========
        if query_classification:
            classification_query_type = query_classification.get("query_type", "detail")
            is_aggregate = (classification_query_type == "aggregate")
        else:
            if query_type == "aggregate":
                is_aggregate = True
            elif query_type == "detail":
                is_aggregate = False
            else:
                is_aggregate = is_aggregate_query(query)

        # ========== 2. 生成 Cypher ==========
        if max_attempts is None:
            max_attempts = self.default_max_attempts

        cypher, errors = await self.generate_and_validate_cypher(
            query=query,
            entry_nodes=entry_nodes,
            max_attempts=max_attempts,
            is_aggregate=is_aggregate,
            query_classification=query_classification,
        )

        if not cypher:
            return await self._fallback_to_simple_or_entry_nodes(query, entry_nodes, is_aggregate)

        # ========== 3. 执行查询（支持拆分）==========
        records, path_records = self._execute_cypher_with_split(cypher, is_aggregate)

        # ========== 4. 处理空结果或 0 值（仅聚合）==========
        if not records:
            logger.warning("Cypher查询结果为空，尝试重试")
            new_cypher, _ = await self.generate_and_validate_cypher(
                query=query,
                entry_nodes=entry_nodes,
                max_attempts=max_attempts,
                is_aggregate=is_aggregate,
                query_classification=query_classification,
            )
            if new_cypher and new_cypher != cypher:
                new_records, new_path_records = self._execute_cypher_with_split(new_cypher, is_aggregate)
                if new_records:
                    self.last_cypher = new_cypher
                    return self._process_results(new_records, new_path_records, query, is_aggregate)

        elif is_aggregate and is_aggregate_result_zero([dict(r) for r in records], cypher):
            logger.warning("统计类查询返回0，尝试重试")
            new_cypher, _ = await self.generate_and_validate_cypher(
                query=query,
                entry_nodes=entry_nodes,
                max_attempts=max_attempts,
                is_aggregate=is_aggregate,
                query_classification=query_classification,
            )
            if new_cypher and new_cypher != cypher:
                new_records, new_path_records = self._execute_cypher_with_split(new_cypher, is_aggregate)
                if new_records:
                    self.last_cypher = new_cypher
                    return self._process_results(new_records, new_path_records, query, is_aggregate)

        # ========== 5. 正常返回 ==========
        self.last_cypher = cypher
        result = self._process_results(records, path_records, query, is_aggregate)

        search_elapsed = (time.time() - search_start) * 1000
        logger.info(f"[时间统计] GraphRetrieval.search: 完成，总耗时: {search_elapsed:.2f}ms, 结果数: {len(result)}")
        return result

    def _execute_cypher_with_split(self, cypher: str, is_aggregate: bool):
        """执行 Cypher，支持拆分为 path + aggregate"""
        path_cypher, aggregate_cypher = self._split_aggregate_cypher(cypher)

        if path_cypher and aggregate_cypher:
            logger.info("[Cypher拆分] 执行 path 和 aggregate 查询")
            try:
                path_result = self.driver.execute_query(path_cypher)
                path_records = path_result.records
            except Exception as e:
                logger.warning(f"[Cypher拆分] Path 查询失败: {e}")
                path_records = None

            try:
                aggregate_result = self.driver.execute_query(aggregate_cypher)
                records = aggregate_result.records
            except Exception as e:
                logger.error(f"[Cypher拆分] Aggregate 查询失败: {e}，回退到原始 Cypher")
                result = self.driver.execute_query(cypher)
                return result.records, None
            return records, path_records
        else:
            result = self.driver.execute_query(cypher)
            return result.records, None

    def _process_results(
            self,
            records,
            path_records,
            query: str,
            is_aggregate: bool
    ) -> List[Dict[str, Any]]:
        """
        统一处理查询结果：
        - 聚合查询：注入 result 到 path，rerank path
        - 非聚合：确保每条记录有可序列化的 'result' 字段，并 rerank
        """

        if is_aggregate:
            aggregate_results = [dict(r) for r in records]
            aggregate_value = aggregate_results[0].get('result', aggregate_results[0])

            if path_records:
                raw_results = [dict(r) for r in path_records]
                # 注入聚合值
                for res in raw_results:
                    res['result'] = aggregate_value
                if raw_results:
                    raw_results[0]['_aggregate_value'] = aggregate_value

                raw_results = self._rerank_results(raw_results, query, is_aggregate=False)
                return raw_results
            else:
                return aggregate_results


        else:
            value_results = [dict(r) for r in records]
            if path_records:
                raw_results = [dict(r) for r in path_records]
                # 注入明细查询结果，防止 value_results 索引越界
                for i in range(len(raw_results)):
                    if i < len(value_results):
                        raw_results[i]['result'] = value_results[i]
                    else:
                        raw_results[i]['result'] = None  # 或 {}、""、[] 等默认值

                raw_results = self._rerank_results(raw_results, query, is_aggregate=False)
                return raw_results
            else:
                return value_results

    async def _fallback_to_simple_or_entry_nodes(
            self,
            query: str,
            entry_nodes: dict,
            is_aggregate: bool,
    ) -> List[Dict[str, Any]]:
        """降级：先试简单模板，再试入口节点"""
        # 尝试简单模板
        simple_cypher = self._generate_simple_template_cypher(entry_nodes, query, is_aggregate)
        if simple_cypher:
            try:
                result = self.driver.execute_query(simple_cypher)
                if result.records:
                    raw_results = [dict(r) for r in result.records]
                    raw_results = self._rerank_results(raw_results, query, is_aggregate)
                    logger.info(f"[降级策略] 简单模板查询成功，返回 {len(raw_results)} 条结果")
                    return raw_results
            except Exception as e:
                logger.warning(f"[降级策略] 简单模板查询失败: {e}")

        # 兜底：入口节点
        fallback_results = format_entry_nodes_as_results(entry_nodes)
        if fallback_results:
            logger.info(f"[降级策略] 返回 {len(fallback_results)} 条入口节点信息（兜底）")
            return fallback_results
        return []


    def _split_aggregate_cypher(self, cypher: str) -> Tuple[Optional[str], Optional[str]]:
        if not cypher or not cypher.strip():
            return None, None

        try:
            import re

            # 提取 MATCH 模式部分（支持前导任意内容，如 "cypher\n"）
            match_match = re.search(
                r'(?i)\bMATCH\s+(.+?)(?=\s+\b(?:WHERE|WITH|RETURN)\b)',
                cypher,
                re.DOTALL
            )
            if not match_match:
                logger.warning(f"[Cypher处理] 未找到合法的 MATCH 子句。输入片段: {cypher[:100]}...")
                return None, None

            full_match_clause = match_match.group(1).strip()

            # 提取 WHERE 子句（如果有）
            where_match = re.search(
                r'(?i)\bWHERE\s+(.+?)(?=\s+\b(?:WITH|RETURN)\b)',
                cypher,
                re.DOTALL
            )
            where_clause = " WHERE " + where_match.group(1).strip() if where_match else ""

            # 取第一个连通模式（逗号分隔时只取第一个）
            patterns = [p.strip() for p in full_match_clause.split(',') if p.strip()]
            if not patterns:
                logger.warning("[Cypher处理] MATCH 子句中无有效模式")
                return None, None

            first_pattern = patterns[0]

            # 构造 path 查询（自动命名 path 变量）
            path_var = "path"
            path_pattern = f"{path_var} = {first_pattern}"

            # 强制关系无向化（防御性）
            path_pattern = re.sub(r'<?-\[', '-[', path_pattern)
            path_pattern = re.sub(r'\]->?', ']-', path_pattern)

            path_cypher = f"MATCH {path_pattern}{where_clause} RETURN DISTINCT {path_var}"
            aggregate_cypher = cypher

            logger.debug(f"[Cypher处理] Path查询: {path_cypher}")
            return path_cypher, aggregate_cypher

        except Exception as e:
            logger.exception(f"[Cypher处理] 生成 path 查询失败: {e}")
            return None, None

    def _generate_simple_template_cypher(
        self,
        entry_nodes: dict,
        query: str,
        is_aggregate: bool
    ) -> Optional[str]:
        """
        生成简单的模板Cypher查询（降级策略）
        """
        if not entry_nodes:
            return None

        try:
            # 选择第一个标签和第一个节点作为起点
            first_label = list(entry_nodes.keys())[0]
            first_nodes = entry_nodes[first_label]
            if not first_nodes:
                return None

            first_node = first_nodes[0]
            node_name = first_node.get("name") or first_node.get("_name") or (first_node.get("node", {}).get("名称"))
            if not node_name:
                return None

            # 生成简单的查询：只查询入口节点及其直接邻居
            if is_aggregate:
                # 统计类：统计邻居数量
                cypher = (
                    f"MATCH path=(n:{first_label} {{名称: '{node_name}'}})-[]-(m)\n"
                    f"RETURN path, m"
                )
            else:
                # 明细类：返回邻居节点
                cypher = (
                    f"MATCH path=(n:{first_label} {{名称: '{node_name}'}})-[]-(m)\n"
                    f"RETURN path, m\n"
                    f"LIMIT 50"
                )

            logger.debug(f"[降级策略] 生成简单模板查询: {cypher}")
            return cypher
        except Exception as e:
            logger.warning(f"[降级策略] 生成简单模板查询失败: {e}")
            return None


    def _extract_path_info(self, record: Dict[str, Any]) -> List[str]:
        """
        从单个记录中提取路径相关信息，用于rerank
        """
        path_texts = []

        # 遍历记录中的所有值，查找路径信息
        for key, value in record.items():
            if value is None:
                continue

            # 如果值是路径对象
            if hasattr(value, 'nodes') and hasattr(value, 'relationships'):
                # 提取路径中的节点和关系信息
                try:
                    # 处理路径中的节点
                    for node in value.nodes:
                        node_info = f"节点: {dict(node).get('name', dict(node).get('名称', '未知节点'))} 类型: {list(node.labels)}"
                        path_texts.append(node_info)

                    # 处理路径中的关系
                    for rel in value.relationships:
                        rel_info = f"关系: {rel.type}"
                        path_texts.append(rel_info)
                except Exception as e:
                    logger.warning(f"提取路径信息时出错: {e}")

            # 如果值是字典，可能是节点或关系对象
            elif isinstance(value, dict):
                if 'labels' in value or 'type' in value:
                    # 这可能是节点或关系
                    if 'labels' in value:  # 节点
                        node_info = f"节点: {value.get('name', value.get('名称', '未知节点'))} 类型: {value.get('labels', [])}"
                        path_texts.append(node_info)
                    elif 'type' in value:  # 关系
                        rel_info = f"关系: {value.get('type')}"
                        path_texts.append(rel_info)

            # 如果值是列表，可能包含多个节点或关系
            elif isinstance(value, list):
                for item in value:
                    if isinstance(item, dict):
                        if 'labels' in item:  # 节点
                            node_info = f"节点: {item.get('name', item.get('名称', '未知节点'))} 类型: {item.get('labels', [])}"
                            path_texts.append(node_info)
                        elif 'type' in item:  # 关系
                            rel_info = f"关系: {item.get('type')}"
                            path_texts.append(rel_info)

        return path_texts

    def _rerank_results(self, results: List[Dict[str, Any]], query: str, is_aggregate: bool = False) -> List[
        Dict[str, Any]]:
        """
        使用 model_api 中的 rerank 对结果进行重排序。
        """
        if not results:
            return results

        # ✅ 统计类查询：不 rerank，直接赋分 1.0
        if is_aggregate:
            logger.info("检测到统计类查询，跳过 rerank，统一设置 rerank_score=1.0")
            reranked_results = []
            for result in results:
                result_copy = result.copy()
                result_copy['rerank_score'] = 1.0
                reranked_results.append(result_copy)
            return reranked_results

        # === 以下为原语义 rerank 逻辑 ===
        try:
            from modelsAPI.model_api import OpenaiAPI

            documents = []
            for result in results:
                path_texts = self._extract_path_info(result)
                doc_text = " ".join(path_texts) if path_texts else str(result)
                documents.append(doc_text)

            rerank_results = OpenaiAPI.rerank_query(query, documents, top_n=len(documents))

            reranked_results = []
            sorted_rerank_results = sorted(rerank_results, key=lambda x: x.get('scores', 0), reverse=True)
            for rerank_result in sorted_rerank_results:
                original_index = rerank_result['index']
                if original_index < len(results):
                    result_copy = results[original_index].copy()
                    result_copy['rerank_score'] = rerank_result['scores']
                    reranked_results.append(result_copy)

            return reranked_results

        except Exception as e:
            logger.error(f"重排序过程中出错: {e}")
            # 失败时也统一加默认分数（保持接口一致）
            fallback_results = []
            for result in results:
                result_copy = result.copy()
                result_copy['rerank_score'] = 0.0
                fallback_results.append(result_copy)
            return fallback_results

