//! SiFu (Signal Fully-connected flowing) 信号传播引擎
//!
//! 受 BriLLM 类脑架构和 Nature 2016 语义图谱论文启发。
//! 实现基于图的能量扩散查询：知识以信号形式在有向加权图中流动，
//! 遵循"最小阻力原则"，高能量节点即为最相关结果。
//!
//! ## 核心概念
//! - **TransformMatrix**: 边上的转换矩阵，描述信号从源到目标的"阻力/增益"
//! - **EdgeWeight**: 边权重，包含 signal_strength、co_activation_count 和可选 transform_matrix
//! - **SiFuQueryEngine**: 信号传播引擎，执行 能量注入→邻居获取→权重乘法→激活→剪枝 循环
//! - **FlowTrace**: 完整传播路径记录，用于可解释性

use std::collections::HashMap;
use std::sync::Arc;

use serde::{Deserialize, Serialize};

// ============================================================================
// TransformMatrix — 小型方阵（嵌入维度无关的低维语义空间变换）
// ============================================================================

/// 低维转换矩阵（d×d，d 通常为 3~8）
///
/// 描述信号从一个节点传播到另一个节点时的"增益/衰减/旋转"。
/// 类比大脑中突触的信号传递特性。
///
/// 为避免引入大型线性代数依赖，手写小矩阵运算。
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TransformMatrix {
    /// 行数
    pub rows: usize,
    /// 列数
    pub cols: usize,
    /// 按行主序存储的矩阵元素（长度 = rows × cols）
    pub data: Vec<f32>,
}

impl TransformMatrix {
    /// 创建单位矩阵
    pub fn identity(dim: usize) -> Self {
        let mut data = vec![0.0f32; dim * dim];
        for i in 0..dim {
            data[i * dim + i] = 1.0;
        }
        Self { rows: dim, cols: dim, data }
    }

    /// 创建均匀缩放矩阵
    pub fn scale(dim: usize, factor: f32) -> Self {
        let mut data = vec![0.0f32; dim * dim];
        for i in 0..dim {
            data[i * dim + i] = factor;
        }
        Self { rows: dim, cols: dim, data }
    }

    /// 从 Vec<Vec<f32>> 创建
    pub fn from_rows(rows_data: &[Vec<f32>]) -> Self {
        let rows = rows_data.len();
        let cols = rows_data.first().map_or(0, |r| r.len());
        let mut data = Vec::with_capacity(rows * cols);
        for row in rows_data {
            data.extend_from_slice(row);
        }
        Self { rows, cols, data }
    }

    /// 矩阵 × 向量乘法
    ///
    /// 输入向量长度必须等于 cols，输出向量长度等于 rows。
    pub fn mul_vec(&self, vec: &[f32]) -> Vec<f32> {
        debug_assert_eq!(vec.len(), self.cols, "Vector length must equal matrix cols");
        let mut result = vec![0.0f32; self.rows];
        for i in 0..self.rows {
            let mut sum = 0.0f32;
            for j in 0..self.cols {
                sum += self.data[i * self.cols + j] * vec[j];
            }
            result[i] = sum;
        }
        result
    }

    /// 矩阵 × 矩阵乘法
    pub fn mul_mat(&self, other: &TransformMatrix) -> TransformMatrix {
        debug_assert_eq!(self.cols, other.rows, "Matrix dimensions must be compatible");
        let mut data = vec![0.0f32; self.rows * other.cols];
        for i in 0..self.rows {
            for j in 0..other.cols {
                let mut sum = 0.0f32;
                for k in 0..self.cols {
                    sum += self.data[i * self.cols + k] * other.data[k * other.cols + j];
                }
                data[i * other.cols + j] = sum;
            }
        }
        TransformMatrix { rows: self.rows, cols: other.cols, data }
    }

    /// 获取元素 (row, col)
    pub fn get(&self, row: usize, col: usize) -> f32 {
        self.data[row * self.cols + col]
    }

    /// Frobenius 范数（矩阵"大小"度量）
    pub fn frobenius_norm(&self) -> f32 {
        self.data.iter().map(|x| x * x).sum::<f32>().sqrt()
    }
}

impl Default for TransformMatrix {
    fn default() -> Self {
        Self::identity(3) // 默认 3×3 单位矩阵（对应 3 语义轴）
    }
}

// ============================================================================
// EdgeWeight — 边权重，包含信号强度和可选转换矩阵
// ============================================================================

/// 边权重数据结构
///
/// 三层权重模型：
/// 1. **全局默认**：所有边共享的基础 TransformMatrix
/// 2. **类型共享**：按 relation_type 共享的 TransformMatrix
/// 3. **独立边**：每条边独立的 TransformMatrix（最高优先级）
///
/// `signal_strength` 是标量权重（0.0~1.0），控制信号总衰减。
/// `transform_matrix` 是方向性转换，控制信号在不同语义维度上的映射。
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EdgeWeight {
    /// 源节点 ID
    pub source_id: String,
    /// 目标节点 ID
    pub target_id: String,
    /// 关系类型（用于类型共享权重查找）
    pub relation_type: String,
    /// 标量信号强度 (0.0~1.0)，控制总衰减
    #[serde(default = "default_signal_strength")]
    pub signal_strength: f64,
    /// 共激活次数（赫布学习用）
    #[serde(default)]
    pub co_activation_count: u64,
    /// 独立边的转换矩阵（优先级最高）
    #[serde(skip_serializing_if = "Option::is_none")]
    pub transform_matrix: Option<TransformMatrix>,
    /// 最后更新时间
    pub updated_at: i64,
}

fn default_signal_strength() -> f64 { 1.0 }

impl EdgeWeight {
    /// 创建新的边权重
    pub fn new(
        source_id: impl Into<String>,
        target_id: impl Into<String>,
        relation_type: impl Into<String>,
    ) -> Self {
        Self {
            source_id: source_id.into(),
            target_id: target_id.into(),
            relation_type: relation_type.into(),
            signal_strength: 1.0,
            co_activation_count: 0,
            transform_matrix: None,
            updated_at: chrono_now_ms(),
        }
    }

    /// 设置信号强度
    pub fn with_signal_strength(mut self, s: f64) -> Self {
        self.signal_strength = s.clamp(0.0, 1.0);
        self
    }

    /// 设置转换矩阵
    pub fn with_transform(mut self, matrix: TransformMatrix) -> Self {
        self.transform_matrix = Some(matrix);
        self
    }

    /// 记录一次共激活（赫布学习）
    pub fn record_co_activation(&mut self) {
        self.co_activation_count += 1;
        self.updated_at = chrono_now_ms();
    }
}

// ============================================================================
// EdgeWeightStore — 三层权重存储
// ============================================================================

/// 三层权重存储
///
/// 按优先级查找边权重：
/// 1. 独立边权重 (source_id, target_id) → EdgeWeight
/// 2. 类型共享权重 relation_type → TransformMatrix
/// 3. 全局默认权重 → TransformMatrix
pub struct EdgeWeightStore {
    /// 全局默认转换矩阵
    global_default: TransformMatrix,
    /// 类型共享转换矩阵：relation_type → TransformMatrix
    type_defaults: HashMap<String, TransformMatrix>,
    /// 独立边权重：(source_id, target_id) → EdgeWeight
    edge_weights: HashMap<(String, String), EdgeWeight>,
}

impl EdgeWeightStore {
    /// 创建新的权重存储
    pub fn new(semantic_dim: usize) -> Self {
        Self {
            global_default: TransformMatrix::identity(semantic_dim),
            type_defaults: HashMap::new(),
            edge_weights: HashMap::new(),
        }
    }

    /// 设置全局默认矩阵
    pub fn set_global_default(&mut self, matrix: TransformMatrix) {
        self.global_default = matrix;
    }

    /// 设置类型共享矩阵
    pub fn set_type_default(&mut self, relation_type: impl Into<String>, matrix: TransformMatrix) {
        self.type_defaults.insert(relation_type.into(), matrix);
    }

    /// 设置独立边权重
    pub fn set_edge_weight(&mut self, weight: EdgeWeight) {
        let key = (weight.source_id.clone(), weight.target_id.clone());
        self.edge_weights.insert(key, weight);
    }

