// Exact Rust translation of LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs.
// Source SHA-256: 4679b5c685e35e73122b7857b77ea4a5b2b83345ad176aeee32239feb528fd9b

use libremetaverse::marketplace::{
    MarketplaceFolderClassifier as Classifier, MarketplaceFolderRole as Role,
    MarketplaceValidationFlags as Flags,
};
use libremetaverse::{GridClient, Inventory, InventoryFolder, InventoryItem};
use libremetaverse_types::{FolderType, UUID};
use std::sync::Arc;

fn uuid() -> UUID {
    UUID::random().expect("UUID Random")
}

fn folder(id: UUID, name: &str, parent: UUID, preferred: Option<FolderType>) -> InventoryFolder {
    let mut folder = InventoryFolder::new(id).expect("InventoryFolder constructor");
    folder.base.set_name(name.into());
    folder.base.set_parent_uuid(parent);
    if let Some(preferred) = preferred {
        folder.set_preferred_type(preferred);
    }
    folder
}

fn item(id: UUID, name: &str, parent: UUID) -> InventoryItem {
    let mut item = InventoryItem::new_with_uuid(id).expect("InventoryItem constructor");
    item.base.set_name(name.into());
    item.base.set_parent_uuid(parent);
    item
}

struct Fixture {
    client: Arc<GridClient>,
    inventory: Inventory,
    root: UUID,
    listings_root: UUID,
    listing: UUID,
    version: UUID,
    stock: UUID,
    content: UUID,
}

impl Fixture {
    fn new() -> Self {
        let client = Arc::new(GridClient::new().expect("GridClient constructor"));
        let mut inventory = Inventory::new_with_grid_client_uuid(Arc::clone(&client), uuid())
            .expect("Inventory constructor");
        let root = uuid();
        inventory.set_root_folder(Some(folder(root, "My Inventory", UUID::zero(), None)));
        let listings_root = uuid();
        inventory
            .update_node_for(&folder(
                listings_root,
                "Marketplace Listings",
                root,
                Some(FolderType::MarketplaceListings),
            ))
            .unwrap();
        let listing = uuid();
        inventory
            .update_node_for(&folder(listing, "My Listing", listings_root, None))
            .unwrap();
        let version = uuid();
        inventory
            .update_node_for(&folder(
                version,
                "Version 1",
                listing,
                Some(FolderType::MarketplaceVersion),
            ))
            .unwrap();
        let stock = uuid();
        inventory
            .update_node_for(&folder(
                stock,
                "Stock",
                version,
                Some(FolderType::MarketplaceStock),
            ))
            .unwrap();
        let content = uuid();
        inventory
            .update_node_for(&folder(content, "Content", version, None))
            .unwrap();
        inventory
            .update_node_for(&item(uuid(), "Product", version))
            .unwrap();
        Self {
            client,
            inventory,
            root,
            listings_root,
            listing,
            version,
            stock,
            content,
        }
    }
}

impl Drop for Fixture {
    fn drop(&mut self) {
        let _ = self.client.dispose_with_method();
    }
}

