//! Tests for node_selection module

use crate::dev::node_selection::{get_available_node_names, get_enabled_node_names, get_nodes_to_run, NodeToRun};
use crate::types::project::{NodeEntry, NodesConfig, ProjectConfig, RobotConfig};

fn create_test_config() -> ProjectConfig {
    ProjectConfig {
        name: "test-project".to_string(),
        version: "0.1.0".to_string(),
        robot: RobotConfig {
            id: "test-robot".to_string(),
            platform: None,
            description: None,
        },
        simulation: None,
        lifecycle: None,
        nodes: NodesConfig {
            drivers: vec![
                NodeEntry {
                    name: "speaker".to_string(),
                    path: "mecha10-nodes-speaker".to_string(),
                    enabled: true,
                    config: None,
                    description: None,
                    run_target: None,
                },
                NodeEntry {
                    name: "listener".to_string(),
                    path: "mecha10-nodes-listener".to_string(),
                    enabled: true,
                    config: None,
                    description: None,
                    run_target: None,
                },
                NodeEntry {
                    name: "motor".to_string(),
                    path: "mecha10-nodes-motor-fake".to_string(),
                    enabled: false,
                    config: None,
                    description: None,
                    run_target: None,
                },
            ],
            custom: vec![NodeEntry {
                name: "custom-node".to_string(),
                path: "nodes/custom".to_string(),
                enabled: true,
                config: None,
                description: None,
                run_target: None,
            }],
        },
        behaviors: None,
        services: Default::default(),
        docker: Default::default(),
        redis: Default::default(),
    }
}

#[test]
fn test_get_nodes_to_run_all_enabled() {
    let config = create_test_config();
    let nodes = get_nodes_to_run(&[], &config);

    assert_eq!(nodes.len(), 3);
    assert!(nodes.iter().any(|n| n.name == "speaker"));
    assert!(nodes.iter().any(|n| n.name == "listener"));
    assert!(nodes.iter().any(|n| n.name == "custom-node"));
    assert!(!nodes.iter().any(|n| n.name == "motor")); // disabled
}

#[test]
fn test_get_nodes_to_run_specific() {
    let config = create_test_config();
    let requested = vec!["speaker".to_string(), "motor".to_string()];
    let nodes = get_nodes_to_run(&requested, &config);

    assert_eq!(nodes.len(), 2);
    assert!(nodes.iter().any(|n| n.name == "speaker"));
    assert!(nodes.iter().any(|n| n.name == "motor"));
}

#[test]
fn test_get_nodes_to_run_empty_config() {
    let config = ProjectConfig {
        name: "test".to_string(),
        version: "0.1.0".to_string(),
        robot: RobotConfig {
            id: "test".to_string(),
            platform: None,
            description: None,
        },
        simulation: None,
        lifecycle: None,
        nodes: NodesConfig {
            drivers: vec![],
            custom: vec![],
        },
        behaviors: None,
        services: Default::default(),
        docker: Default::default(),
        redis: Default::default(),
    };

    let nodes = get_nodes_to_run(&[], &config);
    assert_eq!(nodes.len(), 0);
}

#[test]
fn test_node_to_run_monorepo() {
    let node = NodeToRun {
        name: "speaker".to_string(),
        path: "mecha10-nodes-speaker".to_string(),
        is_monorepo_node: true,
    };

    // Note: binary_path may return global path if binary is installed
    // In test environment without global binaries, it falls back to local build
    let binary_path = node.binary_path("my-robot");
    assert!(binary_path.ends_with("my-robot") || binary_path.ends_with("speaker"));

    // Args depend on whether using global or local binary
    let args = node.args("my-robot");
    // If local build (starts with "target/"), should have "node" subcommand
    // If global binary, should be empty
    assert!(args.is_empty() || args == vec!["node", "speaker"]);
}

#[test]
fn test_node_to_run_local() {
    let node = NodeToRun {
        name: "custom".to_string(),
        path: "nodes/custom".to_string(),
        is_monorepo_node: false,
    };

    assert_eq!(node.binary_path("my-robot"), "target/release/custom");
    assert_eq!(node.args("my-robot"), Vec::<String>::new());
}

#[test]
fn test_node_to_run_from_config_monorepo() {
    let node_config = NodeEntry {
        name: "speaker".to_string(),
        path: "mecha10-nodes-speaker".to_string(),
        enabled: true,
        config: None,
        description: None,
        run_target: None,
    };

    let node = NodeToRun::from_config(&node_config);
    assert_eq!(node.name, "speaker");
    assert_eq!(node.path, "mecha10-nodes-speaker");
    assert!(node.is_monorepo_node);
}

#[test]
fn test_node_to_run_from_config_local() {
    let node_config = NodeEntry {
        name: "custom".to_string(),
        path: "nodes/custom".to_string(),
        enabled: true,
        config: None,
        description: None,
        run_target: None,
    };

    let node = NodeToRun::from_config(&node_config);
    assert_eq!(node.name, "custom");
    assert_eq!(node.path, "nodes/custom");
    assert!(!node.is_monorepo_node);
}

#[test]
fn test_get_available_node_names() {
    let config = create_test_config();
    let names = get_available_node_names(&config);

    assert_eq!(names.len(), 4);
    assert!(names.contains(&"speaker".to_string()));
    assert!(names.contains(&"listener".to_string()));
    assert!(names.contains(&"motor".to_string()));
    assert!(names.contains(&"custom-node".to_string()));
}

#[test]
fn test_get_enabled_node_names() {
    let config = create_test_config();
    let names = get_enabled_node_names(&config);

    assert_eq!(names.len(), 3);
    assert!(names.contains(&"speaker".to_string()));
    assert!(names.contains(&"listener".to_string()));
    assert!(names.contains(&"custom-node".to_string()));
    assert!(!names.contains(&"motor".to_string()));
}

#[test]
fn test_get_available_node_names_empty() {
    let config = ProjectConfig {
        name: "test".to_string(),
        version: "0.1.0".to_string(),
        robot: RobotConfig {
            id: "test".to_string(),
            platform: None,
            description: None,
        },
        simulation: None,
        lifecycle: None,
        nodes: NodesConfig {
            drivers: vec![],
            custom: vec![],
        },
        behaviors: None,
        services: Default::default(),
        docker: Default::default(),
        redis: Default::default(),
    };

    let names = get_available_node_names(&config);
    assert_eq!(names.len(), 0);
}