    /// 三级查找：独立边 → 类型共享 → 全局默认
    ///
    /// 返回 (signal_strength, TransformMatrix引用)
    pub fn resolve_weight(&self, source_id: &str, target_id: &str, relation_type: &str) -> (f64, &TransformMatrix) {
        // Level 1: 独立边权重
        let key = (source_id.to_string(), target_id.to_string());
        if let Some(ew) = self.edge_weights.get(&key) {
            if let Some(ref m) = ew.transform_matrix {
                return (ew.signal_strength, m);
            }
            // 有 signal_strength 但无独立矩阵，回退矩阵到下层
            let matrix = self.type_defaults.get(relation_type)
                .unwrap_or(&self.global_default);
            return (ew.signal_strength, matrix);
        }

        // Level 2: 类型共享矩阵
        if let Some(m) = self.type_defaults.get(relation_type) {
            return (1.0, m);
        }

        // Level 3: 全局默认
        (1.0, &self.global_default)
    }

    /// 获取只读边权重
    pub fn get_edge_weight(&self, source_id: &str, target_id: &str) -> Option<&EdgeWeight> {
        let key = (source_id.to_string(), target_id.to_string());
        self.edge_weights.get(&key)
    }

    /// 获取可变边权重（用于赫布学习等更新）
    pub fn get_edge_weight_mut(&mut self, source_id: &str, target_id: &str) -> Option<&mut EdgeWeight> {
        let key = (source_id.to_string(), target_id.to_string());
        self.edge_weights.get_mut(&key)
    }

    /// 设置独立边权重（带 source/target 参数）
    pub fn set_edge_weight_by_id(&mut self, source_id: &str, target_id: &str, weight: EdgeWeight) {
        let key = (source_id.to_string(), target_id.to_string());
        self.edge_weights.insert(key, weight);
    }

    /// 删除独立边权重
    pub fn remove_edge_weight(&mut self, source_id: &str, target_id: &str) -> Option<EdgeWeight> {
        let key = (source_id.to_string(), target_id.to_string());
        self.edge_weights.remove(&key)
    }

    /// 获取 edge_weights HashMap 的可变引用（用于批量操作）
    pub fn edge_weights_mut(&mut self) -> &mut HashMap<(String, String), EdgeWeight> {
        &mut self.edge_weights
    }

    /// 获取所有边权重（引用）
    pub fn all_edge_weights(&self) -> Vec<&EdgeWeight> {
        self.edge_weights.values().collect()
    }

    /// 获取所有边权重（克隆，用于迭代时修改）
    pub fn all_edge_weights_cloned(&self) -> Vec<EdgeWeight> {
        self.edge_weights.values().cloned().collect()
    }

    /// 边权重总数
    pub fn edge_count(&self) -> usize {
        self.edge_weights.len()
    }

    /// 记录共激活（赫布学习入口）
    ///
    /// 如果边权重不存在则自动创建
    pub fn record_co_activation(
        &mut self,
        source_id: &str,
        target_id: &str,
        relation_type: &str,
    ) {
        let key = (source_id.to_string(), target_id.to_string());
        let ew = self.edge_weights.entry(key).or_insert_with(|| {
            EdgeWeight::new(source_id, target_id, relation_type)
        });
        ew.record_co_activation();
    }

    // ========== 持久化 ==========

    /// 序列化为 JSON（用于文件/Lance 持久化）
    pub fn to_json(&self) -> String {
        let snapshot = EdgeWeightStoreSnapshot {
            global_default: self.global_default.clone(),
            type_defaults: self.type_defaults.clone(),
            edge_weights: self.edge_weights.values().cloned().collect(),
        };
        serde_json::to_string(&snapshot).unwrap_or_default()
    }

    /// 从 JSON 反序列化恢复
    pub fn from_json(json: &str, semantic_dim: usize) -> Self {
        if let Ok(snapshot) = serde_json::from_str::<EdgeWeightStoreSnapshot>(json) {
            let mut edge_weights = HashMap::new();
            for ew in snapshot.edge_weights {
                let key = (ew.source_id.clone(), ew.target_id.clone());
                edge_weights.insert(key, ew);
            }
            Self {
                global_default: snapshot.global_default,
                type_defaults: snapshot.type_defaults,
                edge_weights,
            }
        } else {
            Self::new(semantic_dim)
        }
    }

    /// 保存到文件
    pub fn save_to_file(&self, path: &std::path::Path) -> std::io::Result<()> {
        let json = self.to_json();
        std::fs::write(path, json)
    }

    /// 从文件加载
    pub fn load_from_file(path: &std::path::Path, semantic_dim: usize) -> std::io::Result<Self> {
        if path.exists() {
            let json = std::fs::read_to_string(path)?;
            Ok(Self::from_json(&json, semantic_dim))
        } else {
            Ok(Self::new(semantic_dim))
        }
    }
}

/// EdgeWeightStore 序列化快照
#[derive(Serialize, Deserialize)]
struct EdgeWeightStoreSnapshot {
    global_default: TransformMatrix,
    type_defaults: HashMap<String, TransformMatrix>,
    edge_weights: Vec<EdgeWeight>,
}

// ============================================================================
// SiFu 配置
// ============================================================================

/// SiFu 查询配置
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SiFuConfig {
    /// 最大传播步数（类似 BFS 深度）
    #[serde(default = "default_max_steps")]
    pub max_steps: usize,

    /// 能量阈值——低于此值的节点不再传播（剪枝）
    #[serde(default = "default_energy_threshold")]
    pub energy_threshold: f64,

    /// 衰减因子——每一步信号乘以此系数
    #[serde(default = "default_decay_factor")]
    pub decay_factor: f64,

    /// 激活函数类型
    #[serde(default)]
    pub activation: ActivationFn,

    /// 最大返回结果数
    #[serde(default = "default_top_k")]
    pub top_k: usize,
}

fn default_max_steps() -> usize { 3 }
fn default_energy_threshold() -> f64 { 0.01 }
fn default_decay_factor() -> f64 { 0.7 }
fn default_top_k() -> usize { 20 }

impl Default for SiFuConfig {
    fn default() -> Self {
        Self {
            max_steps: default_max_steps(),
            energy_threshold: default_energy_threshold(),
            decay_factor: default_decay_factor(),
            activation: ActivationFn::default(),
            top_k: default_top_k(),
        }
    }
}

impl SiFuConfig {
    /// 移动端优化配置
    ///
    /// 减少传播深度和激活节点数，加速剪枝，关闭路径追踪。
    /// 适用于 Android (NNAPI) / iOS (CoreML) 等资源受限环境。
    pub fn mobile() -> Self {
        Self {
            max_steps: 2,              // 减少传播深度
            energy_threshold: 0.05,    // 更高阈值（更快剪枝）
            decay_factor: 0.7,         // 更快衰减
            activation: ActivationFn::ReLU,
            top_k: 10,                // 更少结果
        }
    }

    /// 服务端高性能配置
    ///
    /// 允许更深传播和更多激活节点，适合 GPU 加速环境。
    pub fn server() -> Self {
        Self {
            max_steps: 5,
            energy_threshold: 0.005,
            decay_factor: 0.8,
            activation: ActivationFn::ReLU,
            top_k: 50,
        }
    }
}

/// 激活函数类型
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
pub enum ActivationFn {
    /// ReLU: max(0, x)
    #[default]
    ReLU,
    /// Sigmoid: 1 / (1 + exp(-x))
    Sigmoid,
    /// Tanh
    Tanh,
    /// 无激活（线性通过）
    Linear,
}

impl ActivationFn {
    /// 应用激活函数
    pub fn apply(&self, x: f64) -> f64 {
        match self {
            ActivationFn::ReLU => x.max(0.0),
            ActivationFn::Sigmoid => 1.0 / (1.0 + (-x).exp()),
            ActivationFn::Tanh => x.tanh(),
            ActivationFn::Linear => x,
        }
    }
}

// ============================================================================
// FlowTrace — 可解释性传播路径
// ============================================================================

/// 信号传播种子（查询入口）
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FlowSeed {
    /// 种子节点 ID
    pub node_id: String,
    /// 初始能量
    pub initial_energy: f64,
}

