//! Tests for package collector functions

use mecha10_cli::services::package_service::collector::*;
use mecha10_cli::services::package_service::types::*;
use std::fs;
use tempfile::TempDir;

#[test]
fn test_collect_binaries_finds_executables() {
    // Create temp directory structure
    let temp = TempDir::new().unwrap();
    let project_root = temp.path();

    // Create target directory with mock binaries
    let target_dir = project_root
        .join("target")
        .join("x86_64-unknown-linux-gnu")
        .join("release");
    fs::create_dir_all(&target_dir).unwrap();

    // Create mock binary files
    let binary1 = target_dir.join("robot-node");
    let binary2 = target_dir.join("camera-driver");
    fs::write(&binary1, "mock binary content 1").unwrap();
    fs::write(&binary2, "mock binary content 2").unwrap();

    // Make them executable on Unix
    #[cfg(unix)]
    {
        use std::os::unix::fs::PermissionsExt;
        let mut perms = fs::metadata(&binary1).unwrap().permissions();
        perms.set_mode(0o755);
        fs::set_permissions(&binary1, perms).unwrap();

        let mut perms = fs::metadata(&binary2).unwrap().permissions();
        perms.set_mode(0o755);
        fs::set_permissions(&binary2, perms).unwrap();
    }

    let config = PackageConfig {
        output_dir: project_root.join("target/packages"),
        target_arch: TargetArch::X8664UnknownLinuxGnu,
        build_profile: "release".to_string(),
        include_assets: true,
        environment: None,
        custom_metadata: std::collections::HashMap::new(),
    };

    let result = collect_binaries(project_root, &config);

    #[cfg(unix)]
    {
        assert!(result.is_ok());
        let binaries = result.unwrap();
        assert_eq!(binaries.len(), 2);
        assert!(binaries.iter().any(|b| b.name == "robot-node"));
        assert!(binaries.iter().any(|b| b.name == "camera-driver"));
    }

    #[cfg(not(unix))]
    {
        // On Windows, files aren't marked as executable the same way
        // Just check that the function runs without error
        let _ = result;
    }
}

#[test]
fn test_collect_binaries_skips_build_artifacts() {
    let temp = TempDir::new().unwrap();
    let project_root = temp.path();

    let target_dir = project_root
        .join("target")
        .join("x86_64-unknown-linux-gnu")
        .join("release");
    fs::create_dir_all(&target_dir).unwrap();

    // Create files that should be skipped
    let hidden = target_dir.join(".hidden");
    let deps = target_dir.join("deps.d");
    let rlib = target_dir.join("libfoo.rlib");

    fs::write(&hidden, "hidden").unwrap();
    fs::write(&deps, "deps").unwrap();
    fs::write(&rlib, "rlib").unwrap();

    // Create one valid binary
    let binary = target_dir.join("robot-node");
    fs::write(&binary, "binary content").unwrap();

    #[cfg(unix)]
    {
        use std::os::unix::fs::PermissionsExt;
        let mut perms = fs::metadata(&binary).unwrap().permissions();
        perms.set_mode(0o755);
        fs::set_permissions(&binary, perms).unwrap();
    }

    let config = PackageConfig {
        output_dir: project_root.join("target/packages"),
        target_arch: TargetArch::X8664UnknownLinuxGnu,
        build_profile: "release".to_string(),
        include_assets: true,
        environment: None,
        custom_metadata: std::collections::HashMap::new(),
    };

    let result = collect_binaries(project_root, &config);

    #[cfg(unix)]
    {
        assert!(result.is_ok());
        let binaries = result.unwrap();
        assert_eq!(binaries.len(), 1);
        assert_eq!(binaries[0].name, "robot-node");
    }
}

#[test]
fn test_collect_binaries_missing_target_dir() {
    let temp = TempDir::new().unwrap();
    let project_root = temp.path();

    let config = PackageConfig::default();

    let result = collect_binaries(project_root, &config);
    assert!(result.is_err());
    assert!(result.unwrap_err().to_string().contains("Target directory not found"));
}

#[test]
fn test_collect_binaries_no_executables() {
    let temp = TempDir::new().unwrap();
    let project_root = temp.path();

    let target_dir = project_root
        .join("target")
        .join(TargetArch::default_for_platform().as_str())
        .join("release");
    fs::create_dir_all(&target_dir).unwrap();

    // Create non-executable file
    let not_exe = target_dir.join("readme.txt");
    fs::write(&not_exe, "readme").unwrap();

    let config = PackageConfig::default();

    let result = collect_binaries(project_root, &config);
    assert!(result.is_err());
    assert!(result.unwrap_err().to_string().contains("No binaries found"));
}

#[test]
fn test_collect_configs_from_multiple_dirs() {
    let temp = TempDir::new().unwrap();
    let project_root = temp.path();

    // Create config directories
    let config_dir = project_root.join("config");
    let configs_dir = project_root.join("configs");
    let embedded_dir = project_root.join("embedded/configs");

    fs::create_dir_all(&config_dir).unwrap();
    fs::create_dir_all(&configs_dir).unwrap();
    fs::create_dir_all(&embedded_dir).unwrap();

    // Create config files
    fs::write(config_dir.join("robot.json"), "{}").unwrap();
    fs::write(configs_dir.join("sensors.yaml"), "sensors: []").unwrap();
    fs::write(embedded_dir.join("motors.toml"), "[motors]").unwrap();

    let result = collect_configs(project_root);
    assert!(result.is_ok());

    let configs = result.unwrap();
    assert_eq!(configs.len(), 3);

    assert!(configs.iter().any(|c| c.name == "robot.json"));
    assert!(configs.iter().any(|c| c.name == "sensors.yaml"));
    assert!(configs.iter().any(|c| c.name == "motors.toml"));
}

