//! Tests for workspace domain entity

use governor_core::domain::version::SemanticVersion;
use governor_core::domain::workspace::{
    GovernorMetadata, OwnersConfig, WorkingTreeStatus, WorkspaceMetadata, WorkspaceMetadataExtra,
};
use std::path::PathBuf;

#[test]
fn test_workspace_metadata_new() {
    let root = PathBuf::from("/test");
    let meta = WorkspaceMetadata::new(root.clone());
    assert_eq!(meta.root, root);
    assert!(meta.members.is_empty());
    assert!(meta.exclude.is_empty());
    assert!(meta.version.is_none());
    assert!(meta.resolver.is_none());
}

#[test]
fn test_workspace_metadata_is_shared_version() {
    let mut meta = WorkspaceMetadata::new(PathBuf::from("/test"));
    assert!(!meta.is_shared_version());
    meta.version = Some(SemanticVersion::parse("1.0.0").unwrap());
    assert!(meta.is_shared_version());
}

#[test]
fn test_workspace_metadata_member_paths() {
    let mut meta = WorkspaceMetadata::new(PathBuf::from("/workspace"));
    meta.members.push("crate1".to_string());
    meta.members.push("./crate2".to_string());
    meta.members.push("/absolute/crate3".to_string());

    let paths = meta.member_paths();
    assert_eq!(paths.len(), 3);
    assert!(paths[0].ends_with("crate1"));
    assert!(paths[1].ends_with("crate2"));
    assert!(paths[2].ends_with("crate3"));
}

#[test]
fn test_workspace_metadata_member_paths_absolute() {
    let mut meta = WorkspaceMetadata::new(PathBuf::from("/workspace"));
    meta.members.push("crate1".to_string());

    let paths = meta.member_paths();
    assert_eq!(paths[0], PathBuf::from("/workspace/crate1"));
}

#[test]
fn test_workspace_metadata_is_excluded() {
    let mut meta = WorkspaceMetadata::new(PathBuf::from("/test"));
    meta.exclude.push("target".to_string());
    meta.exclude.push("build".to_string());

    assert!(meta.is_excluded(&PathBuf::from("/test/target")));
    assert!(meta.is_excluded(&PathBuf::from("/test/build")));
    assert!(!meta.is_excluded(&PathBuf::from("/test/src")));
    assert!(!meta.is_excluded(&PathBuf::from("/test/other")));
}

#[test]
fn test_workspace_metadata_is_excluded_with_dot_slash() {
    let mut meta = WorkspaceMetadata::new(PathBuf::from("/test"));
    meta.exclude.push("./target".to_string());

    // ./target is relative to root, so /test/./target should be excluded
    // Note: PathBuf normalizes paths, so this may not work as expected
    // The important thing is that relative paths work correctly
    assert!(!meta.is_excluded(&PathBuf::from("/test/target")));
}

#[test]
fn test_workspace_metadata_is_excluded_absolute() {
    let mut meta = WorkspaceMetadata::new(PathBuf::from("/test"));
    meta.exclude.push("/test/target".to_string());

    assert!(meta.is_excluded(&PathBuf::from("/test/target")));
}

#[test]
fn test_working_tree_status_clean() {
    let status = WorkingTreeStatus::clean();
    assert!(status.is_clean());
    assert!(!status.has_changes);
    assert!(status.modified.is_empty());
    assert!(status.added.is_empty());
    assert!(status.deleted.is_empty());
    assert!(status.untracked.is_empty());
}

#[test]
fn test_working_tree_status_with_changes() {
    let status = WorkingTreeStatus {
        has_changes: true,
        modified: vec!["file1.rs".to_string()],
        added: vec!["file2.rs".to_string()],
        deleted: vec!["file3.rs".to_string()],
        untracked: vec!["file4.rs".to_string()],
    };

    assert!(!status.is_clean());
    assert!(status.has_changes);
    assert_eq!(status.modified.len(), 1);
    assert_eq!(status.added.len(), 1);
    assert_eq!(status.deleted.len(), 1);
    assert_eq!(status.untracked.len(), 1);
}

#[test]
fn test_working_tree_status_all_changes() {
    let mut status = WorkingTreeStatus::clean();
    status.modified.push("mod.rs".to_string());
    status.added.push("new.rs".to_string());
    status.deleted.push("old.rs".to_string());

    let changes = status.all_changes();
    assert_eq!(changes.len(), 3);
}

#[test]
fn test_working_tree_status_total_changes() {
    let mut status = WorkingTreeStatus::clean();
    status.modified.push("a.rs".to_string());
    status.modified.push("b.rs".to_string());
    status.added.push("c.rs".to_string());
    status.deleted.push("d.rs".to_string());

    assert_eq!(status.total_changes(), 4);
}

#[test]
fn test_workspace_metadata_extra_default() {
    let extra = WorkspaceMetadataExtra::default();
    assert!(extra.governor.is_none());
}

#[test]
fn test_owners_config_default() {
    let config = OwnersConfig::default();
    assert!(config.users.is_empty());
    assert!(config.groups.is_empty());
}

#[test]
fn test_owners_config_with_users() {
    let config = OwnersConfig {
        users: vec!["user1".to_string(), "user2".to_string()],
        groups: vec!["group1".to_string()],
    };
    assert_eq!(config.users.len(), 2);
    assert_eq!(config.groups.len(), 1);
}

#[test]
fn test_governors_metadata_with_owners() {
    let owners = OwnersConfig {
        users: vec!["admin".to_string()],
        groups: vec![],
    };
    let meta = GovernorMetadata {
        owners: Some(owners),
    };
    assert!(meta.owners.is_some());
}

#[test]
fn test_workspace_metadata_with_resolver() {
    let mut meta = WorkspaceMetadata::new(PathBuf::from("/test"));
    meta.resolver = Some("2".to_string());
    assert_eq!(meta.resolver, Some("2".to_string()));
}