/// 信号传播中的一次跳转
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FlowHop {
    /// 源节点 ID
    pub from_id: String,
    /// 目标节点 ID
    pub to_id: String,
    /// 使用的关系类型
    pub relation_type: String,
    /// 传入能量（跳转前）
    pub energy_in: f64,
    /// 传出能量（跳转后，经过权重+激活）
    pub energy_out: f64,
    /// 使用的 signal_strength
    pub signal_strength: f64,
    /// 传播步数（第几步）
    pub step: usize,
}

/// 完整信号传播追踪
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FlowTrace {
    /// 种子节点
    pub seeds: Vec<FlowSeed>,
    /// 所有跳转记录
    pub hops: Vec<FlowHop>,
    /// 最终各节点能量
    pub final_energies: Vec<(String, f64)>,
    /// 传播步数
    pub total_steps: usize,
    /// 配置快照
    pub config: SiFuConfig,
}

// ============================================================================
// SiFu 查询结果
// ============================================================================

/// SiFu 查询结果（单个节点）
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SiFuResult {
    /// 节点 ID
    pub node_id: String,
    /// 最终能量（越高越相关）
    pub energy: f64,
    /// 到达此节点的最短路径步数
    pub steps: usize,
}

// ============================================================================
// SiFuQueryEngine — 核心信号传播引擎
// ============================================================================

use crate::storage::GraphStore;

/// SiFu 信号传播查询引擎
///
/// 执行能量扩散查询：
/// 1. 在种子节点注入初始能量
/// 2. 每一步，将能量通过图的边传播到邻居
/// 3. 传播时应用 EdgeWeight（signal_strength × TransformMatrix）
/// 4. 应用激活函数 + 阈值剪枝
/// 5. 迭代直到达到 max_steps 或所有能量低于阈值
/// 6. 返回能量最高的 top_k 节点
pub struct SiFuQueryEngine {
    /// 图存储（获取邻居关系）
    graph_store: Arc<GraphStore>,
    /// 边权重存储
    edge_weight_store: Arc<parking_lot::RwLock<EdgeWeightStore>>,
    /// 计算后端（矩阵-向量乘法加速）
    compute_backend: Arc<dyn crate::query::compute_backend::ComputeBackend>,
}

impl SiFuQueryEngine {
    /// 创建 SiFu 查询引擎（使用默认 CPU 后端）
    pub fn new(
        graph_store: Arc<GraphStore>,
        edge_weight_store: Arc<parking_lot::RwLock<EdgeWeightStore>>,
    ) -> Self {
        Self {
            graph_store,
            edge_weight_store,
            compute_backend: Arc::new(crate::query::compute_backend::CpuBackend::new()),
        }
    }

    /// 创建 SiFu 查询引擎并指定计算后端
    pub fn with_backend(
        graph_store: Arc<GraphStore>,
        edge_weight_store: Arc<parking_lot::RwLock<EdgeWeightStore>>,
        compute_backend: Arc<dyn crate::query::compute_backend::ComputeBackend>,
    ) -> Self {
        Self { graph_store, edge_weight_store, compute_backend }
    }

    /// 获取当前计算后端信息
    pub fn backend_info(&self) -> crate::query::compute_backend::ComputeBackendInfo {
        self.compute_backend.info()
    }

    /// 执行信号传播查询
    ///
    /// `seeds`: 种子节点 ID 及其初始能量
    /// `config`: 传播配置
    ///
    /// 返回按能量降序排列的节点列表
    pub fn propagate(
        &self,
        seeds: &[(String, f64)],
        config: &SiFuConfig,
    ) -> Vec<SiFuResult> {
        let (results, _) = self.propagate_with_trace(seeds, config, false);
        results
    }

    /// 执行信号传播查询并记录完整路径（可解释性）
    pub fn propagate_with_trace(
        &self,
        seeds: &[(String, f64)],
        config: &SiFuConfig,
        enable_trace: bool,
    ) -> (Vec<SiFuResult>, Option<FlowTrace>) {
        // 当前能量表
        let mut energies: HashMap<String, f64> = HashMap::new();
        // 最短到达步数
        let mut min_steps: HashMap<String, usize> = HashMap::new();
        // 跳转记录（可选）
        let mut hops: Vec<FlowHop> = Vec::new();

        // 注入种子能量
        for (node_id, energy) in seeds {
            *energies.entry(node_id.clone()).or_default() += energy;
            min_steps.insert(node_id.clone(), 0);
        }

        let ew_store = self.edge_weight_store.read();

        // 迭代传播
        for step in 0..config.max_steps {
            // 收集当前步需要传播的节点和能量
            let active_nodes: Vec<(String, f64)> = energies
                .iter()
                .filter(|(_, &e)| e >= config.energy_threshold)
                .map(|(id, &e)| (id.clone(), e))
                .collect();

            if active_nodes.is_empty() {
                break;
            }

            let mut new_energies: HashMap<String, f64> = HashMap::new();

            for (node_id, current_energy) in &active_nodes {
                // 获取该节点的所有出边关系
                let relations = self.graph_store.outgoing_from(node_id);
                // 双向关系也需要考虑入边
                let incoming = self.graph_store.incoming_to(node_id);

                // 处理出边
                for relation in &relations {
                    let target_id = &relation.target;

                    let (signal_strength, _transform) = ew_store.resolve_weight(
                        node_id,
                        target_id,
                        &relation.relation_type,
                    );

                    let raw_energy = current_energy * signal_strength * config.decay_factor;
                    let activated_energy = config.activation.apply(raw_energy);

                    if activated_energy >= config.energy_threshold {
                        *new_energies.entry(target_id.clone()).or_default() += activated_energy;
                        min_steps.entry(target_id.clone()).or_insert(step + 1);

                        if enable_trace {
                            hops.push(FlowHop {
                                from_id: node_id.clone(),
                                to_id: target_id.clone(),
                                relation_type: relation.relation_type.clone(),
                                energy_in: *current_energy,
                                energy_out: activated_energy,
                                signal_strength,
                                step: step + 1,
                            });
                        }
                    }
                }

                // 处理双向关系的入边（反向传播）
                for relation in &incoming {
                    if !relation.bidirectional {
                        continue;
                    }
                    let target_id = &relation.source;

                    let (signal_strength, _transform) = ew_store.resolve_weight(
                        node_id,
                        target_id,
                        &relation.relation_type,
                    );

                    let raw_energy = current_energy * signal_strength * config.decay_factor;
                    let activated_energy = config.activation.apply(raw_energy);

                    if activated_energy >= config.energy_threshold {
                        *new_energies.entry(target_id.clone()).or_default() += activated_energy;
                        min_steps.entry(target_id.clone()).or_insert(step + 1);

                        if enable_trace {
                            hops.push(FlowHop {
                                from_id: node_id.clone(),
                                to_id: target_id.clone(),
                                relation_type: relation.relation_type.clone(),
                                energy_in: *current_energy,
                                energy_out: activated_energy,
                                signal_strength,
                                step: step + 1,
                            });
                        }
                    }
                }
            }

            // 更新能量表（累加新能量到已有能量上）
            for (id, energy) in new_energies {
                *energies.entry(id).or_default() += energy;
            }
        }

        // 按能量降序取 top_k
        let mut results: Vec<SiFuResult> = energies
            .into_iter()
            .map(|(node_id, energy)| SiFuResult {
                steps: *min_steps.get(&node_id).unwrap_or(&0),
                node_id,
                energy,
            })
            .collect();

        results.sort_by(|a, b| b.energy.partial_cmp(&a.energy).unwrap_or(std::cmp::Ordering::Equal));
        results.truncate(config.top_k);

        let trace = if enable_trace {
            Some(FlowTrace {
                seeds: seeds.iter().map(|(id, e)| FlowSeed {
                    node_id: id.clone(),
                    initial_energy: *e,
                }).collect(),
                hops,
                final_energies: results.iter().map(|r| (r.node_id.clone(), r.energy)).collect(),
                total_steps: config.max_steps,
                config: config.clone(),
            })
        } else {
            None
        };

        (results, trace)
    }

