//! DAG 定义 — 可序列化/反序列化，用于持久化和模板

use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// DAG 定义 — 可序列化/反序列化，用于持久化和模板
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct DagDef {
    // nodes/edges 加 #[serde(default)]。
    // 前端 run_workflow 推进链传空 dag `{}`(设计意图:空 dag + target_status →
    // workflow.rs:91 dag.nodes.is_empty() 进 template_for 自动选模板)。原无 default
    // 时,`{}` 在 Tauri IPC 入口反序列化 DagDef 即报 `missing field nodes`,挡在选模板
    // 逻辑之前。加 default 后 `{}` → 空 DagDef(HashMap/Vec 的 Default 均空集合),
    // 顺利进 :91 选模板分支。非空 dag 零回归(serde default 仅补缺失字段)。
    #[serde(default)]
    pub nodes: HashMap<String, NodeDef>,
    #[serde(default)]
    pub edges: Vec<EdgeDef>,
}

/// 节点定义
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct NodeDef {
    pub id: String,
    pub node_type: String,
    pub config: serde_json::Value,
    #[serde(default)]
    pub label: Option<String>,
}

/// 边定义
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct EdgeDef {
    pub source: String,
    pub target: String,
    #[serde(default)]
    pub condition: Option<String>,
}

impl DagDef {
    /// 创建空的 DAG 定义
    pub fn new() -> Self {
        Self {
            nodes: HashMap::new(),
            edges: Vec::new(),
        }
    }

    /// 添加节点定义
    pub fn add_node(&mut self, id: impl Into<String>, node_type: impl Into<String>, config: serde_json::Value) {
        let id = id.into();
        self.nodes.insert(id.clone(), NodeDef {
            id,
            node_type: node_type.into(),
            config,
            label: None,
        });
    }

    /// 添加边定义
    pub fn add_edge(&mut self, source: impl Into<String>, target: impl Into<String>) {
        self.edges.push(EdgeDef {
            source: source.into(),
            target: target.into(),
            condition: None,
        });
    }

    /// 添加带条件的边定义
    pub fn add_edge_with_condition(
        &mut self,
        source: impl Into<String>,
        target: impl Into<String>,
        condition: impl Into<String>,
    ) {
        self.edges.push(EdgeDef {
            source: source.into(),
            target: target.into(),
            condition: Some(condition.into()),
        });
    }
}

impl Default for DagDef {
    fn default() -> Self {
        Self::new()
    }
}

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

    // 前端 run_workflow 推进链传空 dag `{}`,serde 必须能反序列化
    // 成空 DagDef(非报 missing field),后续 workflow.rs:91 dag.nodes.is_empty()
    // 进 template_for 选模板。nodes/edges 加 #[serde(default)] 前此用例会失败。
    #[test]
    fn empty_json_deserializes_to_empty_dagdef() {
        let dag: DagDef = serde_json::from_str("{}").expect("空 JSON 应反序列化为空 DagDef");
        assert!(dag.nodes.is_empty());
        assert!(dag.edges.is_empty());
    }

    // 零回归:非空 dag 反序列化行为不变(serde default 仅补缺失字段)。
    #[test]
    fn nonempty_dag_deserializes_unchanged() {
        let json = r#"{"nodes":{"n1":{"id":"n1","node_type":"echo","config":{}}},"edges":[]}"#;
        let dag: DagDef = serde_json::from_str(json).expect("非空 dag 反序列化");
        assert_eq!(dag.nodes.len(), 1);
        assert!(dag.nodes.contains_key("n1"));
        assert!(dag.edges.is_empty());
    }
}
