"""
入口节点处理模块

功能：过滤、排序和格式化入口节点
"""

import logging
from typing import Dict, List, Tuple, Any

logger = logging.getLogger(__name__)


def filter_and_sort_entry_nodes(entry_nodes: dict,max_nodes_per_label: int = 5,min_score_threshold: float = 0.2,adaptive_limit: bool = True,min_total_nodes: int = 3,) -> dict:
    """
    过滤和重排序入口节点（按标签内排序和限制，带简单质量控制）
    
    策略：
    1. 对每个标签内的节点按 score 降序排序；
    2. 过滤掉分数过低的节点（min_score_threshold 以下）；
    3. 自适应限制：高分节点多时适当多留，低分节点多时按上限截断；
    4. 如果整体节点数量过少（例如 < min_total_nodes），在内存中做一次
       跨标签的轻量补充，不新增任何数据库查询，几乎不增加运行时间。
    
    Args:
        entry_nodes: 入口节点字典，格式如 {"设备": [{"name": "xxx", "score": 0.9}]}
        max_nodes_per_label: 每个标签最大节点数（默认 5）
        min_score_threshold: 入口节点最低得分阈值（默认 0.2）
        adaptive_limit: 是否根据得分分布自适应调整每个标签保留数量
        min_total_nodes: 全部标签合计至少保留的节点数量下限
        
    Returns:
        dict: 过滤和排序后的入口节点字典
    """
    if not entry_nodes:
        return {}
    
    filtered: Dict[str, List[Dict[str, Any]]] = {}
    
    # ========== 1. 标签内排序 + 质量过滤 ==========
    for label, nodes in entry_nodes.items():
        if not isinstance(nodes, list) or not nodes:
            continue
        
        # 1.1 按 score 降序排序（标签内排序）
        sorted_nodes = sorted(nodes, key=lambda x: x.get('score', 0), reverse=True)
        
        # 1.2 过滤低质量节点
        high_quality_nodes = [
            n for n in sorted_nodes
            if n.get('score', 0) >= min_score_threshold
        ]
        if not high_quality_nodes:
            # 如果全都低于阈值，则保底保留分数最高的 1 个，避免完全丢标签
            high_quality_nodes = sorted_nodes[:1]
        
        # 1.3 自适应限制
        if adaptive_limit:
            # 认为 score >= 0.7 为"高置信度"
            strong_nodes = [n for n in high_quality_nodes if n.get('score', 0) >= 0.7]
            if len(strong_nodes) >= 1:
                # 高置信度节点存在时，优先全部保留这些节点；
                # 如果数量仍然超过上限，则再按上限截断。
                if len(strong_nodes) > max_nodes_per_label:
                    filtered_nodes = strong_nodes[:max_nodes_per_label]
                else:
                    # 高置信度节点数量不多，可以再补少量次优节点
                    remain = max_nodes_per_label - len(strong_nodes)
                    extra = [n for n in high_quality_nodes if n not in strong_nodes][:remain]
                    filtered_nodes = strong_nodes + extra
            else:
                # 没有高置信度节点，直接按上限截断
                filtered_nodes = high_quality_nodes[:max_nodes_per_label]
        else:
            filtered_nodes = high_quality_nodes[:max_nodes_per_label]
        
        if filtered_nodes:
            filtered[label] = filtered_nodes
    
    total_before = sum(len(nodes) for nodes in entry_nodes.values() if isinstance(nodes, list))
    total_after = sum(len(nodes) for nodes in filtered.values())
    
    # ========== 2. 结果过少时的轻量级跨标签补充 ==========
    # 不触发任何新的检索，只在已有 entry_nodes 里做二次筛选，
    # 对整体性能影响可以忽略。
    if total_after < min_total_nodes:
        # 已经存在的 (label, id(node))，用于去重
        existing_keys = set()
        for label, nodes in filtered.items():
            for n in nodes:
                key = (label, n.get('name') or n.get('_name') or (n.get('node', {}).get('名称')) or id(n))
                existing_keys.add(key)
        
        # 收集所有可候选的节点
        candidates: List[Tuple[str, Dict[str, Any]]] = []
        for label, nodes in entry_nodes.items():
            if not isinstance(nodes, list):
                continue
            for n in nodes:
                score = n.get('score', 0)
                if score < min_score_threshold:
                    continue
                key = (label, n.get('name') or n.get('_name') or (n.get('node', {}).get('名称')) or id(n))
                if key in existing_keys:
                    continue
                candidates.append((label, n))
        
        # 按 score 降序排序，补充若干个节点，直到达到 min_total_nodes 或候选耗尽
        candidates.sort(key=lambda x: x[1].get('score', 0), reverse=True)
        for label, node in candidates:
            if sum(len(nodes) for nodes in filtered.values()) >= min_total_nodes:
                break
            # 控制单个标签不会被过度补充，这里允许最多是 max_nodes_per_label 的 2 倍
            if len(filtered.get(label, [])) >= max_nodes_per_label * 2:
                continue
            filtered.setdefault(label, []).append(node)
        
        total_after = sum(len(nodes) for nodes in filtered.values())
    
    logger.debug(
        f"入口节点过滤: 原始 {total_before} 个节点 "
        f"-> 过滤后 {total_after} 个节点 "
        f"({len(filtered)} 个标签，每个标签最多 {max_nodes_per_label} 个, "
        f"min_score_threshold={min_score_threshold}, adaptive_limit={adaptive_limit}, "
        f"min_total_nodes={min_total_nodes})"
    )
    
    return filtered


def format_entry_nodes(entry_nodes: dict) -> str:
    """
    格式化入口节点信息
    
    Args:
        entry_nodes: 入口节点字典，格式如 {"设备": [{"name": "xxx", "score": 0.9}]}
        
    Returns:
        str: 格式化后的节点信息字符串
    """
    if not entry_nodes:
        return "无入口节点"
    
    formatted = []
    for label, nodes in entry_nodes.items():
        if isinstance(nodes, list) and nodes:
            node_list = []
            # 显示所有节点（因为已经过滤过了）
            for node in nodes:
                name = node.get('name') or node.get('_name') or str(node)
                score = node.get('score', 0)
                node_list.append(f"  - {name} (score: {score:.2f})")
            formatted.append(f"{label}:\n" + "\n".join(node_list))
        elif nodes:
            formatted.append(f"{label}: {nodes}")

    logger.info(f"入口节点: {formatted}")
    return "\n\n".join(formatted) if formatted else "无入口节点"