    /// 追踪信号流（FlowTrace 可解释性接口）
    pub fn trace_flow(
        &self,
        seeds: &[(String, f64)],
        config: &SiFuConfig,
    ) -> FlowTrace {
        let (_, trace) = self.propagate_with_trace(seeds, config, true);
        trace.unwrap()
    }

    /// 动态关联推荐：从给定节点出发，发现最强关联节点
    pub fn recommend_related(
        &self,
        node_id: &str,
        config: &SiFuConfig,
    ) -> Vec<SiFuResult> {
        let seeds = vec![(node_id.to_string(), 1.0)];
        let mut results = self.propagate(&seeds, config);
        // 排除自身
        results.retain(|r| r.node_id != node_id);
        results
    }

    /// 语义聚类发现：从多个种子注入能量，汇聚效应发现语义簇
    pub fn discover_clusters(
        &self,
        seed_ids: &[&str],
        config: &SiFuConfig,
    ) -> Vec<SiFuResult> {
        let seeds: Vec<(String, f64)> = seed_ids
            .iter()
            .map(|id| (id.to_string(), 1.0))
            .collect();
        self.propagate(&seeds, config)
    }
}

// ============================================================================
// Pipeline — VectorSearch → SignalPropagation 两阶段查询
// ============================================================================

/// Pipeline 查询配置
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PipelineConfig {
    /// 阶段1：向量搜索返回的种子数量
    #[serde(default = "default_seed_count")]
    pub seed_count: usize,
    /// 种子节点的初始能量（可按相似度分数自动分配）
    #[serde(default)]
    pub energy_from_similarity: bool,
    /// 阶段2：SiFu 传播配置
    #[serde(default)]
    pub sifu_config: SiFuConfig,
}

fn default_seed_count() -> usize { 5 }

impl Default for PipelineConfig {
    fn default() -> Self {
        Self {
            seed_count: default_seed_count(),
            energy_from_similarity: true,
            sifu_config: SiFuConfig::default(),
        }
    }
}

/// Pipeline 查询结果
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PipelineResult {
    /// 最终排序结果
    pub results: Vec<SiFuResult>,
    /// 阶段1：向量搜索种子
    pub seeds: Vec<(String, f64)>,
    /// 可选 FlowTrace
    pub trace: Option<FlowTrace>,
}

impl SiFuQueryEngine {
    /// 两阶段 Pipeline 查询：
    ///
    /// **阶段1**: 使用向量相似度搜索发现种子节点
    /// **阶段2**: 从种子节点出发进行 SiFu 信号传播扩散
    ///
    /// `vector_results`: 向量搜索返回的 (node_id, similarity_score) 列表
    /// `config`: Pipeline 配置
    pub fn query_pipeline(
        &self,
        vector_results: &[(String, f64)],
        config: &PipelineConfig,
    ) -> PipelineResult {
        self.query_pipeline_inner(vector_results, config, false)
    }

    /// Pipeline 查询 + FlowTrace
    pub fn query_pipeline_with_trace(
        &self,
        vector_results: &[(String, f64)],
        config: &PipelineConfig,
    ) -> PipelineResult {
        self.query_pipeline_inner(vector_results, config, true)
    }

    fn query_pipeline_inner(
        &self,
        vector_results: &[(String, f64)],
        config: &PipelineConfig,
        enable_trace: bool,
    ) -> PipelineResult {
        // 阶段1：选择种子节点
        let seeds: Vec<(String, f64)> = vector_results
            .iter()
            .take(config.seed_count)
            .map(|(id, score)| {
                let energy = if config.energy_from_similarity {
                    // 将相似度分数 (0~1) 映射为初始能量
                    score.clamp(0.0, 1.0)
                } else {
                    1.0 // 均匀能量
                };
                (id.clone(), energy)
            })
            .collect();

        if seeds.is_empty() {
            return PipelineResult {
                results: Vec::new(),
                seeds: Vec::new(),
                trace: None,
            };
        }

        // 阶段2：SiFu 传播
        let (results, trace) = self.propagate_with_trace(&seeds, &config.sifu_config, enable_trace);

        PipelineResult {
            results,
            seeds,
            trace,
        }
    }
}

// ============================================================================
// Helpers
// ============================================================================

fn chrono_now_ms() -> i64 {
    use std::time::{SystemTime, UNIX_EPOCH};
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_millis() as i64
}

// ============================================================================
// Tests
// ============================================================================

#[cfg(test)]
mod tests {
    use super::*;

    // ---- TransformMatrix tests ----

    #[test]
    fn test_identity_matrix() {
        let m = TransformMatrix::identity(3);
        assert_eq!(m.rows, 3);
        assert_eq!(m.cols, 3);
        let v = m.mul_vec(&[1.0, 2.0, 3.0]);
        assert_eq!(v, vec![1.0, 2.0, 3.0]);
    }

    #[test]
    fn test_scale_matrix() {
        let m = TransformMatrix::scale(3, 0.5);
        let v = m.mul_vec(&[2.0, 4.0, 6.0]);
        assert_eq!(v, vec![1.0, 2.0, 3.0]);
    }

    #[test]
    fn test_matrix_mul_mat() {
        let a = TransformMatrix::from_rows(&[
            vec![1.0, 2.0],
            vec![3.0, 4.0],
        ]);
        let b = TransformMatrix::from_rows(&[
            vec![5.0, 6.0],
            vec![7.0, 8.0],
        ]);
        let c = a.mul_mat(&b);
        assert_eq!(c.data, vec![19.0, 22.0, 43.0, 50.0]);
    }

    #[test]
    fn test_frobenius_norm() {
        let m = TransformMatrix::from_rows(&[
            vec![3.0, 0.0],
            vec![0.0, 4.0],
        ]);
        assert!((m.frobenius_norm() - 5.0).abs() < 1e-5);
    }

    // ---- EdgeWeight tests ----

    #[test]
    fn test_edge_weight_creation() {
        let ew = EdgeWeight::new("a", "b", "relates_to")
            .with_signal_strength(0.8);
        assert_eq!(ew.source_id, "a");
        assert_eq!(ew.target_id, "b");
        assert_eq!(ew.signal_strength, 0.8);
        assert_eq!(ew.co_activation_count, 0);
    }

    #[test]
    fn test_co_activation() {
        let mut ew = EdgeWeight::new("a", "b", "relates_to");
        ew.record_co_activation();
        ew.record_co_activation();
        assert_eq!(ew.co_activation_count, 2);
    }

    // ---- EdgeWeightStore tests ----

    #[test]
    fn test_resolve_weight_global_default() {
        let store = EdgeWeightStore::new(3);
        let (strength, matrix) = store.resolve_weight("x", "y", "unknown_type");
        assert_eq!(strength, 1.0);
        assert_eq!(matrix, &TransformMatrix::identity(3));
    }

    #[test]
    fn test_resolve_weight_type_default() {
        let mut store = EdgeWeightStore::new(3);
        let m = TransformMatrix::scale(3, 0.5);
        store.set_type_default("similar_to", m.clone());

        let (strength, matrix) = store.resolve_weight("x", "y", "similar_to");
        assert_eq!(strength, 1.0);
        assert_eq!(matrix, &m);
    }

    #[test]
    fn test_resolve_weight_edge_override() {
        let mut store = EdgeWeightStore::new(3);
        store.set_type_default("similar_to", TransformMatrix::scale(3, 0.5));

        let m_edge = TransformMatrix::scale(3, 2.0);
        let ew = EdgeWeight::new("a", "b", "similar_to")
            .with_signal_strength(0.3)
            .with_transform(m_edge.clone());
        store.set_edge_weight(ew);

        let (strength, matrix) = store.resolve_weight("a", "b", "similar_to");
        assert_eq!(strength, 0.3);
        assert_eq!(matrix, &m_edge);
    }

    // ---- ActivationFn tests ----

    #[test]
    fn test_activation_functions() {
        assert_eq!(ActivationFn::ReLU.apply(-1.0), 0.0);
        assert_eq!(ActivationFn::ReLU.apply(0.5), 0.5);
        assert!((ActivationFn::Sigmoid.apply(0.0) - 0.5).abs() < 1e-6);
        assert!((ActivationFn::Tanh.apply(0.0)).abs() < 1e-6);
        assert_eq!(ActivationFn::Linear.apply(42.0), 42.0);
    }

