// Exact Rust translation of LibreMetaverse.Tests/RLV/InventoryMapTests.cs.

use libremetaverse_compat_tests::rlv_support::{SampleInventoryTree, guid};
use libremetaverse_rlv::{InventoryMap, RlvAttachmentPoint, RlvWearableType};

fn path_matches(
    clothing_name: Option<&str>,
    hats_name: Option<&str>,
    path: &str,
    skip_private: bool,
    expected: Option<&str>,
) -> bool {
    let mut tree = SampleInventoryTree::build().expect("sample inventory tree");
    if let Some(name) = clothing_name {
        tree.clothing.set_name(name.into());
    }
    if let Some(name) = hats_name {
        tree.hats.set_name(name.into());
    }
    let expected_id = expected.map(|folder| match folder {
        "clothing" => tree.clothing.id(),
        "hats" => tree.hats.id(),
        _ => unreachable!(),
    });
    let map = InventoryMap::new(tree.root, vec![]).unwrap();
    let mut found = None;
    map.try_get_folder_from_path(path.into(), skip_private, &mut found) == expected_id.is_some()
        && found.map(|folder| folder.id()) == expected_id
}

fn folder_ids(
    folders: impl Iterator<Item = libremetaverse_rlv::RlvSharedFolder>,
) -> Vec<libremetaverse_types::compat::Guid> {
    let mut ids: Vec<_> = folders.map(|folder| folder.id()).collect();
    ids.sort_by_key(|id| id.0);
    ids
}

