// Tests for mecha10_cli::services::simulation

use mecha10_cli::services::simulation::*;

use tempfile::TempDir;

// === Service Creation Tests ===

#[test]
fn test_new_service() {
    let service = SimulationService::new();
    assert_eq!(service.base_path(), Path::new("simulation/godot"));
}

#[test]
fn test_with_base_path() {
    let service = SimulationService::with_base_path("custom/path");
    assert_eq!(service.base_path(), Path::new("custom/path"));
}

#[test]
fn test_with_base_path_string() {
    let service = SimulationService::with_base_path(String::from("another/path"));
    assert_eq!(service.base_path(), Path::new("another/path"));
}

#[test]
fn test_with_base_path_pathbuf() {
    let path = PathBuf::from("path/from/pathbuf");
    let service = SimulationService::with_base_path(path);
    assert_eq!(service.base_path(), Path::new("path/from/pathbuf"));
}

#[test]
fn test_with_base_path_absolute() {
    let service = SimulationService::with_base_path("/absolute/path");
    assert_eq!(service.base_path(), Path::new("/absolute/path"));
}

#[test]
fn test_with_base_path_relative() {
    let service = SimulationService::with_base_path("./relative/path");
    assert_eq!(service.base_path(), Path::new("./relative/path"));
}

#[test]
fn test_default_trait() {
    let service = SimulationService::default();
    assert_eq!(service.base_path(), Path::new("simulation/godot"));
}

#[test]
fn test_default_equals_new() {
    let service1 = SimulationService::new();
    let service2 = SimulationService::default();
    assert_eq!(service1.base_path(), service2.base_path());
}

// === Base Path Tests ===

#[test]
fn test_base_path_getter() {
    let service = SimulationService::with_base_path("test/path");
    assert_eq!(service.base_path(), Path::new("test/path"));
}

#[test]
fn test_base_path_immutable() {
    let service = SimulationService::with_base_path("original");
    let path1 = service.base_path();
    let path2 = service.base_path();
    assert_eq!(path1, path2);
    assert_eq!(path1, Path::new("original"));
}

#[test]
fn test_multiple_services_independent() {
    let service1 = SimulationService::with_base_path("path1");
    let service2 = SimulationService::with_base_path("path2");

    assert_eq!(service1.base_path(), Path::new("path1"));
    assert_eq!(service2.base_path(), Path::new("path2"));
}

// === Setup Tests ===

#[test]
fn test_is_setup_false_when_path_missing() {
    let temp = TempDir::new().unwrap();
    let service = SimulationService::with_base_path(temp.path().join("nonexistent"));
    assert!(!service.is_setup());
}

#[test]
fn test_is_setup_false_when_robot_scene_missing() {
    let temp = TempDir::new().unwrap();
    std::fs::create_dir_all(temp.path()).unwrap();

    let service = SimulationService::with_base_path(temp.path());
    assert!(!service.is_setup());
}

#[test]
fn test_is_setup_true_when_robot_scene_exists() {
    let temp = TempDir::new().unwrap();
    let robot_scene = temp.path().join("robot.tscn");
    std::fs::write(&robot_scene, "").unwrap();

    let service = SimulationService::with_base_path(temp.path());
    assert!(service.is_setup());
}

// === GodotInfo Tests ===

#[test]
fn test_godot_info_creation() {
    let info = GodotInfo {
        path: "/usr/bin/godot".to_string(),
        version: "4.2.1.stable".to_string(),
        in_path: true,
    };

    assert_eq!(info.path, "/usr/bin/godot");
    assert_eq!(info.version, "4.2.1.stable");
    assert!(info.in_path);
}

#[test]
fn test_godot_info_clone() {
    let info1 = GodotInfo {
        path: "godot".to_string(),
        version: "4.2.0".to_string(),
        in_path: false,
    };

    let info2 = info1.clone();

    assert_eq!(info1.path, info2.path);
    assert_eq!(info1.version, info2.version);
    assert_eq!(info1.in_path, info2.in_path);
}

#[test]
fn test_godot_info_debug() {
    let info = GodotInfo {
        path: "godot".to_string(),
        version: "4.2.1".to_string(),
        in_path: true,
    };

    let debug_str = format!("{:?}", info);
    assert!(debug_str.contains("GodotInfo"));
    assert!(debug_str.contains("godot"));
    assert!(debug_str.contains("4.2.1"));
}

// === GenerationResult Tests ===

#[test]
fn test_generation_result_creation() {
    let result = GenerationResult {
        robot_scene: PathBuf::from("output/robot.tscn"),
        environments: vec!["warehouse".to_string(), "office".to_string()],
        available_count: 2,
        godot_info: GodotInfo {
            path: "godot".to_string(),
            version: "4.2.0".to_string(),
            in_path: true,
        },
    };

    assert_eq!(result.robot_scene, PathBuf::from("output/robot.tscn"));
    assert_eq!(result.environments.len(), 2);
    assert_eq!(result.available_count, 2);
}

#[test]
fn test_generation_result_debug() {
    let result = GenerationResult {
        robot_scene: PathBuf::from("robot.tscn"),
        environments: vec!["env1".to_string()],
        available_count: 1,
        godot_info: GodotInfo {
            path: "godot".to_string(),
            version: "4.0.0".to_string(),
            in_path: true,
        },
    };

    let debug_str = format!("{:?}", result);
    assert!(debug_str.contains("GenerationResult"));
    assert!(debug_str.contains("robot.tscn"));
}

// === EnvironmentInfo Tests ===

#[test]
fn test_environment_info_creation() {
    let info = EnvironmentInfo {
        id: "warehouse-1".to_string(),
        name: "Warehouse".to_string(),
        description: "A large warehouse environment".to_string(),
        score: 85,
        available: true,
    };

    assert_eq!(info.id, "warehouse-1");
    assert_eq!(info.name, "Warehouse");
    assert_eq!(info.score, 85);
    assert!(info.available);
}