    // ---- SiFuQueryEngine integration test ----

    #[test]
    fn test_sifu_propagation_basic() {
        use crate::types::{Entity, Relation};
        use tempfile::tempdir;

        // Setup graph: A -> B -> C, A -> D
        let dir = tempdir().unwrap();
        let graph = Arc::new(GraphStore::open_or_create(dir.path().join("graph")).unwrap());

        let a = Entity::new("Node A", "concept").with_id("a");
        let b = Entity::new("Node B", "concept").with_id("b");
        let c = Entity::new("Node C", "concept").with_id("c");
        let d = Entity::new("Node D", "concept").with_id("d");

        graph.put_entity(a);
        graph.put_entity(b);
        graph.put_entity(c);
        graph.put_entity(d);

        graph.put_relation(Relation::new("a", "b", "relates_to"));
        graph.put_relation(Relation::new("b", "c", "relates_to"));
        graph.put_relation(Relation::new("a", "d", "relates_to"));

        let ew_store = Arc::new(parking_lot::RwLock::new(EdgeWeightStore::new(3)));
        let engine = SiFuQueryEngine::new(graph, ew_store);

        let config = SiFuConfig {
            max_steps: 3,
            energy_threshold: 0.01,
            decay_factor: 0.7,
            activation: ActivationFn::ReLU,
            top_k: 10,
        };

        let results = engine.propagate(&[("a".to_string(), 1.0)], &config);

        // A 应该有最高能量（初始注入），B 和 D 次之，C 最低
        assert!(!results.is_empty(), "Should have results");

        // Find specific nodes
        let a_result = results.iter().find(|r| r.node_id == "a");
        let b_result = results.iter().find(|r| r.node_id == "b");
        let d_result = results.iter().find(|r| r.node_id == "d");

        assert!(a_result.is_some(), "Node A should be in results");
        assert!(b_result.is_some(), "Node B should be in results");
        assert!(d_result.is_some(), "Node D should be in results");

        // B and D should have similar energy (both 1-hop from A)
        let b_energy = b_result.unwrap().energy;
        let d_energy = d_result.unwrap().energy;
        assert!((b_energy - d_energy).abs() < 0.001, "B and D should have equal energy");
    }

    #[test]
    fn test_sifu_trace_flow() {
        use crate::types::{Entity, Relation};
        use tempfile::tempdir;

        let dir = tempdir().unwrap();
        let graph = Arc::new(GraphStore::open_or_create(dir.path().join("graph")).unwrap());

        graph.put_entity(Entity::new("A", "concept").with_id("a"));
        graph.put_entity(Entity::new("B", "concept").with_id("b"));
        graph.put_relation(Relation::new("a", "b", "relates_to"));

        let ew_store = Arc::new(parking_lot::RwLock::new(EdgeWeightStore::new(3)));
        let engine = SiFuQueryEngine::new(graph, ew_store);

        let config = SiFuConfig::default();
        let trace = engine.trace_flow(&[("a".to_string(), 1.0)], &config);

        assert_eq!(trace.seeds.len(), 1);
        assert_eq!(trace.seeds[0].node_id, "a");
        assert!(!trace.hops.is_empty(), "Should have at least one hop");
        assert_eq!(trace.hops[0].from_id, "a");
        assert_eq!(trace.hops[0].to_id, "b");
    }

    #[test]
    fn test_sifu_recommend_related() {
        use crate::types::{Entity, Relation};
        use tempfile::tempdir;

        let dir = tempdir().unwrap();
        let graph = Arc::new(GraphStore::open_or_create(dir.path().join("graph")).unwrap());

        graph.put_entity(Entity::new("Center", "concept").with_id("center"));
        graph.put_entity(Entity::new("Near", "concept").with_id("near"));
        graph.put_entity(Entity::new("Far", "concept").with_id("far"));

        graph.put_relation(Relation::new("center", "near", "relates_to").with_weight(0.9));
        graph.put_relation(Relation::new("near", "far", "relates_to").with_weight(0.5));

        let ew_store = Arc::new(parking_lot::RwLock::new(EdgeWeightStore::new(3)));
        let engine = SiFuQueryEngine::new(graph, ew_store);

        let results = engine.recommend_related("center", &SiFuConfig::default());
        assert!(results.iter().all(|r| r.node_id != "center"), "Should exclude self");
        assert!(!results.is_empty());
    }

    #[test]
    fn test_edge_weight_store_co_activation() {
        let mut store = EdgeWeightStore::new(3);
        store.record_co_activation("x", "y", "relates_to");
        store.record_co_activation("x", "y", "relates_to");

        let ew = store.get_edge_weight_mut("x", "y").unwrap();
        assert_eq!(ew.co_activation_count, 2);
    }

    // ---- EdgeWeightStore 持久化 tests ----

    #[test]
    fn test_edge_weight_store_json_roundtrip() {
        let mut store = EdgeWeightStore::new(3);
        store.set_type_default("similar_to", TransformMatrix::scale(3, 0.5));

        let ew = EdgeWeight::new("a", "b", "relates_to")
            .with_signal_strength(0.7)
            .with_transform(TransformMatrix::scale(3, 2.0));
        store.set_edge_weight(ew);

        let json = store.to_json();
        let restored = EdgeWeightStore::from_json(&json, 3);

        // 验证全局默认
        let (s, m) = restored.resolve_weight("x", "y", "unknown");
        assert_eq!(s, 1.0);
        assert_eq!(m.rows, 3);

        // 验证类型默认
        let (s, m) = restored.resolve_weight("x", "y", "similar_to");
        assert_eq!(s, 1.0);
        assert!((m.get(0, 0) - 0.5).abs() < 1e-5);

        // 验证独立边
        let (s, m) = restored.resolve_weight("a", "b", "relates_to");
        assert!((s - 0.7).abs() < 1e-5);
        assert!((m.get(0, 0) - 2.0).abs() < 1e-5);
    }

    #[test]
    fn test_edge_weight_store_file_persistence() {
        use tempfile::tempdir;

        let dir = tempdir().unwrap();
        let path = dir.path().join("edge_weights.json");

        let mut store = EdgeWeightStore::new(3);
        store.record_co_activation("x", "y", "test");
        store.record_co_activation("x", "y", "test");

        store.save_to_file(&path).unwrap();

        let loaded = EdgeWeightStore::load_from_file(&path, 3).unwrap();
        assert_eq!(loaded.edge_count(), 1);

        let (s, _) = loaded.resolve_weight("x", "y", "test");
        assert_eq!(s, 1.0); // default signal_strength
    }

    // ---- Pipeline tests ----

    #[test]
    fn test_pipeline_basic() {
        use crate::types::{Entity, Relation};
        use tempfile::tempdir;

        let dir = tempdir().unwrap();
        let graph = Arc::new(GraphStore::open_or_create(dir.path().join("graph")).unwrap());

        // Build a small graph: A -> B -> C
        graph.put_entity(Entity::new("A", "concept").with_id("a"));
        graph.put_entity(Entity::new("B", "concept").with_id("b"));
        graph.put_entity(Entity::new("C", "concept").with_id("c"));
        graph.put_relation(Relation::new("a", "b", "relates_to"));
        graph.put_relation(Relation::new("b", "c", "relates_to"));

        let ew_store = Arc::new(parking_lot::RwLock::new(EdgeWeightStore::new(3)));
        let engine = SiFuQueryEngine::new(graph, ew_store);

        // 模拟向量搜索结果：A 和 B 是种子
        let vector_results = vec![
            ("a".to_string(), 0.95),
            ("b".to_string(), 0.80),
        ];

        let config = PipelineConfig {
            seed_count: 2,
            energy_from_similarity: true,
            sifu_config: SiFuConfig {
                max_steps: 2,
                energy_threshold: 0.01,
                decay_factor: 0.7,
                activation: ActivationFn::ReLU,
                top_k: 10,
            },
        };

        let result = engine.query_pipeline(&vector_results, &config);

        assert_eq!(result.seeds.len(), 2);
        assert!(!result.results.is_empty());

        // C 应该通过传播被发现
        let c_found = result.results.iter().any(|r| r.node_id == "c");
        assert!(c_found, "Node C should be discovered via propagation");
    }

