use crate::dto::{EdgeDef, NodeDef, NodeType};
use crate::engine::model::FlowGraph;
use crate::error::{WorkflowError, WorkflowResult};

/// 解析节点和边列表为 FlowGraph 并验证合法性。
pub fn parse_and_validate(nodes: &[NodeDef], edges: &[EdgeDef]) -> WorkflowResult<FlowGraph> {
    // 基本检查：至少有一个节点
    if nodes.is_empty() {
        return Err(WorkflowError::InvalidDiagram("流程图不能为空".to_string()));
    }

    // 检查恰好 1 个 StartEvent
    let start_count = nodes
        .iter()
        .filter(|n| n.node_type == NodeType::StartEvent)
        .count();
    if start_count == 0 {
        return Err(WorkflowError::InvalidDiagram(
            "流程图必须包含一个开始事件".to_string(),
        ));
    }
    if start_count > 1 {
        return Err(WorkflowError::InvalidDiagram(
            "流程图只能包含一个开始事件".to_string(),
        ));
    }

    // 检查至少 1 个 EndEvent
    let end_count = nodes
        .iter()
        .filter(|n| n.node_type == NodeType::EndEvent)
        .count();
    if end_count == 0 {
        return Err(WorkflowError::InvalidDiagram(
            "流程图必须包含至少一个结束事件".to_string(),
        ));
    }

    // 检查节点 ID 唯一性
    let node_ids: std::collections::HashSet<&str> = nodes.iter().map(|n| n.id.as_str()).collect();
    if node_ids.len() != nodes.len() {
        return Err(WorkflowError::InvalidDiagram(
            "节点 ID 不能重复".to_string(),
        ));
    }

    // 检查边引用的节点存在
    for e in edges {
        if !node_ids.contains(e.source.as_str()) {
            return Err(WorkflowError::InvalidDiagram(format!(
                "连线 {} 的源节点 {} 不存在",
                e.id, e.source
            )));
        }
        if !node_ids.contains(e.target.as_str()) {
            return Err(WorkflowError::InvalidDiagram(format!(
                "连线 {} 的目标节点 {} 不存在",
                e.id, e.target
            )));
        }
    }

    // 构建图
    let graph = FlowGraph::build(nodes, edges);

    // 检查 StartEvent 没有入边
    if let Some(start_id) = &graph.start_node_id {
        if !graph.get_incoming_edges(start_id).is_empty() {
            return Err(WorkflowError::InvalidDiagram(
                "开始事件不能有入边".to_string(),
            ));
        }
        if graph.get_outgoing_edges(start_id).is_empty() {
            return Err(WorkflowError::InvalidDiagram(
                "开始事件必须有出边".to_string(),
            ));
        }
    }

    // 检查 EndEvent 没有出边
    for end_id in &graph.end_node_ids {
        if !graph.get_outgoing_edges(end_id).is_empty() {
            return Err(WorkflowError::InvalidDiagram(
                "结束事件不能有出边".to_string(),
            ));
        }
    }

    // 检查网关至少有一个入边和一个出边（排除 start/end）
    for node in nodes {
        match &node.node_type {
            NodeType::ExclusiveGateway | NodeType::ParallelGateway => {
                let inc = graph.get_incoming_edges(&node.id);
                let out = graph.get_outgoing_edges(&node.id);
                if inc.is_empty() {
                    return Err(WorkflowError::InvalidDiagram(format!(
                        "网关 '{}' 必须有至少一条入边",
                        node.name
                    )));
                }
                if out.is_empty() {
                    return Err(WorkflowError::InvalidDiagram(format!(
                        "网关 '{}' 必须有至少一条出边",
                        node.name
                    )));
                }
                // 排他网关的出边应该有条件（第一条可以无条件作为默认分支）
                if node.node_type == NodeType::ExclusiveGateway && out.len() > 1 {
                    let with_condition: Vec<_> =
                        out.iter().filter(|e| e.condition.is_some()).collect();
                    if with_condition.is_empty() {
                        return Err(WorkflowError::InvalidDiagram(format!(
                            "排他网关 '{}' 有多条出边但没有条件表达式",
                            node.name
                        )));
                    }
                }
            }
            _ => {}
        }
    }

    Ok(graph)
}

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

    fn make_start() -> NodeDef {
        NodeDef {
            id: "start".to_string(),
            node_type: NodeType::StartEvent,
            name: "开始".to_string(),
            assignee_id: None,
            candidate_groups: None,
            service_type: None,
            service_config: None,
            position: Some(NodePosition { x: 100.0, y: 100.0 }),
        }
    }

    fn make_end() -> NodeDef {
        NodeDef {
            id: "end".to_string(),
            node_type: NodeType::EndEvent,
            name: "结束".to_string(),
            assignee_id: None,
            candidate_groups: None,
            service_type: None,
            service_config: None,
            position: Some(NodePosition { x: 100.0, y: 300.0 }),
        }
    }

    fn make_user_task(id: &str, name: &str) -> NodeDef {
        NodeDef {
            id: id.to_string(),
            node_type: NodeType::UserTask,
            name: name.to_string(),
            assignee_id: None,
            candidate_groups: None,
            service_type: None,
            service_config: None,
            position: None,
        }
    }

    fn make_edge(id: &str, source: &str, target: &str) -> EdgeDef {
        EdgeDef {
            id: id.to_string(),
            source: source.to_string(),
            target: target.to_string(),
            condition: None,
            label: None,
        }
    }

    #[test]
    fn test_valid_linear_flow() {
        let nodes = vec![make_start(), make_user_task("task1", "审批"), make_end()];
        let edges = vec![
            make_edge("e1", "start", "task1"),
            make_edge("e2", "task1", "end"),
        ];
        let result = parse_and_validate(&nodes, &edges);
        assert!(result.is_ok());
        let graph = result.unwrap();
        assert_eq!(graph.start_node_id, Some("start".to_string()));
        assert_eq!(graph.end_node_ids, vec!["end".to_string()]);
    }

    #[test]
    fn test_no_start_event() {
        let nodes = vec![make_user_task("task1", "审批"), make_end()];
        let edges = vec![make_edge("e1", "task1", "end")];
        let result = parse_and_validate(&nodes, &edges);
        assert!(result.is_err());
        let msg = result.unwrap_err().to_string();
        assert!(msg.contains("开始事件"));
    }

    #[test]
    fn test_no_end_event() {
        let nodes = vec![make_start(), make_user_task("task1", "审批")];
        let edges = vec![make_edge("e1", "start", "task1")];
        let result = parse_and_validate(&nodes, &edges);
        assert!(result.is_err());
        let msg = result.unwrap_err().to_string();
        assert!(msg.contains("结束事件"));
    }

    #[test]
    fn test_duplicate_node_id() {
        let nodes = vec![
            make_start(),
            NodeDef {
                id: "start".to_string(), // 重复 ID
                node_type: NodeType::EndEvent,
                name: "结束".to_string(),
                assignee_id: None,
                candidate_groups: None,
                service_type: None,
                service_config: None,
                position: None,
            },
        ];
        let edges = vec![];
        let result = parse_and_validate(&nodes, &edges);
        assert!(result.is_err());
    }

    #[test]
    fn test_end_event_with_outgoing() {
        let nodes = vec![make_start(), make_end()];
        let edges = vec![
            make_edge("e1", "start", "end"),
            make_edge("e2", "end", "start"), // 结束事件有出边
        ];
        let result = parse_and_validate(&nodes, &edges);
        assert!(result.is_err());
    }

    #[test]
    fn test_exclusive_gateway_without_conditions() {
        let nodes = vec![
            make_start(),
            NodeDef {
                id: "gw1".to_string(),
                node_type: NodeType::ExclusiveGateway,
                name: "判断".to_string(),
                assignee_id: None,
                candidate_groups: None,
                service_type: None,
                service_config: None,
                position: None,
            },
            make_end(),
        ];
        let edges = vec![
            make_edge("e1", "start", "gw1"),
            make_edge("e2", "gw1", "end"),
            make_edge("e3", "gw1", "end"), // 两条出边无条件
        ];
        let result = parse_and_validate(&nodes, &edges);
        assert!(result.is_err());
    }

    // ---- 边界测试扩展 ----

    #[test]
    fn test_empty_nodes_rejected() {
        let result = parse_and_validate(&[], &[]);
        assert!(result.is_err());
        let msg = result.unwrap_err().to_string();
        assert!(msg.contains("不能为空"));
    }

    #[test]
    fn test_multiple_start_events_rejected() {
        let nodes = vec![
            make_start(),
            NodeDef {
                id: "start2".to_string(),
                node_type: NodeType::StartEvent,
                name: "开始2".to_string(),
                assignee_id: None,
                candidate_groups: None,
                service_type: None,
                service_config: None,
                position: None,
            },
            make_end(),
        ];
        let edges = vec![
            make_edge("e1", "start", "end"),
            make_edge("e2", "start2", "end"),
        ];
        let result = parse_and_validate(&nodes, &edges);
        assert!(result.is_err());
        let msg = result.unwrap_err().to_string();
        assert!(msg.contains("只能包含一个开始事件"));
    }

    #[test]
    fn test_edge_references_nonexistent_source() {
        let nodes = vec![make_start(), make_end()];
        let edges = vec![make_edge("e1", "ghost", "end")];
        let result = parse_and_validate(&nodes, &edges);
        assert!(result.is_err());
        let msg = result.unwrap_err().to_string();
        assert!(msg.contains("源节点") && msg.contains("不存在"));
    }

    #[test]
    fn test_edge_references_nonexistent_target() {
        let nodes = vec![make_start(), make_end()];
        let edges = vec![make_edge("e1", "start", "ghost")];
        let result = parse_and_validate(&nodes, &edges);
        assert!(result.is_err());
        let msg = result.unwrap_err().to_string();
        assert!(msg.contains("目标节点") && msg.contains("不存在"));
    }

    #[test]
    fn test_start_event_with_incoming_edge_rejected() {
        let nodes = vec![make_start(), make_end()];
        let edges = vec![
            make_edge("e1", "start", "end"),
            make_edge("e2", "end", "start"), // start 有入边
        ];
        let result = parse_and_validate(&nodes, &edges);
        assert!(result.is_err());
        let msg = result.unwrap_err().to_string();
        assert!(msg.contains("入边"));
    }

    #[test]
    fn test_start_event_without_outgoing_edge_rejected() {
        let nodes = vec![make_start(), make_end()];
        let edges = vec![]; // start 没有出边
        let result = parse_and_validate(&nodes, &edges);
        assert!(result.is_err());
        let msg = result.unwrap_err().to_string();
        assert!(msg.contains("出边"));
    }

    #[test]
    fn test_exclusive_gateway_single_outgoing_ok() {
        // 单条出边的排他网关不需要条件
        let nodes = vec![
            make_start(),
            NodeDef {
                id: "gw".to_string(),
                node_type: NodeType::ExclusiveGateway,
                name: "判断".to_string(),
                assignee_id: None,
                candidate_groups: None,
                service_type: None,
                service_config: None,
                position: None,
            },
            make_end(),
        ];
        let edges = vec![make_edge("e1", "start", "gw"), make_edge("e2", "gw", "end")];
        assert!(parse_and_validate(&nodes, &edges).is_ok());
    }

    #[test]
    fn test_exclusive_gateway_with_conditions_ok() {
        let nodes = vec![
            make_start(),
            NodeDef {
                id: "gw".to_string(),
                node_type: NodeType::ExclusiveGateway,
                name: "金额判断".to_string(),
                assignee_id: None,
                candidate_groups: None,
                service_type: None,
                service_config: None,
                position: None,
            },
            make_user_task("task_a", "小额审批"),
            make_user_task("task_b", "大额审批"),
            make_end(),
        ];
        let edges = vec![
            make_edge("e1", "start", "gw"),
            EdgeDef {
                id: "e2".to_string(),
                source: "gw".to_string(),
                target: "task_a".to_string(),
                condition: Some("amount <= 1000".to_string()),
                label: None,
            },
            EdgeDef {
                id: "e3".to_string(),
                source: "gw".to_string(),
                target: "task_b".to_string(),
                condition: Some("amount > 1000".to_string()),
                label: None,
            },
            make_edge("e4", "task_a", "end"),
            make_edge("e5", "task_b", "end"),
        ];
        assert!(parse_and_validate(&nodes, &edges).is_ok());
    }

    #[test]
    fn test_gateway_without_incoming_rejected() {
        let nodes = vec![
            make_start(),
            NodeDef {
                id: "gw".to_string(),
                node_type: NodeType::ParallelGateway,
                name: "并行".to_string(),
                assignee_id: None,
                candidate_groups: None,
                service_type: None,
                service_config: None,
                position: None,
            },
            make_end(),
        ];
        // gw 没有入边
        let edges = vec![
            make_edge("e1", "start", "end"),
            make_edge("e2", "gw", "end"),
        ];
        let result = parse_and_validate(&nodes, &edges);
        assert!(result.is_err());
        let msg = result.unwrap_err().to_string();
        assert!(msg.contains("入边"));
    }

    #[test]
    fn test_gateway_without_outgoing_rejected() {
        let nodes = vec![
            make_start(),
            NodeDef {
                id: "gw".to_string(),
                node_type: NodeType::ParallelGateway,
                name: "并行".to_string(),
                assignee_id: None,
                candidate_groups: None,
                service_type: None,
                service_config: None,
                position: None,
            },
            make_end(),
        ];
        // gw 没有出边
        let edges = vec![
            make_edge("e1", "start", "gw"),
            make_edge("e2", "start", "end"),
        ];
        let result = parse_and_validate(&nodes, &edges);
        assert!(result.is_err());
        let msg = result.unwrap_err().to_string();
        assert!(msg.contains("出边"));
    }

    #[test]
    fn test_parallel_gateway_valid() {
        let nodes = vec![
            make_start(),
            NodeDef {
                id: "fork".to_string(),
                node_type: NodeType::ParallelGateway,
                name: "拆分".to_string(),
                assignee_id: None,
                candidate_groups: None,
                service_type: None,
                service_config: None,
                position: None,
            },
            make_user_task("a", "任务A"),
            make_user_task("b", "任务B"),
            make_end(),
        ];
        let edges = vec![
            make_edge("e1", "start", "fork"),
            make_edge("e2", "fork", "a"),
            make_edge("e3", "fork", "b"),
            make_edge("e4", "a", "end"),
            make_edge("e5", "b", "end"),
        ];
        assert!(parse_and_validate(&nodes, &edges).is_ok());
    }
}