#[test]
fn test_environment_info_clone() {
    let info1 = EnvironmentInfo {
        id: "env1".to_string(),
        name: "Environment 1".to_string(),
        description: "Description".to_string(),
        score: 75,
        available: false,
    };

    let info2 = info1.clone();

    assert_eq!(info1.id, info2.id);
    assert_eq!(info1.name, info2.name);
    assert_eq!(info1.score, info2.score);
    assert_eq!(info1.available, info2.available);
}

#[test]
fn test_environment_info_debug() {
    let info = EnvironmentInfo {
        id: "test-env".to_string(),
        name: "Test Environment".to_string(),
        description: "For testing".to_string(),
        score: 90,
        available: true,
    };

    let debug_str = format!("{:?}", info);
    assert!(debug_str.contains("EnvironmentInfo"));
    assert!(debug_str.contains("test-env"));
}

// === ValidationResult Tests ===

#[test]
fn test_validation_result_valid() {
    let result = ValidationResult {
        is_valid: true,
        errors: vec![],
        warnings: vec!["No sensors configured".to_string()],
        platform: "rover".to_string(),
        sensor_count: 0,
        node_count: 3,
    };

    assert!(result.is_valid);
    assert!(result.errors.is_empty());
    assert_eq!(result.warnings.len(), 1);
    assert_eq!(result.platform, "rover");
}

#[test]
fn test_validation_result_invalid() {
    let result = ValidationResult {
        is_valid: false,
        errors: vec!["Platform not specified".to_string()],
        warnings: vec![],
        platform: String::new(),
        sensor_count: 2,
        node_count: 1,
    };

    assert!(!result.is_valid);
    assert_eq!(result.errors.len(), 1);
    assert!(result.warnings.is_empty());
}

#[test]
fn test_validation_result_debug() {
    let result = ValidationResult {
        is_valid: true,
        errors: vec![],
        warnings: vec![],
        platform: "arm".to_string(),
        sensor_count: 5,
        node_count: 2,
    };

    let debug_str = format!("{:?}", result);
    assert!(debug_str.contains("ValidationResult"));
    assert!(debug_str.contains("true"));
    assert!(debug_str.contains("arm"));
}

// === Godot Validation Tests ===

#[test]
fn test_validate_godot() {
    let service = SimulationService::new();
    // This test will pass or fail depending on whether Godot is installed
    // We just check that the function runs without panicking
    let _ = service.validate_godot();
}

#[test]
fn test_validate_godot_with_custom_path() {
    let service = SimulationService::with_base_path("/custom/sim/path");
    // Validation doesn't depend on base_path
    let _ = service.validate_godot();
}

// === Method Signature Tests ===

#[test]
fn test_generate_robot_method_exists() {
    let temp = TempDir::new().unwrap();
    let config = temp.path().join("mecha10.json");
    let output = temp.path().join("robot.tscn");

    let service = SimulationService::new();
    // Just verify method signature - will fail without valid config
    let _ = service.generate_robot(&config, &output);
}

#[test]
fn test_list_environments_method_exists() {
    let temp = TempDir::new().unwrap();
    let config = temp.path().join("mecha10.json");

    let service = SimulationService::new();
    // Just verify method signature - will fail without valid config
    let _ = service.list_environments(&config, false);
}

#[test]
fn test_validate_config_method_exists() {
    let temp = TempDir::new().unwrap();
    let config = temp.path().join("mecha10.json");

    let service = SimulationService::new();
    // Just verify method signature - will fail without valid config
    let _ = service.validate_config(&config);
}

#[test]
fn test_run_method_exists() {
    let service = SimulationService::new();
    // Just verify method signature - will fail without Godot
    let _ = service.run("test-robot", "test-env", false);
}

// === Edge Cases ===

#[test]
fn test_base_path_with_empty_string() {
    let service = SimulationService::with_base_path("");
    assert_eq!(service.base_path(), Path::new(""));
}

#[test]
fn test_base_path_with_unicode() {
    let service = SimulationService::with_base_path("模拟/路径");
    assert_eq!(service.base_path(), Path::new("模拟/路径"));
}

#[test]
fn test_base_path_with_special_characters() {
    let service = SimulationService::with_base_path("path with spaces/and-dashes");
    assert_eq!(service.base_path(), Path::new("path with spaces/and-dashes"));
}

#[test]
fn test_godot_info_with_empty_strings() {
    let info = GodotInfo {
        path: String::new(),
        version: String::new(),
        in_path: false,
    };

    assert_eq!(info.path, "");
    assert_eq!(info.version, "");
    assert!(!info.in_path);
}

#[test]
fn test_generation_result_empty_environments() {
    let result = GenerationResult {
        robot_scene: PathBuf::from("robot.tscn"),
        environments: vec![],
        available_count: 0,
        godot_info: GodotInfo {
            path: "godot".to_string(),
            version: "4.0.0".to_string(),
            in_path: true,
        },
    };

    assert!(result.environments.is_empty());
    assert_eq!(result.available_count, 0);
}

#[test]
fn test_validation_result_with_multiple_errors_and_warnings() {
    let result = ValidationResult {
        is_valid: false,
        errors: vec!["Error 1".to_string(), "Error 2".to_string(), "Error 3".to_string()],
        warnings: vec!["Warning 1".to_string(), "Warning 2".to_string()],
        platform: "test".to_string(),
        sensor_count: 0,
        node_count: 0,
    };

    assert_eq!(result.errors.len(), 3);
    assert_eq!(result.warnings.len(), 2);
}