    #[test]
    fn test_pipeline_with_trace() {
        use crate::types::{Entity, Relation};
        use tempfile::tempdir;

        let dir = tempdir().unwrap();
        let graph = Arc::new(GraphStore::open_or_create(dir.path().join("graph")).unwrap());

        graph.put_entity(Entity::new("X", "concept").with_id("x"));
        graph.put_entity(Entity::new("Y", "concept").with_id("y"));
        graph.put_relation(Relation::new("x", "y", "relates_to"));

        let ew_store = Arc::new(parking_lot::RwLock::new(EdgeWeightStore::new(3)));
        let engine = SiFuQueryEngine::new(graph, ew_store);

        let vector_results = vec![("x".to_string(), 1.0)];
        let config = PipelineConfig::default();

        let result = engine.query_pipeline_with_trace(&vector_results, &config);

        assert!(result.trace.is_some(), "Should have FlowTrace");
        let trace = result.trace.unwrap();
        assert!(!trace.hops.is_empty());
    }

    #[test]
    fn test_pipeline_empty_seeds() {
        use crate::types::Entity;
        use tempfile::tempdir;

        let dir = tempdir().unwrap();
        let graph = Arc::new(GraphStore::open_or_create(dir.path().join("graph")).unwrap());
        graph.put_entity(Entity::new("Solo", "concept").with_id("solo"));

        let ew_store = Arc::new(parking_lot::RwLock::new(EdgeWeightStore::new(3)));
        let engine = SiFuQueryEngine::new(graph, ew_store);

        let result = engine.query_pipeline(&[], &PipelineConfig::default());
        assert!(result.results.is_empty());
        assert!(result.seeds.is_empty());
    }

    #[test]
    fn test_sifu_with_edge_weights() {
        use crate::types::{Entity, Relation};
        use tempfile::tempdir;

        let dir = tempdir().unwrap();
        let graph = Arc::new(GraphStore::open_or_create(dir.path().join("graph")).unwrap());

        // A -> B (strong), A -> C (weak)
        graph.put_entity(Entity::new("A", "concept").with_id("a"));
        graph.put_entity(Entity::new("B", "concept").with_id("b"));
        graph.put_entity(Entity::new("C", "concept").with_id("c"));
        graph.put_relation(Relation::new("a", "b", "strong_link"));
        graph.put_relation(Relation::new("a", "c", "weak_link"));

        let mut ew = EdgeWeightStore::new(3);
        ew.set_edge_weight(
            EdgeWeight::new("a", "b", "strong_link").with_signal_strength(0.9),
        );
        ew.set_edge_weight(
            EdgeWeight::new("a", "c", "weak_link").with_signal_strength(0.1),
        );

        let ew_store = Arc::new(parking_lot::RwLock::new(ew));
        let engine = SiFuQueryEngine::new(graph, ew_store);

        let config = SiFuConfig {
            max_steps: 1,
            energy_threshold: 0.001,
            decay_factor: 1.0, // no decay to see pure weight effect
            activation: ActivationFn::Linear,
            top_k: 10,
        };

        let results = engine.propagate(&[("a".to_string(), 1.0)], &config);

        let b_energy = results.iter().find(|r| r.node_id == "b").map(|r| r.energy).unwrap_or(0.0);
        let c_energy = results.iter().find(|r| r.node_id == "c").map(|r| r.energy).unwrap_or(0.0);

        // B 应该获得比 C 多得多的能量（0.9 vs 0.1）
        assert!(b_energy > c_energy * 5.0, "B ({}) should have much more energy than C ({})", b_energy, c_energy);
    }

    #[test]
    fn test_discover_clusters_convergence() {
        use crate::types::{Entity, Relation};
        use tempfile::tempdir;

        let dir = tempdir().unwrap();
        let graph = Arc::new(GraphStore::open_or_create(dir.path().join("graph")).unwrap());

        // Two clusters: {A,B,C} and {D,E,F} connected by weak link B->D
        for id in &["a", "b", "c", "d", "e", "f"] {
            graph.put_entity(Entity::new(*id, "concept").with_id(*id));
        }
        // Cluster 1
        graph.put_relation(Relation::new("a", "b", "relates_to"));
        graph.put_relation(Relation::new("b", "c", "relates_to"));
        graph.put_relation(Relation::new("c", "a", "relates_to"));
        // Cluster 2
        graph.put_relation(Relation::new("d", "e", "relates_to"));
        graph.put_relation(Relation::new("e", "f", "relates_to"));
        graph.put_relation(Relation::new("f", "d", "relates_to"));
        // Weak bridge
        graph.put_relation(Relation::new("b", "d", "bridge"));

        let mut ew = EdgeWeightStore::new(3);
        ew.set_type_default("relates_to", TransformMatrix::scale(3, 1.0));
        ew.set_type_default("bridge", TransformMatrix::scale(3, 0.3));
        ew.set_edge_weight(
            EdgeWeight::new("b", "d", "bridge").with_signal_strength(0.2),
        );

        let ew_store = Arc::new(parking_lot::RwLock::new(ew));
        let engine = SiFuQueryEngine::new(graph, ew_store);

        // 从 cluster 1 的 a 出发
        let config = SiFuConfig {
            max_steps: 4,
            energy_threshold: 0.001,
            decay_factor: 0.7,
            activation: ActivationFn::ReLU,
            top_k: 10,
        };

        let results = engine.propagate(&[("a".to_string(), 1.0)], &config);

        // Cluster 1 nodes 应该有较高能量
        let cluster1_energy: f64 = results.iter()
            .filter(|r| ["a", "b", "c"].contains(&r.node_id.as_str()))
            .map(|r| r.energy)
            .sum();
        let cluster2_energy: f64 = results.iter()
            .filter(|r| ["d", "e", "f"].contains(&r.node_id.as_str()))
            .map(|r| r.energy)
            .sum();

        assert!(cluster1_energy > cluster2_energy,
            "Cluster 1 ({}) should have more energy than cluster 2 ({})",
            cluster1_energy, cluster2_energy);
    }

    // ================================================================
    // T49.8 — 端到端集成测试 + 传播正确性验证
    // ================================================================

    /// 验证能量守恒（衰减后）：每一步总能量单调递减
    #[test]
    fn test_energy_decay_monotonic() {
        use crate::types::{Entity, Relation};
        use tempfile::tempdir;

        let dir = tempdir().unwrap();
        let graph = Arc::new(GraphStore::open_or_create(dir.path().join("graph")).unwrap());

        // 线性链：A -> B -> C -> D -> E
        for id in &["a", "b", "c", "d", "e"] {
            graph.put_entity(Entity::new(*id, "concept").with_id(*id));
        }
        graph.put_relation(Relation::new("a", "b", "relates_to"));
        graph.put_relation(Relation::new("b", "c", "relates_to"));
        graph.put_relation(Relation::new("c", "d", "relates_to"));
        graph.put_relation(Relation::new("d", "e", "relates_to"));

        let ew_store = Arc::new(parking_lot::RwLock::new(EdgeWeightStore::new(3)));
        let engine = SiFuQueryEngine::new(graph, ew_store);

        let config = SiFuConfig {
            max_steps: 5,
            energy_threshold: 0.001,
            decay_factor: 0.5,
            activation: ActivationFn::Linear,
            top_k: 10,
        };

        let trace = engine.trace_flow(&[("a".to_string(), 1.0)], &config);

        // 验证每一步的 hop energy_out <= energy_in
        for hop in &trace.hops {
            assert!(hop.energy_out <= hop.energy_in + 0.001,
                "Hop {}->{}: energy_out ({}) should <= energy_in ({})",
                hop.from_id, hop.to_id, hop.energy_out, hop.energy_in);
        }

        // 验证沿链传播时能量逐跳衰减
        let b_energy = trace.final_energies.iter()
            .find(|(id, _)| id == "b").map(|(_, e)| *e).unwrap_or(0.0);
        let c_energy = trace.final_energies.iter()
            .find(|(id, _)| id == "c").map(|(_, e)| *e).unwrap_or(0.0);
        let d_energy = trace.final_energies.iter()
            .find(|(id, _)| id == "d").map(|(_, e)| *e).unwrap_or(0.0);

        // B 直接从 A 获取能量，C 从 B，D 从 C → B > C > D
        assert!(b_energy >= c_energy, "B ({}) >= C ({})", b_energy, c_energy);
        assert!(c_energy >= d_energy, "C ({}) >= D ({})", c_energy, d_energy);
    }