// parity-case: LibreMetaverse.Tests/RLV/InventoryMapTests.cs::InventoryMapTests.TryGetFolderFromPath_Normal::test 97787ac6b51198e317ed391e91125724ce1b580924c40500f81186812b45f625 translated
#[test]
fn try_get_folder_from_normal_path() {
    assert!(path_matches(
        None,
        None,
        "Clothing/Hats",
        true,
        Some("hats")
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/InventoryMapTests.cs::InventoryMapTests.TryGetFolderFromPath_EmptyPath::test 0ffd4f32e285ba94e94f63ac312d3be20bf144b769e8d18af32e15f481042ed7 translated
#[test]
fn try_get_folder_rejects_empty_path() {
    assert!(path_matches(None, None, "", true, None));
}

// parity-case: LibreMetaverse.Tests/RLV/InventoryMapTests.cs::InventoryMapTests.TryGetFolderFromPath_FolderNameContainsForwardSlash::test 90ae520739ea1a2612f491bb110b0870ed4dcb8a983aad9526ed25d8c5cbe687 translated
#[test]
fn try_get_folder_when_name_contains_slash() {
    assert!(path_matches(
        Some("Clo/thing"),
        None,
        "Clo/thing/Hats",
        true,
        Some("hats"),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/InventoryMapTests.cs::InventoryMapTests.TryGetFolderFromPath_FolderNameContainsForwardSlashes::test 8c0ed3c6cc16b916f9df8d25d55618e82299fd1bcd66983f682219c338853028 translated
#[test]
fn try_get_folder_when_names_contain_multiple_slashes() {
    assert!(path_matches(
        Some("/Clo//thing//"),
        Some("//h/ats/"),
        "/Clo//thing/////h/ats/",
        true,
        Some("hats"),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/InventoryMapTests.cs::InventoryMapTests.TryGetFolderFromPath_FolderNameWithSlashPrefix::test 8a86081d6b25ae50b3cf8cdcb1c58c8539264931ab74f395dfc7345ee7f4f861 translated
#[test]
fn try_get_folder_with_slash_prefix() {
    assert!(path_matches(
        Some("/Clothing"),
        None,
        "/Clothing",
        true,
        Some("clothing")
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/InventoryMapTests.cs::InventoryMapTests.TryGetFolderFromPath_FolderNameWithSlashSuffix::test 04afafa569b7a10d6add8c1e9d8f26ac502791f1da2dbdb3e37407e945c87872 translated
#[test]
fn try_get_folder_with_slash_suffix() {
    assert!(path_matches(
        Some("Clothing/"),
        None,
        "Clothing/",
        true,
        Some("clothing")
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/InventoryMapTests.cs::InventoryMapTests.TryGetFolderFromPath_FolderNameWithSlashAffix::test 7eaec38fa6b545f133748b502169fab575308c9c4e60548cfbfd704452bd9761 translated
#[test]
fn try_get_folder_with_slash_affix() {
    assert!(path_matches(
        Some("/Clothing/"),
        None,
        "/Clothing/",
        true,
        Some("clothing"),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/InventoryMapTests.cs::InventoryMapTests.TryGetFolderFromPath_ContendingFoldersWithSlashes::test 96a8189266746a564072122342f36b3321998f3e0cbe0094f3e4c83d29bc53d5 translated
#[test]
fn try_get_folder_prefers_exact_slash_match() {
    let mut tree = SampleInventoryTree::build().unwrap();
    tree.root
        .add_child(
            guid("12345678-0001-4ddd-8ddd-dddddddddddd"),
            "Clothing".into(),
        )
        .unwrap();
    tree.root
        .add_child(
            guid("12345678-0002-4ddd-8ddd-dddddddddddd"),
            "+Clothing///".into(),
        )
        .unwrap();
    tree.root
        .add_child(
            guid("12345678-0003-4ddd-8ddd-dddddddddddd"),
            "+Clothing///".into(),
        )
        .unwrap();
    tree.clothing.set_name("Clothing///".into());
    tree.hats.set_name("//h/ats/".into());
    let expected = tree.hats.id();
    let map = InventoryMap::new(tree.root, vec![]).unwrap();
    let mut found = None;
    assert!(map.try_get_folder_from_path("Clothing//////h/ats/".into(), true, &mut found,));
    assert_eq!(found.unwrap().id(), expected);
}

// parity-case: LibreMetaverse.Tests/RLV/InventoryMapTests.cs::InventoryMapTests.TryGetFolderFromPath_InvalidPath::test 547def910ded67bc7950b211ad929f775d4eff26e351d11612f146b3e863510e translated
#[test]
fn try_get_folder_rejects_invalid_path() {
    assert!(path_matches(None, None, "Clothing/Hats123", true, None));
}

// parity-case: LibreMetaverse.Tests/RLV/InventoryMapTests.cs::InventoryMapTests.TryGetFolderFromPath_IgnoreFolderPrefix::test 5039b79496d215d3921427ab6f65867ba7a4ea5239864f1f5a1e38ea3e5bb426 translated
#[test]
fn try_get_folder_ignores_tilde_and_plus_prefixes() {
    assert!(path_matches(
        Some("~Clothing"),
        Some("+Hats"),
        "Clothing/Hats",
        true,
        Some("hats"),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/InventoryMapTests.cs::InventoryMapTests.TryGetFolderFromPath_FailOnHiddenFolder::test bb0c7e531584128d251b06a4ac23b8cfad16ec4b117be79a53dea3c54aed4510 translated
#[test]
fn try_get_folder_skips_hidden_folder() {
    assert!(path_matches(
        Some(".Clothing"),
        None,
        ".Clothing",
        true,
        None
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/InventoryMapTests.cs::InventoryMapTests.TryGetFolderFromPath_AllowHiddenFolder::test 9217cb5bd9c325fc792303e5ab53cefd71e95a5acf9e5a1601957ba8bf807346 translated
#[test]
fn try_get_folder_allows_hidden_folder() {
    assert!(path_matches(
        Some(".Clothing"),
        None,
        ".Clothing",
        false,
        Some("clothing"),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/InventoryMapTests.cs::InventoryMapTests.FindFoldersContaining_ById::test fc89036c3472dcc2ea4d3fcb55636238e3d2c75b490f46d328b5f91f28301ed0 translated
#[test]
fn find_folders_containing_by_prim_id() {
    let mut tree = SampleInventoryTree::build().unwrap();
    let prim = guid("11111111-0002-4aaa-8aaa-ffffffffffff");
    tree.party_hat
        .set_attached_to(Some(Some(RlvAttachmentPoint::Spine)));
    tree.party_hat.set_attached_prim_id(Some(Some(prim)));
    let expected = vec![tree.hats.id()];
    let map = InventoryMap::new(tree.root, vec![]).unwrap();
    assert_eq!(
        folder_ids(
            map.find_folders_containing(false, Some(Some(prim)), None, None)
                .unwrap()
        ),
        expected
    );
}

// parity-case: LibreMetaverse.Tests/RLV/InventoryMapTests.cs::InventoryMapTests.FindFoldersContaining_ByAttachmentType::test 90b36c29312b87c824c158987e551ca31522cf53a68f5f6bd3eae8fe03e914ed translated
#[test]
fn find_folders_containing_by_attachment_point() {
    let mut tree = SampleInventoryTree::build().unwrap();
    tree.happy_shirt
        .set_attached_to(Some(Some(RlvAttachmentPoint::Chin)));
    tree.happy_shirt
        .set_attached_prim_id(Some(Some(guid("11111111-0001-4aaa-8aaa-ffffffffffff"))));
    tree.fancy_hat
        .set_attached_to(Some(Some(RlvAttachmentPoint::Chin)));
    tree.fancy_hat
        .set_attached_prim_id(Some(Some(guid("11111111-0003-4aaa-8aaa-ffffffffffff"))));
    let mut expected = vec![tree.clothing.id(), tree.hats.id()];
    expected.sort_by_key(|id| id.0);
    let map = InventoryMap::new(tree.root, vec![]).unwrap();
    assert_eq!(
        folder_ids(
            map.find_folders_containing(false, None, Some(Some(RlvAttachmentPoint::Chin)), None,)
                .unwrap()
        ),
        expected
    );
}

// parity-case: LibreMetaverse.Tests/RLV/InventoryMapTests.cs::InventoryMapTests.FindFoldersContaining_ByAttachmentType_SingleResult::test a973bf8087abb0c68cd80d76949fac2735474bf6ccbe743933051f60527d93ce translated
#[test]
fn find_folders_containing_by_attachment_point_single_result() {
    let mut tree = SampleInventoryTree::build().unwrap();
    tree.happy_shirt
        .set_attached_to(Some(Some(RlvAttachmentPoint::Chin)));
    tree.fancy_hat
        .set_attached_to(Some(Some(RlvAttachmentPoint::Chin)));
    let expected = vec![tree.hats.id()];
    let map = InventoryMap::new(tree.root, vec![]).unwrap();
    assert_eq!(
        folder_ids(
            map.find_folders_containing(true, None, Some(Some(RlvAttachmentPoint::Chin)), None,)
                .unwrap()
        ),
        expected
    );
}

// parity-case: LibreMetaverse.Tests/RLV/InventoryMapTests.cs::InventoryMapTests.FindFoldersContaining_ByWearType::test d6712cbeacd6ddf25a1f76dc1eeb10f4c0d8967a7591c249127f9b6387f0df8a translated
#[test]
fn find_folders_containing_by_wearable_type() {
    let mut tree = SampleInventoryTree::build().unwrap();
    tree.happy_shirt
        .set_worn_on(Some(Some(RlvWearableType::Pants)));
    tree.fancy_hat
        .set_worn_on(Some(Some(RlvWearableType::Hair)));
    let expected = vec![tree.clothing.id()];
    let map = InventoryMap::new(tree.root, vec![]).unwrap();
    assert_eq!(
        folder_ids(
            map.find_folders_containing(false, None, None, Some(Some(RlvWearableType::Pants)),)
                .unwrap()
        ),
        expected
    );
}