fn empty_inventory() -> (Arc<GridClient>, Inventory) {
    let client = Arc::new(GridClient::new().expect("GridClient constructor"));
    let inventory = Inventory::new_with_grid_client_uuid(Arc::clone(&client), uuid())
        .expect("Inventory constructor");
    (client, inventory)
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetRole_ListingsRootFolder_ReturnsListingsRoot::test a3c21e19f21924bffbbafe1199a4b942b545bdb278c3362b2a1ec98b35d672cf translated
#[test]
fn get_role_listings_root_folder_returns_listings_root() {
    let f = Fixture::new();
    assert_eq!(
        Classifier::get_role(f.listings_root, &f.inventory).unwrap(),
        Role::ListingsRoot
    );
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetRole_ListingFolder_ReturnsListing::test c08d860a4506254d9ac831e91894f2fc34d840c100468ee7e39b505690302368 translated
#[test]
fn get_role_listing_folder_returns_listing() {
    let f = Fixture::new();
    assert_eq!(
        Classifier::get_role(f.listing, &f.inventory).unwrap(),
        Role::Listing
    );
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetRole_VersionFolder_ReturnsVersion::test 90d792d6858c6a253b9ddf80d3f5e7244570490fa4ef68c1914b342db05c7aa2 translated
#[test]
fn get_role_version_folder_returns_version() {
    let f = Fixture::new();
    assert_eq!(
        Classifier::get_role(f.version, &f.inventory).unwrap(),
        Role::Version
    );
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetRole_StockFolder_ReturnsStock::test 330a418b3a7b534a60d4e84610af1cd51bb9e6ee77b603c45576d3416972cb75 translated
#[test]
fn get_role_stock_folder_returns_stock() {
    let f = Fixture::new();
    assert_eq!(
        Classifier::get_role(f.stock, &f.inventory).unwrap(),
        Role::Stock
    );
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetRole_ContentFolder_ReturnsContent::test 43f487b7be01edcdbf3b05c5ab8b50b6aec6722ae1e3dae0c44f1dea93fb72bd translated
#[test]
fn get_role_content_folder_returns_content() {
    let f = Fixture::new();
    assert_eq!(
        Classifier::get_role(f.content, &f.inventory).unwrap(),
        Role::Content
    );
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetRole_UnknownUUID_ReturnsNone::test 06958bff71d6248d23b9508a11059ab150cc558cf81efbee86ba04f38446a534 translated
#[test]
fn get_role_unknown_uuid_returns_none() {
    let f = Fixture::new();
    assert_eq!(
        Classifier::get_role(uuid(), &f.inventory).unwrap(),
        Role::None
    );
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetRole_RegularFolderOutsideMarketplace_ReturnsNone::test da102792d7adc232b8af6b43c03196c579474e63cb3139a38cabab957efc27dc translated
#[test]
fn get_role_regular_folder_outside_marketplace_returns_none() {
    let f = Fixture::new();
    let regular = uuid();
    f.inventory
        .update_node_for(&folder(regular, "Objects", f.root, None))
        .unwrap();
    assert_eq!(
        Classifier::get_role(regular, &f.inventory).unwrap(),
        Role::None
    );
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.IsMarketplaceFolder_ListingFolder_ReturnsTrue::test a759b5be1acf6fa627ad1cacbfdee7b5387b66dbd456d1296a9ccca1b9d21d2a translated
#[test]
fn is_marketplace_folder_listing_returns_true() {
    let f = Fixture::new();
    assert!(Classifier::is_marketplace_folder(f.listing, &f.inventory).unwrap());
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.IsMarketplaceFolder_VersionFolder_ReturnsTrue::test bdcf12d6e0c50d7ccb5dc2a219490fd63099726569fd212878bdc8d762b3e769 translated
#[test]
fn is_marketplace_folder_version_returns_true() {
    let f = Fixture::new();
    assert!(Classifier::is_marketplace_folder(f.version, &f.inventory).unwrap());
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.IsMarketplaceFolder_RegularFolderOutsideMarketplace_ReturnsFalse::test bbfeeeb783208c1dd6056f180a3f1d350dff15a7ddf7866fd71d2e9a350db5bf translated
#[test]
fn is_marketplace_folder_regular_returns_false() {
    let f = Fixture::new();
    let regular = uuid();
    f.inventory
        .update_node_for(&folder(regular, "Objects", f.root, None))
        .unwrap();
    assert!(!Classifier::is_marketplace_folder(regular, &f.inventory).unwrap());
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.IsMarketplaceFolder_UnknownUUID_ReturnsFalse::test 33b914bdccd2c7b1f134bf9be6fa3baf55f93801f0de86e64a7aa0d376837706 translated
#[test]
fn is_marketplace_folder_unknown_returns_false() {
    let f = Fixture::new();
    assert!(!Classifier::is_marketplace_folder(uuid(), &f.inventory).unwrap());
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetListingsRoot_HasMarketplaceListingsFolder_ReturnsItsUUID::test bae93f760f1a2cc97608a487b98d44e40a552a3698b71f27014ae12bc2ab08d0 translated
#[test]
fn get_listings_root_returns_marketplace_folder() {
    let f = Fixture::new();
    assert_eq!(
        Classifier::get_listings_root(&f.inventory).unwrap(),
        f.listings_root
    );
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetListingsRoot_NoMarketplaceListingsFolder_ReturnsZero::test 68eaa338235b510b202dbf067902999b38f52aef0040edb3d7539ae24f884b25 translated
#[test]
fn get_listings_root_without_marketplace_folder_returns_zero() {
    let (client, mut inventory) = empty_inventory();
    let root = uuid();
    inventory.set_root_folder(Some(folder(root, "My Inventory", UUID::zero(), None)));
    inventory
        .update_node_for(&folder(uuid(), "Objects", root, None))
        .unwrap();
    assert_eq!(
        Classifier::get_listings_root(&inventory).unwrap(),
        UUID::zero()
    );
    let _ = client.dispose_with_method();
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetListingsRoot_NoRootFolder_ReturnsZero::test e23c43ed9179d62b38e290a47711bae64fd9c9ff7c7445b5ad065187af740367 translated
#[test]
fn get_listings_root_without_root_returns_zero() {
    let (client, inventory) = empty_inventory();
    assert_eq!(
        Classifier::get_listings_root(&inventory).unwrap(),
        UUID::zero()
    );
    let _ = client.dispose_with_method();
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetListingFolder_CalledOnListingFolder_ReturnsSelf::test 6761009b4891640dfeb44dd361f02cefa968df503ebca6d8b5f3defe86ec803f translated
#[test]
fn get_listing_folder_on_listing_returns_self() {
    let f = Fixture::new();
    assert_eq!(
        Classifier::get_listing_folder(f.listing, &f.inventory).unwrap(),
        f.listing
    );
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetListingFolder_CalledOnVersionFolder_ReturnsListingFolder::test ae3e668b09cda08f558ad654132bda1acdaa3bce08a832b08465ad9302cd6773 translated
#[test]
fn get_listing_folder_on_version_returns_listing() {
    let f = Fixture::new();
    assert_eq!(
        Classifier::get_listing_folder(f.version, &f.inventory).unwrap(),
        f.listing
    );
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetListingFolder_CalledOnStockFolder_ReturnsListingFolder::test 2af63c64992155da9343b273ec7e80984bf3233d5391625e61317d60e976bccf translated
#[test]
fn get_listing_folder_on_stock_returns_listing() {
    let f = Fixture::new();
    assert_eq!(
        Classifier::get_listing_folder(f.stock, &f.inventory).unwrap(),
        f.listing
    );
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetListingFolder_CalledOnListingsRoot_ReturnsZero::test 8921bd854b48d34916e3329640fb758e6d5ac6bc099f3b79dbdbde63529886db translated
#[test]
fn get_listing_folder_on_root_returns_zero() {
    let f = Fixture::new();
    assert_eq!(
        Classifier::get_listing_folder(f.listings_root, &f.inventory).unwrap(),
        UUID::zero()
    );
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetListingFolder_CalledOnUnknownFolder_ReturnsZero::test db0ccbbbcd06e502abdb1db6ebc351c7c202c68ee01e0e17143a0f28dec1ec12 translated
#[test]
fn get_listing_folder_on_unknown_returns_zero() {
    let f = Fixture::new();
    assert_eq!(
        Classifier::get_listing_folder(uuid(), &f.inventory).unwrap(),
        UUID::zero()
    );
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetVersionFolder_ListingHasVersionFolder_ReturnsVersionUUID::test a5f816a37538eed4ae5eedfbe5c357c8aa296bad0c734de50ce8f286d8785084 translated
#[test]
fn get_version_folder_returns_version() {
    let f = Fixture::new();
    assert_eq!(
        Classifier::get_version_folder(f.listing, &f.inventory).unwrap(),
        f.version
    );
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetVersionFolder_ListingHasNoVersionFolder_ReturnsZero::test f35acb3a9edd8e0554d2e1c6c87723804a447060135a29fbd39cd07e7e092467 translated
#[test]
fn get_version_folder_without_version_returns_zero() {
    let f = Fixture::new();
    let listing = uuid();
    f.inventory
        .update_node_for(&folder(listing, "Empty Listing", f.listings_root, None))
        .unwrap();
    assert_eq!(
        Classifier::get_version_folder(listing, &f.inventory).unwrap(),
        UUID::zero()
    );
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetStockCount_VersionFolderWithOneItem_ReturnsOne::test d6dadccb9d8c19814038262f376b09ce05201a165634721ad1fd70213a53462d translated
#[test]
fn get_stock_count_one_item_returns_one() {
    let f = Fixture::new();
    assert_eq!(
        Classifier::get_stock_count(f.version, &f.inventory).unwrap(),
        1
    );
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetStockCount_VersionFolderWithOnlySubfolders_ReturnsZero::test 27dc9e58e342db51b5a400bf9785a77c62fe8011b4cabba3bbf69a7ea6aec6c8 translated
#[test]
fn get_stock_count_only_subfolders_returns_zero() {
    let f = Fixture::new();
    let version = uuid();
    f.inventory
        .update_node_for(&folder(
            version,
            "Version Empty",
            f.listing,
            Some(FolderType::MarketplaceVersion),
        ))
        .unwrap();
    f.inventory
        .update_node_for(&folder(uuid(), "SubFolder", version, None))
        .unwrap();
    assert_eq!(
        Classifier::get_stock_count(version, &f.inventory).unwrap(),
        0
    );
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetStockCount_ZeroUUID_ReturnsZero::test bd1fdd59481aae5b8e443a55dc8300ac50956c5c4ded3289dbbc4d863a7a9042 translated
#[test]
fn get_stock_count_zero_uuid_returns_zero() {
    let f = Fixture::new();
    assert_eq!(
        Classifier::get_stock_count(UUID::zero(), &f.inventory).unwrap(),
        0
    );
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetStockCount_MultipleItems_ReturnsCorrectCount::test d0ff960d09e791672000e5c63423181e70fc28437eb1cb26538fa175ce4d75f1 translated
#[test]
fn get_stock_count_multiple_items_returns_three() {
    let f = Fixture::new();
    let version = uuid();
    f.inventory
        .update_node_for(&folder(
            version,
            "Version Multi",
            f.listing,
            Some(FolderType::MarketplaceVersion),
        ))
        .unwrap();
    for name in ["Item A", "Item B", "Item C"] {
        f.inventory
            .update_node_for(&item(uuid(), name, version))
            .unwrap();
    }
    assert_eq!(
        Classifier::get_stock_count(version, &f.inventory).unwrap(),
        3
    );
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetAllListingFolderIds_SingleListingFolder_ReturnsOne::test bc49aaa674206f0b4b5f625260090ec2f7ee59ad053881141890271f2f9369e1 translated
#[test]
fn get_all_listing_folder_ids_returns_single_listing() {
    let f = Fixture::new();
    let ids = Classifier::get_all_listing_folder_ids(&f.inventory).unwrap();
    assert_eq!(ids.len(), 1);
    assert!(ids.contains(&f.listing));
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetAllListingFolderIds_TwoListingFolders_ReturnsBoth::test 4cf174ea092dbbd6741a9cec7ab0caca5da7e41ab5283e88c427d2f7057dd9ff translated
#[test]
fn get_all_listing_folder_ids_returns_both() {
    let f = Fixture::new();
    let second = uuid();
    f.inventory
        .update_node_for(&folder(second, "Second Listing", f.listings_root, None))
        .unwrap();
    let ids = Classifier::get_all_listing_folder_ids(&f.inventory).unwrap();
    assert_eq!(ids.len(), 2);
    assert!(ids.contains(&f.listing));
    assert!(ids.contains(&second));
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.GetAllListingFolderIds_NoMarketplaceRoot_ReturnsEmpty::test fedc2c585ea3fdaae8bf14eb56ccbdcfbb0d2d8a790bb7137a6dd0a02a6ef2c8 translated
#[test]
fn get_all_listing_folder_ids_without_marketplace_root_returns_empty() {
    let (client, inventory) = empty_inventory();
    assert!(
        Classifier::get_all_listing_folder_ids(&inventory)
            .unwrap()
            .is_empty()
    );
    let _ = client.dispose_with_method();
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.ValidateListing_WellFormedListing_ReturnsValid::test b15ac0ff131da6764651ccb24a87771f8e9f955c389e813e9c1b50865b472b16 translated
#[test]
fn validate_listing_well_formed_returns_valid() {
    let f = Fixture::new();
    assert_eq!(
        Classifier::validate_listing(f.listing, &f.inventory).unwrap(),
        Flags::VALID
    );
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.ValidateListing_NoVersionFolder_ReturnsMissingVersionFolder::test 5becd6939ba410c0e6c7cfb42234b1c513a6e69f827080b7f4301da3da6dfab8 translated
#[test]
fn validate_listing_without_version_returns_missing_version() {
    let f = Fixture::new();
    let listing = uuid();
    f.inventory
        .update_node_for(&folder(
            listing,
            "No Version Listing",
            f.listings_root,
            None,
        ))
        .unwrap();
    assert_eq!(
        Classifier::validate_listing(listing, &f.inventory).unwrap(),
        Flags::MISSING_VERSION_FOLDER
    );
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.ValidateListing_MultipleVersionFolders_ReturnsMultipleVersionFolders::test f10ec95540212b4c47035928de4472cdd268cb4946dc1b73f2e65cff32881142 translated
#[test]
fn validate_listing_multiple_versions_returns_multiple_versions() {
    let f = Fixture::new();
    let listing = uuid();
    f.inventory
        .update_node_for(&folder(
            listing,
            "Multi-Version Listing",
            f.listings_root,
            None,
        ))
        .unwrap();
    for name in ["Version A", "Version B"] {
        f.inventory
            .update_node_for(&folder(
                uuid(),
                name,
                listing,
                Some(FolderType::MarketplaceVersion),
            ))
            .unwrap();
    }
    assert_eq!(
        Classifier::validate_listing(listing, &f.inventory).unwrap(),
        Flags::MULTIPLE_VERSION_FOLDERS
    );
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.ValidateListing_EmptyVersionFolder_ReturnsEmptyListing::test 1443157d7ab29ffe2ea45f38ea43e121262abb89f2264e141c750c7e7689e2c8 translated
#[test]
fn validate_listing_empty_version_returns_empty_listing() {
    let f = Fixture::new();
    let listing = uuid();
    f.inventory
        .update_node_for(&folder(listing, "Empty Listing", f.listings_root, None))
        .unwrap();
    f.inventory
        .update_node_for(&folder(
            uuid(),
            "Version 1",
            listing,
            Some(FolderType::MarketplaceVersion),
        ))
        .unwrap();
    assert_eq!(
        Classifier::validate_listing(listing, &f.inventory).unwrap(),
        Flags::EMPTY_LISTING
    );
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.ValidateListing_UnknownUUID_ReturnsInvalidStructure::test 9ee13dc32219883b7847c2f71de03a42e80681e5916e86d6723bda11fd8223ab translated
#[test]
fn validate_listing_unknown_returns_invalid_structure() {
    let f = Fixture::new();
    assert_eq!(
        Classifier::validate_listing(uuid(), &f.inventory).unwrap(),
        Flags::INVALID_STRUCTURE
    );
}

// parity-case: LibreMetaverse.Tests/MarketplaceFolderClassifierTests.cs::MarketplaceFolderClassifierTests.ValidateListing_ValidFlags_AreBitmaskComposable::test dcdb7dfcdbcf003c4698e7a2a580046db610a1b8ab3ed7ad7cbe8e2aa36eace4 translated
#[test]
fn validation_flags_are_bitmask_composable() {
    let combined = Flags(Flags::MISSING_VERSION_FOLDER.0 | Flags::EMPTY_LISTING.0);
    assert_ne!(combined.0 & Flags::MISSING_VERSION_FOLDER.0, 0);
    assert_ne!(combined.0 & Flags::EMPTY_LISTING.0, 0);
    assert_eq!(combined.0 & Flags::MULTIPLE_VERSION_FOLDERS.0, 0);
}