    /// 验证 FlowTrace 路径可追溯性
    #[test]
    fn test_flow_trace_path_integrity() {
        use crate::types::{Entity, Relation};
        use tempfile::tempdir;

        let dir = tempdir().unwrap();
        let graph = Arc::new(GraphStore::open_or_create(dir.path().join("graph")).unwrap());

        // 三角形：A -> B, A -> C, B -> C
        graph.put_entity(Entity::new("A", "concept").with_id("a"));
        graph.put_entity(Entity::new("B", "concept").with_id("b"));
        graph.put_entity(Entity::new("C", "concept").with_id("c"));
        graph.put_relation(Relation::new("a", "b", "relates_to"));
        graph.put_relation(Relation::new("a", "c", "relates_to"));
        graph.put_relation(Relation::new("b", "c", "relates_to"));

        let ew_store = Arc::new(parking_lot::RwLock::new(EdgeWeightStore::new(3)));
        let engine = SiFuQueryEngine::new(graph, ew_store);

        let config = SiFuConfig {
            max_steps: 2,
            energy_threshold: 0.01,
            decay_factor: 0.7,
            activation: ActivationFn::ReLU,
            top_k: 10,
        };

        let trace = engine.trace_flow(&[("a".to_string(), 1.0)], &config);

        // 验证种子记录
        assert_eq!(trace.seeds.len(), 1);
        assert_eq!(trace.seeds[0].node_id, "a");
        assert_eq!(trace.seeds[0].initial_energy, 1.0);

        // 验证所有 hop 都有有效的 from/to
        let valid_ids: Vec<&str> = vec!["a", "b", "c"];
        for hop in &trace.hops {
            assert!(valid_ids.contains(&hop.from_id.as_str()),
                "Hop from_id '{}' should be a valid node", hop.from_id);
            assert!(valid_ids.contains(&hop.to_id.as_str()),
                "Hop to_id '{}' should be a valid node", hop.to_id);
            assert!(hop.step >= 1, "Step should be >= 1");
            assert!(hop.energy_out > 0.0, "Energy out should be positive");
            assert!(hop.signal_strength > 0.0, "Signal strength should be positive");
        }

        // C 应该通过两条路径获得能量（A->C 和 A->B->C）
        let c_hops: Vec<&FlowHop> = trace.hops.iter()
            .filter(|h| h.to_id == "c")
            .collect();
        assert!(c_hops.len() >= 1, "C should receive at least 1 hop");
    }

    /// 验证双向关系正确传播
    #[test]
    fn test_bidirectional_propagation() {
        use crate::types::{Entity, Relation};
        use tempfile::tempdir;

        let dir = tempdir().unwrap();
        let graph = Arc::new(GraphStore::open_or_create(dir.path().join("graph")).unwrap());

        graph.put_entity(Entity::new("X", "concept").with_id("x"));
        graph.put_entity(Entity::new("Y", "concept").with_id("y"));
        // X -> Y 双向关系
        graph.put_relation(Relation::new("x", "y", "relates_to").bidirectional());

        let ew_store = Arc::new(parking_lot::RwLock::new(EdgeWeightStore::new(3)));
        let engine = SiFuQueryEngine::new(graph, ew_store);

        let config = SiFuConfig {
            max_steps: 2,
            energy_threshold: 0.001,
            decay_factor: 0.8,
            activation: ActivationFn::ReLU,
            top_k: 10,
        };

        // 从 Y 出发（Y 是 target，但关系是双向的）
        let results = engine.propagate(&[("y".to_string(), 1.0)], &config);

        // X 应该通过双向关系获得能量
        let x_found = results.iter().any(|r| r.node_id == "x" && r.energy > 0.0);
        assert!(x_found, "X should receive energy via bidirectional relation");
    }

    /// 验证 sigmoid 激活函数限制能量上界
    #[test]
    fn test_sigmoid_activation_bounds() {
        use crate::types::{Entity, Relation};
        use tempfile::tempdir;

        let dir = tempdir().unwrap();
        let graph = Arc::new(GraphStore::open_or_create(dir.path().join("graph")).unwrap());

        // 密集连接：所有节点互联
        for id in &["a", "b", "c", "d"] {
            graph.put_entity(Entity::new(*id, "concept").with_id(*id));
        }
        for &src in &["a", "b", "c", "d"] {
            for &tgt in &["a", "b", "c", "d"] {
                if src != tgt {
                    graph.put_relation(Relation::new(src, tgt, "relates_to"));
                }
            }
        }

        let ew_store = Arc::new(parking_lot::RwLock::new(EdgeWeightStore::new(3)));
        let engine = SiFuQueryEngine::new(graph, ew_store);

        let config = SiFuConfig {
            max_steps: 5,
            energy_threshold: 0.001,
            decay_factor: 1.0, // 无衰减
            activation: ActivationFn::Sigmoid,
            top_k: 10,
        };

        let results = engine.propagate(&[("a".to_string(), 1.0)], &config);

        // Sigmoid 输出在 (0, 1) 范围内，但能量会累加
        // 验证所有中间 hop 的 energy_out 都在合理范围
        let trace = engine.trace_flow(&[("a".to_string(), 1.0)], &config);
        for hop in &trace.hops {
            // 每次 sigmoid 应用后的单次输出不超过 1.0
            assert!(hop.energy_out <= 1.0 + 0.001,
                "Sigmoid hop energy_out ({}) should be <= 1.0", hop.energy_out);
        }
    }