#[test]
fn test_collect_configs_recursive() {
    let temp = TempDir::new().unwrap();
    let project_root = temp.path();

    let config_dir = project_root.join("config");
    let nested_dir = config_dir.join("sensors/cameras");
    fs::create_dir_all(&nested_dir).unwrap();

    fs::write(config_dir.join("main.json"), "{}").unwrap();
    fs::write(config_dir.join("sensors/imu.yaml"), "{}").unwrap();
    fs::write(nested_dir.join("calibration.toml"), "{}").unwrap();

    let result = collect_configs(project_root);
    assert!(result.is_ok());

    let configs = result.unwrap();
    assert_eq!(configs.len(), 3);
}

#[test]
fn test_collect_configs_only_valid_extensions() {
    let temp = TempDir::new().unwrap();
    let project_root = temp.path();

    let config_dir = project_root.join("config");
    fs::create_dir_all(&config_dir).unwrap();

    // Valid extensions
    fs::write(config_dir.join("a.json"), "{}").unwrap();
    fs::write(config_dir.join("b.yaml"), "{}").unwrap();
    fs::write(config_dir.join("c.yml"), "{}").unwrap();
    fs::write(config_dir.join("d.toml"), "{}").unwrap();

    // Invalid extensions (should be skipped)
    fs::write(config_dir.join("e.txt"), "{}").unwrap();
    fs::write(config_dir.join("f.md"), "{}").unwrap();
    fs::write(config_dir.join("g.rs"), "{}").unwrap();

    let result = collect_configs(project_root);
    assert!(result.is_ok());

    let configs = result.unwrap();
    assert_eq!(configs.len(), 4);
}

#[test]
fn test_collect_configs_empty_dirs() {
    let temp = TempDir::new().unwrap();
    let project_root = temp.path();

    // Create empty directories
    fs::create_dir_all(project_root.join("config")).unwrap();
    fs::create_dir_all(project_root.join("configs")).unwrap();

    let result = collect_configs(project_root);
    assert!(result.is_ok());

    let configs = result.unwrap();
    assert_eq!(configs.len(), 0);
}

#[test]
fn test_collect_assets_from_multiple_dirs() {
    let temp = TempDir::new().unwrap();
    let project_root = temp.path();

    // Create asset directories
    let assets_dir = project_root.join("assets");
    let models_dir = project_root.join("models");
    let data_dir = project_root.join("data");
    let calibration_dir = project_root.join("calibration");

    fs::create_dir_all(&assets_dir).unwrap();
    fs::create_dir_all(&models_dir).unwrap();
    fs::create_dir_all(&data_dir).unwrap();
    fs::create_dir_all(&calibration_dir).unwrap();

    // Create asset files
    fs::write(assets_dir.join("logo.png"), "png data").unwrap();
    fs::write(models_dir.join("robot.urdf"), "urdf data").unwrap();
    fs::write(data_dir.join("training.csv"), "csv data").unwrap();
    fs::write(calibration_dir.join("camera.yaml"), "cal data").unwrap();

    let result = collect_assets(project_root);
    assert!(result.is_ok());

    let assets = result.unwrap();
    assert_eq!(assets.len(), 4);
}

#[test]
fn test_collect_assets_recursive() {
    let temp = TempDir::new().unwrap();
    let project_root = temp.path();

    let assets_dir = project_root.join("assets");
    let nested_dir = assets_dir.join("images/icons");
    fs::create_dir_all(&nested_dir).unwrap();

    fs::write(assets_dir.join("main.png"), "png").unwrap();
    fs::write(assets_dir.join("images/logo.svg"), "svg").unwrap();
    fs::write(nested_dir.join("icon.ico"), "ico").unwrap();

    let result = collect_assets(project_root);
    assert!(result.is_ok());

    let assets = result.unwrap();
    assert_eq!(assets.len(), 3);
}

#[test]
fn test_collect_assets_empty() {
    let temp = TempDir::new().unwrap();
    let project_root = temp.path();

    // No asset directories created

    let result = collect_assets(project_root);
    assert!(result.is_ok());

    let assets = result.unwrap();
    assert_eq!(assets.len(), 0);
}

#[test]
fn test_collect_assets_includes_metadata() {
    let temp = TempDir::new().unwrap();
    let project_root = temp.path();

    let assets_dir = project_root.join("assets");
    fs::create_dir_all(&assets_dir).unwrap();

    let asset_file = assets_dir.join("test.png");
    fs::write(&asset_file, "png data content").unwrap();

    let result = collect_assets(project_root);
    assert!(result.is_ok());

    let assets = result.unwrap();
    assert_eq!(assets.len(), 1);

    let asset = &assets[0];
    assert_eq!(asset.name, "test.png");
    assert!(asset.path.starts_with("assets/"));
    assert_eq!(asset.size_bytes, 16); // "png data content" is 16 bytes
    assert!(!asset.checksum.is_empty());
}
