use sal_process::{kill, process_get, process_list, which, ProcessError};

#[test]
fn test_which_existing_command() {
    // Test with a command that should exist on all systems
    #[cfg(target_os = "windows")]
    let cmd = "cmd";

    #[cfg(not(target_os = "windows"))]
    let cmd = "sh";

    let result = which(cmd);
    assert!(result.is_some());
    assert!(!result.unwrap().is_empty());
}

#[test]
fn test_which_nonexistent_command() {
    let result = which("nonexistent_command_12345");
    assert!(result.is_none());
}

#[test]
fn test_which_common_commands() {
    // Test common commands that should exist
    let common_commands = if cfg!(target_os = "windows") {
        vec!["cmd", "powershell"]
    } else {
        vec!["sh", "ls", "echo"]
    };

    for cmd in common_commands {
        let result = which(cmd);
        assert!(result.is_some(), "Command '{}' should be found", cmd);
        assert!(!result.unwrap().is_empty());
    }
}

#[test]
fn test_process_list_all() {
    let result = process_list("").unwrap();
    assert!(
        !result.is_empty(),
        "Should find at least one running process"
    );

    // Verify process info structure
    let first_process = &result[0];
    assert!(first_process.pid > 0, "Process PID should be positive");
    assert!(
        !first_process.name.is_empty(),
        "Process name should not be empty"
    );
}

#[test]
fn test_process_list_with_pattern() {
    // Try to find processes with common names
    let patterns = if cfg!(target_os = "windows") {
        vec!["explorer", "winlogon", "System"]
    } else {
        vec!["init", "kernel", "systemd"]
    };

    let mut found_any = false;
    for pattern in patterns {
        if let Ok(processes) = process_list(pattern) {
            if !processes.is_empty() {
                found_any = true;
                for process in processes {
                    assert!(
                        process.name.contains(pattern)
                            || process
                                .name
                                .to_lowercase()
                                .contains(&pattern.to_lowercase())
                    );
                    assert!(process.pid > 0);
                }
                break;
            }
        }
    }

    // At least one pattern should match some processes
    assert!(
        found_any,
        "Should find at least one process with common patterns"
    );
}

#[test]
fn test_process_list_nonexistent_pattern() {
    let result = process_list("nonexistent_process_12345").unwrap();
    assert!(
        result.is_empty(),
        "Should not find any processes with nonexistent pattern"
    );
}

#[test]
fn test_process_info_structure() {
    let processes = process_list("").unwrap();
    assert!(!processes.is_empty());

    let process = &processes[0];

    // Test ProcessInfo fields
    assert!(process.pid > 0);
    assert!(!process.name.is_empty());
    // memory and cpu are placeholders, so we just check they exist
    assert!(process.memory >= 0.0);
    assert!(process.cpu >= 0.0);
}

#[test]
fn test_process_get_single_match() {
    // Find a process that should be unique
    let processes = process_list("").unwrap();
    assert!(!processes.is_empty());

    // Try to find a process with a unique enough name
    let mut unique_process = None;
    for process in &processes {
        let matches = process_list(&process.name).unwrap();
        if matches.len() == 1 {
            unique_process = Some(process.clone());
            break;
        }
    }

    if let Some(process) = unique_process {
        let result = process_get(&process.name).unwrap();
        assert_eq!(result.pid, process.pid);
        assert_eq!(result.name, process.name);
    }
}

#[test]
fn test_process_get_no_match() {
    let result = process_get("nonexistent_process_12345");
    assert!(result.is_err());
    match result.unwrap_err() {
        ProcessError::NoProcessFound(pattern) => {
            assert_eq!(pattern, "nonexistent_process_12345");
        }
        _ => panic!("Expected NoProcessFound error"),
    }
}

#[test]
fn test_process_get_multiple_matches() {
    // Find a pattern that matches multiple processes
    let all_processes = process_list("").unwrap();
    assert!(!all_processes.is_empty());

    // Try common patterns that might match multiple processes
    let patterns = if cfg!(target_os = "windows") {
        vec!["svchost", "conhost"]
    } else {
        vec!["kthread", "ksoftirqd"]
    };

    let mut _found_multiple = false;
    for pattern in patterns {
        if let Ok(processes) = process_list(pattern) {
            if processes.len() > 1 {
                let result = process_get(pattern);
                assert!(result.is_err());
                match result.unwrap_err() {
                    ProcessError::MultipleProcessesFound(p, count) => {
                        assert_eq!(p, pattern);
                        assert_eq!(count, processes.len());
                        _found_multiple = true;
                        break;
                    }
                    _ => panic!("Expected MultipleProcessesFound error"),
                }
            }
        }
    }

    // If we can't find multiple matches with common patterns, that's okay
    // The test validates the error handling works correctly
}

#[test]
fn test_kill_nonexistent_process() {
    let result = kill("nonexistent_process_12345").unwrap();
    assert!(result.contains("No matching processes") || result.contains("Successfully killed"));
}

#[test]
fn test_process_list_performance() {
    use std::time::Instant;

    let start = Instant::now();
    let _processes = process_list("").unwrap();
    let duration = start.elapsed();

    // Process listing should complete within reasonable time (5 seconds)
    assert!(
        duration.as_secs() < 5,
        "Process listing took too long: {:?}",
        duration
    );
}

#[test]
fn test_which_performance() {
    use std::time::Instant;

    let start = Instant::now();
    let _result = which("echo");
    let duration = start.elapsed();

    // Which command should be very fast (1 second)
    assert!(
        duration.as_secs() < 1,
        "Which command took too long: {:?}",
        duration
    );
}

#[test]
fn test_process_list_filtering_accuracy() {
    // Test that filtering actually works correctly
    let all_processes = process_list("").unwrap();
    assert!(!all_processes.is_empty());

    // Pick a process name and filter by it
    let test_process = &all_processes[0];
    let filtered_processes = process_list(&test_process.name).unwrap();

    // All filtered processes should contain the pattern
    for process in filtered_processes {
        assert!(process.name.contains(&test_process.name));
    }
}

#[test]
fn test_process_error_display() {
    let error = ProcessError::NoProcessFound("test".to_string());
    let error_string = format!("{}", error);
    assert!(error_string.contains("No processes found matching 'test'"));

    let error = ProcessError::MultipleProcessesFound("test".to_string(), 5);
    let error_string = format!("{}", error);
    assert!(error_string.contains("Multiple processes (5) found matching 'test'"));
}

#[test]
fn test_cross_platform_process_operations() {
    // Test operations that should work on all platforms

    // Test which with platform-specific commands
    #[cfg(target_os = "windows")]
    {
        assert!(which("cmd").is_some());
        assert!(which("notepad").is_some());
    }

    #[cfg(target_os = "macos")]
    {
        assert!(which("sh").is_some());
        assert!(which("ls").is_some());
    }

    #[cfg(target_os = "linux")]
    {
        assert!(which("sh").is_some());
        assert!(which("ls").is_some());
    }

    // Test process listing works on all platforms
    let processes = process_list("").unwrap();
    assert!(!processes.is_empty());
}