    /// 端到端集成：HybridQueryEngine + SiFu 三路融合
    #[test]
    fn test_hybrid_engine_with_sifu_integration() {
        use crate::types::{MemoryNode, Entity, Relation, HNSWConfig};
        use crate::vector::HNSWIndex;
        use crate::storage::DocumentStore;
        use crate::query::hybrid::{HybridQuery, HybridQueryEngine};
        use tempfile::tempdir;

        let dir = tempdir().unwrap();

        // 1. 创建 HNSW 索引和文档存储
        let hnsw_config = HNSWConfig::new(4)
            .with_m(4)
            .with_ef_construction(16)
            .with_ef_search(16)
            .with_max_elements(100);
        let hnsw = Arc::new(HNSWIndex::new(hnsw_config));
        let doc_store = Arc::new(
            DocumentStore::open_or_create(dir.path().join("docs.jsonl")).unwrap()
        );

        // 2. 创建图存储和 SiFu 引擎
        let graph = Arc::new(GraphStore::open_or_create(dir.path().join("graph")).unwrap());

        // 3. 添加文档节点
        let nodes = vec![
            MemoryNode::new("Rust programming language")
                .with_id("rust")
                .with_embedding(vec![1.0, 0.0, 0.0, 0.0])
                .with_tag("programming"),
            MemoryNode::new("Cargo build system")
                .with_id("cargo")
                .with_embedding(vec![0.9, 0.1, 0.0, 0.0])
                .with_tag("programming"),
            MemoryNode::new("Lance storage format")
                .with_id("lance")
                .with_embedding(vec![0.0, 1.0, 0.0, 0.0])
                .with_tag("storage"),
            MemoryNode::new("Arrow columnar format")
                .with_id("arrow")
                .with_embedding(vec![0.0, 0.9, 0.1, 0.0])
                .with_tag("storage"),
        ];

        for node in &nodes {
            doc_store.put(node.clone()).unwrap();
            if let Some(ref emb) = node.embedding {
                let _ = hnsw.insert(node.id.clone(), emb.clone());
            }
        }

        // 4. 建立图关系：rust -> cargo, lance -> arrow, cargo -> lance (桥接)
        graph.put_entity(Entity::new("Rust", "concept").with_id("rust"));
        graph.put_entity(Entity::new("Cargo", "concept").with_id("cargo"));
        graph.put_entity(Entity::new("Lance", "concept").with_id("lance"));
        graph.put_entity(Entity::new("Arrow", "concept").with_id("arrow"));

        graph.put_relation(Relation::new("rust", "cargo", "uses"));
        graph.put_relation(Relation::new("lance", "arrow", "based_on"));
        graph.put_relation(Relation::new("cargo", "lance", "builds_with")); // 桥接

        // 5. 创建 SiFu 引擎
        let ew_store = Arc::new(parking_lot::RwLock::new(EdgeWeightStore::new(3)));
        let sifu = Arc::new(SiFuQueryEngine::new(graph.clone(), ew_store));

        // 6. 创建三路融合 HybridQueryEngine
        let engine = HybridQueryEngine::new(hnsw.clone(), doc_store.clone())
            .with_graph_store(graph)
            .with_sifu_engine(sifu);

        // 7. 执行三路查询：向量搜索 "rust" 附近 + SiFu 传播
        // SiFu 权重设低，确保向量搜索主导排名
        let query = HybridQuery::vector(vec![1.0, 0.0, 0.0, 0.0])
            .with_sifu(SiFuConfig {
                max_steps: 2,
                energy_threshold: 0.01,
                decay_factor: 0.5,
                activation: ActivationFn::ReLU,
                top_k: 10,
            }, 0.1) // 低权重：SiFu 只提供微调
            .limit(10);

        let results = engine.query(&query);

        assert!(!results.is_empty(), "Should have results");

        // rust 和 cargo 应该都在结果中
        let rust_result = results.iter().find(|r| r.node.id == "rust");
        let cargo_result = results.iter().find(|r| r.node.id == "cargo");
        assert!(rust_result.is_some(), "Rust should be in results");
        assert!(cargo_result.is_some(), "Cargo should be in results");

        // 验证 SiFu 分数被正确填充
        let has_sifu_score = results.iter().any(|r| r.score_breakdown.sifu_score.is_some());
        assert!(has_sifu_score, "At least one result should have SiFu score");

        // 前两个结果应该是 rust 和 cargo（向量距离最近的两个）
        let top_ids: Vec<&str> = results.iter().take(2).map(|r| r.node.id.as_str()).collect();
        assert!(top_ids.contains(&"rust"), "Rust should be in top 2, got: {:?}", top_ids);
        assert!(top_ids.contains(&"cargo"), "Cargo should be in top 2, got: {:?}", top_ids);
    }

    /// 验证 TransformMatrix 对信号方向的影响
    #[test]
    fn test_transform_matrix_signal_direction() {
        let m = TransformMatrix::from_rows(&[
            vec![2.0, 0.0, 0.0],
            vec![0.0, 0.5, 0.0],
            vec![0.0, 0.0, 1.0],
        ]);

        // 输入信号 [1.0, 1.0, 1.0]
        let output = m.mul_vec(&[1.0, 1.0, 1.0]);

        // 第一维度放大2倍，第二维度缩小一半，第三维度不变
        assert!((output[0] - 2.0).abs() < 1e-5);
        assert!((output[1] - 0.5).abs() < 1e-5);
        assert!((output[2] - 1.0).abs() < 1e-5);
    }

    /// 验证能量阈值剪枝：单步传播下远端节点不可达
    #[test]
    fn test_energy_threshold_pruning() {
        use crate::types::{Entity, Relation};
        use tempfile::tempdir;

        let dir = tempdir().unwrap();
        let graph = Arc::new(GraphStore::open_or_create(dir.path().join("graph")).unwrap());

        // 长链：A -> B -> C -> D -> E -> F
        for id in &["a", "b", "c", "d", "e", "f"] {
            graph.put_entity(Entity::new(*id, "concept").with_id(*id));
        }
        graph.put_relation(Relation::new("a", "b", "r"));
        graph.put_relation(Relation::new("b", "c", "r"));
        graph.put_relation(Relation::new("c", "d", "r"));
        graph.put_relation(Relation::new("d", "e", "r"));
        graph.put_relation(Relation::new("e", "f", "r"));

        let ew_store = Arc::new(parking_lot::RwLock::new(EdgeWeightStore::new(3)));
        let engine = SiFuQueryEngine::new(graph, ew_store);

        // 高阈值 + 高衰减 + 仅1步：只有直接邻居能被激活
        // Step 0: A=1.0 active → A->B: 1.0*1.0*0.5=0.5 >= 0.3 ✓
        // 只传播1步，所以 C/D/E/F 均不可达
        let config = SiFuConfig {
            max_steps: 1,
            energy_threshold: 0.3,
            decay_factor: 0.5,
            activation: ActivationFn::Linear,
            top_k: 10,
        };

        let results = engine.propagate(&[("a".to_string(), 1.0)], &config);

        // B 应该有能量
        let b_found = results.iter().any(|r| r.node_id == "b" && r.energy > 0.0);
        assert!(b_found, "B should receive energy from A");

        // C 不应该有能量（max_steps=1 只走了一跳）
        let c_found = results.iter().any(|r| r.node_id == "c" && r.energy > 0.0);
        assert!(!c_found, "C should not be reached with max_steps=1");

        // D/E/F 更不可达
        let d_found = results.iter().any(|r| r.node_id == "d" && r.energy > 0.0);
        let e_found = results.iter().any(|r| r.node_id == "e" && r.energy > 0.0);
        let f_found = results.iter().any(|r| r.node_id == "f" && r.energy > 0.0);
        assert!(!d_found, "D should not be reached");
        assert!(!e_found, "E should not be reached");
        assert!(!f_found, "F should not be reached");

        // 多步传播下验证能量衰减导致远端节点能量低
        let config_multi = SiFuConfig {
            max_steps: 5,
            energy_threshold: 0.01,
            decay_factor: 0.3,
            activation: ActivationFn::Linear,
            top_k: 10,
        };

        let results_multi = engine.propagate(&[("a".to_string(), 1.0)], &config_multi);

        // B 能量应该最高（离 A 最近）
        let b_energy = results_multi.iter().find(|r| r.node_id == "b").map(|r| r.energy).unwrap_or(0.0);
        let f_energy = results_multi.iter().find(|r| r.node_id == "f").map(|r| r.energy).unwrap_or(0.0);

        assert!(b_energy > f_energy,
            "B ({}) should have more energy than F ({})", b_energy, f_energy);
    }

    /// 验证多种子注入的能量汇聚
    #[test]
    fn test_multi_seed_energy_convergence() {
        use crate::types::{Entity, Relation};
        use tempfile::tempdir;

        let dir = tempdir().unwrap();
        let graph = Arc::new(GraphStore::open_or_create(dir.path().join("graph")).unwrap());

        // 星形图：A->Center, B->Center, C->Center
        for id in &["a", "b", "c", "center"] {
            graph.put_entity(Entity::new(*id, "concept").with_id(*id));
        }
        graph.put_relation(Relation::new("a", "center", "relates_to"));
        graph.put_relation(Relation::new("b", "center", "relates_to"));
        graph.put_relation(Relation::new("c", "center", "relates_to"));

        let ew_store = Arc::new(parking_lot::RwLock::new(EdgeWeightStore::new(3)));
        let engine = SiFuQueryEngine::new(graph, ew_store);

        let config = SiFuConfig {
            max_steps: 1,
            energy_threshold: 0.01,
            decay_factor: 1.0,
            activation: ActivationFn::Linear,
            top_k: 10,
        };

        // 三个种子同时注入
        let seeds = vec![
            ("a".to_string(), 1.0),
            ("b".to_string(), 1.0),
            ("c".to_string(), 1.0),
        ];

        let results = engine.propagate(&seeds, &config);

        // Center 应该汇聚三条路径的能量
        let center_energy = results.iter()
            .find(|r| r.node_id == "center")
            .map(|r| r.energy)
            .unwrap_or(0.0);

        // center 应该获得 3 × 1.0 × 1.0 × 1.0 = 3.0 的传播能量 + 0（初始无种子能量）
        assert!(center_energy >= 2.5,
            "Center should accumulate energy from 3 sources, got: {}", center_energy);
    }
}
