#![allow(clippy::too_many_lines)]

// Exact BSD-3-Clause fixture provenance at LibreMetaverse
// 2aa70bb68513b39795da5d13c88f31b86e85a3ba (see LICENSE.md):
// AppearanceLiveTests.cs              9f4ec256d11290cbad70e209d2f9043d4c6afb18255796c09c4cb26415f971a6
// AppearanceManagerTests.cs           b4f6e0ffeb3c1719df2125800932ddc27c181da3d3a38020c1e74862d9af9920
// AppearanceTests.cs                  3d1f6a43127ee2c678284fd68572c0e81d1825766cc88e17136111fe8b3b7a86
// BatchLinkTests.cs                   092ccb2686769d27f6a8072114654e91308f88edd94223a77af92f56426ee1ee
// CurrentOutfitFolderTests.cs         07177fff27fbee0d0714286c652253985add3a9401a50e8d7a829ce6c2b3bea9
// MisclassifiedLinkRegressionTests.cs f1e2a14f9671d6af084d72cc5280daf372985bf1b08c75e5989d9c8df0932a64

use libremetaverse::{
    AppearanceManager, AvatarTextureIndex, BakeType, GridClient, GridClientBakingTextureProvider,
    IBakingTextureProvider,
};
use libremetaverse_types::{AssetType, UUID, WearableType};
use std::any::TypeId;
use std::sync::Arc;

struct RecordingTextureProvider;
impl IBakingTextureProvider for RecordingTextureProvider {}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.WearableTypeToAssetType_BodypartsAndClothing_ReturnsExpected::test 6d0f0d09a6da8121ba828159a75ca1c1ce54329ec48060a6786aa8f807c13ec0 translated
#[test]
fn appearance_manager_wearable_type_to_asset_type_returns_expected() {
    for wearable in [
        WearableType::Shape,
        WearableType::Skin,
        WearableType::Hair,
        WearableType::Eyes,
    ] {
        assert_eq!(
            AppearanceManager::wearable_type_to_asset_type(wearable)
                .expect("WearableTypeToAssetType"),
            AssetType::Bodypart
        );
    }
    for wearable in [
        WearableType::Shirt,
        WearableType::Pants,
        WearableType::Shoes,
        WearableType::Tattoo,
    ] {
        assert_eq!(
            AppearanceManager::wearable_type_to_asset_type(wearable)
                .expect("WearableTypeToAssetType"),
            AssetType::Clothing
        );
    }
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeIndexToTextureIndex_Matches_BakeTypeToAgentTextureIndex::test c559ea49314b157605346243713fd10480a10f9a97daffd9cac8acccbd39612b translated
#[test]
fn appearance_manager_bake_index_matches_agent_texture_index() {
    let indices = AppearanceManager::bake_index_to_texture_index();
    for baked_index in 0..AppearanceManager::BAKED_TEXTURE_COUNT {
        let expected = AppearanceManager::bake_type_to_agent_texture_index(BakeType(baked_index))
            .expect("BakeTypeToAgentTextureIndex") as i32;
        assert_eq!(
            i32::from(indices[usize::try_from(baked_index).unwrap()]),
            expected
        );
    }
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeIndex_ArrayLength_Equals_BAKED_TEXTURE_COUNT::test b661ca888df5b7fd41e51dfd298fb6d5e30a3a848102006c109b9dc4fc0e165c translated
#[test]
fn appearance_manager_bake_index_array_length_matches_count() {
    assert_eq!(
        AppearanceManager::bake_index_to_texture_index().len(),
        AppearanceManager::BAKED_TEXTURE_COUNT as usize
    );
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_Head_IncludesExpectedSlots::test 44e03523f0e3f6691e527e014bfc38d9ca57c1d310e7cf2c6d7ae8a429a31e1d translated
#[test]
fn appearance_manager_head_bake_includes_expected_slots() {
    let textures =
        AppearanceManager::bake_type_to_textures(BakeType::Head).expect("BakeTypeToTextures");
    assert!(textures.contains(&AvatarTextureIndex::HeadBodypaint));
    assert!(textures.contains(&AvatarTextureIndex::HeadTattoo));
    assert!(textures.contains(&AvatarTextureIndex::Hair));
    assert!(textures.contains(&AvatarTextureIndex::HeadAlpha));
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_UpperBody_IncludesExpectedSlots::test fb7650becf6c01c1e7ffd731ac7b6c013957467110c7ed8d5bef9788ee9a5a16 translated
#[test]
fn appearance_manager_upper_body_bake_includes_expected_slots() {
    let textures =
        AppearanceManager::bake_type_to_textures(BakeType::UpperBody).expect("BakeTypeToTextures");
    assert!(textures.contains(&AvatarTextureIndex::UpperBodypaint));
    assert!(textures.contains(&AvatarTextureIndex::UpperTattoo));
    assert!(textures.contains(&AvatarTextureIndex::UpperShirt));
    assert!(textures.contains(&AvatarTextureIndex::UpperAlpha));
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_LowerBody_Eyes_Skirt_Hair_IncludeExpected::test fad2baeca74cab39305c8f599c5de5777b55c329738db5c55f58a09366870374 translated
#[test]
fn appearance_manager_lower_eyes_skirt_hair_bakes_include_expected_slots() {
    let lower =
        AppearanceManager::bake_type_to_textures(BakeType::LowerBody).expect("BakeTypeToTextures");
    assert!(lower.contains(&AvatarTextureIndex::LowerPants));
    assert!(lower.contains(&AvatarTextureIndex::LowerShoes));
    let eyes =
        AppearanceManager::bake_type_to_textures(BakeType::Eyes).expect("BakeTypeToTextures");
    assert!(eyes.contains(&AvatarTextureIndex::EyesIris));
    assert!(eyes.contains(&AvatarTextureIndex::EyesAlpha));
    let skirt =
        AppearanceManager::bake_type_to_textures(BakeType::Skirt).expect("BakeTypeToTextures");
    assert!(skirt.contains(&AvatarTextureIndex::Skirt));
    let hair =
        AppearanceManager::bake_type_to_textures(BakeType::Hair).expect("BakeTypeToTextures");
    assert!(hair.contains(&AvatarTextureIndex::Hair));
    assert!(hair.contains(&AvatarTextureIndex::HairAlpha));
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.MorphLayerForBakeType_ReturnsExpectedIndices::test 3051ccea9adec4f9ef824ce967c74e7708b62a2264b98ae22340eb8d1927c44f translated
#[test]
fn appearance_manager_morph_layer_returns_expected_indices() {
    assert_eq!(
        AppearanceManager::morph_layer_for_bake_type(BakeType::Head)
            .expect("MorphLayerForBakeType"),
        AvatarTextureIndex::Hair
    );
    assert_eq!(
        AppearanceManager::morph_layer_for_bake_type(BakeType::UpperBody)
            .expect("MorphLayerForBakeType"),
        AvatarTextureIndex::UpperShirt
    );
    assert_eq!(
        AppearanceManager::morph_layer_for_bake_type(BakeType::LowerBody)
            .expect("MorphLayerForBakeType"),
        AvatarTextureIndex::LowerPants
    );
    assert_eq!(
        AppearanceManager::morph_layer_for_bake_type(BakeType::Skirt)
            .expect("MorphLayerForBakeType"),
        AvatarTextureIndex::Skirt
    );
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.WearableBakeMap_HasExpectedDimensions_And_FirstLayerContainsShapeAndSkin::test 42c072449d1819c956035d1f0290b90acc1e62b4e1e6309590d0cb6cb41bb375 translated
#[test]
fn appearance_manager_wearable_bake_map_has_expected_dimensions_and_first_layer() {
    let map = AppearanceManager::wearable_bake_map();
    assert_eq!(map.len(), AppearanceManager::BAKED_TEXTURE_COUNT as usize);
    assert!(map[0].contains(&WearableType::Shape));
    assert!(map[0].contains(&WearableType::Skin));
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToAgentTextureIndex_ReturnsUnknown_ForInvalid::test bc79d717462f06d498050182b6d116cea73f3bc173853f6d7894e3de88b70432 translated
#[test]
fn appearance_manager_invalid_bake_type_returns_unknown_texture_index() {
    assert_eq!(
        AppearanceManager::bake_type_to_agent_texture_index(BakeType(-999))
            .expect("BakeTypeToAgentTextureIndex"),
        AvatarTextureIndex::Unknown
    );
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BAKED_TEXTURE_COUNT_Is11::test 9d34c333f7e143d25964553dedaa1bae79c20e1aead331626f449a3894e31e5b translated
#[test]
fn appearance_manager_baked_texture_count_is_eleven() {
    assert_eq!(AppearanceManager::BAKED_TEXTURE_COUNT, 11);
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakedTextureHash_Has11Entries::test 7b26852cc8c1d7257e3e5f6f936f3870e0a06845f5809cc3000d67eb7590a49e translated
#[test]
fn appearance_manager_baked_texture_hash_has_eleven_entries() {
    assert_eq!(AppearanceManager::baked_texture_hash().len(), 11);
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakedTextureHash_AllEntriesNonZero::test dc7a4010cfc2f975c0624bc5fbceed414297b1139f734d79786b2bb20d943553 translated
#[test]
fn appearance_manager_baked_texture_hash_entries_are_nonzero() {
    let zero = UUID::zero();
    for hash in AppearanceManager::baked_texture_hash() {
        assert_ne!(hash, zero);
    }
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.WearableBakeMap_Has11Rows::test db6016bcdc90f21095f0723556489ec174603783dc40805b56123e25d7a83312 translated
#[test]
fn appearance_manager_wearable_bake_map_has_eleven_rows() {
    assert_eq!(AppearanceManager::wearable_bake_map().len(), 11);
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.WearableBakeMap_ExtendedRows_AreAllInvalid::test 9b36d72c16980dc3780137dda47afa9cd79ef3a2be0d8ccb20723e1e1ac67f41 translated
#[test]
fn appearance_manager_extended_wearable_bake_rows_are_invalid() {
    let map = AppearanceManager::wearable_bake_map();
    for row in &map[6..11] {
        for wearable in row {
            assert_eq!(*wearable, WearableType::Invalid);
        }
    }
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeIndexToTextureIndex_ExtendedBakes_MatchAgentTextureIndex::test c331e94827930443aa3ca327374ec562fc9a294fce3304d7cba9f7e7859e43d3 translated
#[test]
fn appearance_manager_extended_bake_indices_match_agent_texture_indices() {
    let indices = AppearanceManager::bake_index_to_texture_index();
    for index in 6..AppearanceManager::BAKED_TEXTURE_COUNT {
        let expected = AppearanceManager::bake_type_to_agent_texture_index(BakeType(index))
            .expect("BakeTypeToAgentTextureIndex") as i32;
        assert_eq!(
            i32::from(indices[usize::try_from(index).unwrap()]),
            expected
        );
    }
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_Head_IncludesUniversalTattoo::test 578423fedcd5e4ed4e53b3d7d80c24a9c8ce30b35db13a09bdf8f47312b7da87 translated
#[test]
fn appearance_manager_head_bake_includes_universal_tattoo() {
    let textures =
        AppearanceManager::bake_type_to_textures(BakeType::Head).expect("BakeTypeToTextures");
    assert!(textures.contains(&AvatarTextureIndex::HeadUniversalTattoo));
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_UpperBody_IncludesUniversalTattoo::test 6d95989bf8380348714a0ba9ae4c04a85ea64ae47ffc94aaa841ce64810abebd translated
#[test]
fn appearance_manager_upper_body_bake_includes_universal_tattoo() {
    let textures =
        AppearanceManager::bake_type_to_textures(BakeType::UpperBody).expect("BakeTypeToTextures");
    assert!(textures.contains(&AvatarTextureIndex::UpperUniversalTattoo));
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_LowerBody_IncludesUniversalTattoo::test a21524c69c8bd32610e8cfa405a3cb3eae0a48add62d40a281f9311ebe739ffb translated
#[test]
fn appearance_manager_lower_body_bake_includes_universal_tattoo() {
    let textures =
        AppearanceManager::bake_type_to_textures(BakeType::LowerBody).expect("BakeTypeToTextures");
    assert!(textures.contains(&AvatarTextureIndex::LowerUniversalTattoo));
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_Eyes_IncludesTattoo::test 606c6bad5f911d070c5d8126004d5a1e1dfd4887a115e8dd1f69f2d36bf53ac6 translated
#[test]
fn appearance_manager_eyes_bake_includes_tattoo() {
    let textures =
        AppearanceManager::bake_type_to_textures(BakeType::Eyes).expect("BakeTypeToTextures");
    assert!(textures.contains(&AvatarTextureIndex::EyesTattoo));
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_Skirt_IncludesTattoo::test 7a38a59414bc74221bfbdc8889ef60714a4c6b4f26c9bd47e8f41bfa5a5da811 translated
#[test]
fn appearance_manager_skirt_bake_includes_tattoo() {
    let textures =
        AppearanceManager::bake_type_to_textures(BakeType::Skirt).expect("BakeTypeToTextures");
    assert!(textures.contains(&AvatarTextureIndex::SkirtTattoo));
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_Hair_IncludesHairTattoo::test c9a8c6a0dce259103fb466eb571785eb3c9395c8fa144f1a02d42ae73c0d8253 translated
#[test]
fn appearance_manager_hair_bake_includes_hair_tattoo() {
    let textures =
        AppearanceManager::bake_type_to_textures(BakeType::Hair).expect("BakeTypeToTextures");
    assert!(textures.contains(&AvatarTextureIndex::HairTattoo));
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_BakedLeftArm_ContainsLeftArmTattoo::test bbbb01e8e009489967b36f22d83c6f326e295b9aabcbb58c00d82bab4e803f66 translated
#[test]
fn appearance_manager_left_arm_bake_contains_only_left_arm_tattoo() {
    let textures = AppearanceManager::bake_type_to_textures(BakeType::BakedLeftArm)
        .expect("BakeTypeToTextures");
    assert!(textures.contains(&AvatarTextureIndex::LeftArmTattoo));
    assert_eq!(textures.len(), 1);
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_BakedLeftLeg_ContainsLeftLegTattoo::test 9d2a25a6ed9cd5a99c206a0fc6d66389959fc5e30dd036302a73fb0b500880b5 translated
#[test]
fn appearance_manager_left_leg_bake_contains_left_leg_tattoo() {
    let textures = AppearanceManager::bake_type_to_textures(BakeType::BakedLeftLeg)
        .expect("BakeTypeToTextures");
    assert!(textures.contains(&AvatarTextureIndex::LeftLegTattoo));
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_BakedAux1_ContainsAux1Tattoo::test b9b0b56e1d4d552f2b43e5e570f3fc1a1f8651d8e77055fabcc35b37b5a9f21d translated
#[test]
fn appearance_manager_aux1_bake_contains_aux1_tattoo() {
    let textures =
        AppearanceManager::bake_type_to_textures(BakeType::BakedAux1).expect("BakeTypeToTextures");
    assert!(textures.contains(&AvatarTextureIndex::Aux1Tattoo));
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_BakedAux2_ContainsAux2Tattoo::test 6be4bdbf667cadd673afbb1dafa8f4c1844ac9c34464fe5bbbc62f8ebe705b4d translated
#[test]
fn appearance_manager_aux2_bake_contains_aux2_tattoo() {
    let textures =
        AppearanceManager::bake_type_to_textures(BakeType::BakedAux2).expect("BakeTypeToTextures");
    assert!(textures.contains(&AvatarTextureIndex::Aux2Tattoo));
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.BakeTypeToTextures_BakedAux3_ContainsAux3Tattoo::test 5d7530c72824f8baf6044fb122d224da61fff1b6cfbf3114ac2f24b1418675d2 translated
#[test]
fn appearance_manager_aux3_bake_contains_aux3_tattoo() {
    let textures =
        AppearanceManager::bake_type_to_textures(BakeType::BakedAux3).expect("BakeTypeToTextures");
    assert!(textures.contains(&AvatarTextureIndex::Aux3Tattoo));
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.MorphLayerForBakeType_ExtendedBakes_ReturnTattooSlots::test 7f05dc0b59adce3b46c82f7415e91e4c945963d918ad6ad1f0e5c9569ee2220e translated
#[test]
fn appearance_manager_extended_morph_layers_return_tattoo_slots() {
    for (bake, texture) in [
        (BakeType::BakedLeftArm, AvatarTextureIndex::LeftArmTattoo),
        (BakeType::BakedLeftLeg, AvatarTextureIndex::LeftLegTattoo),
        (BakeType::BakedAux1, AvatarTextureIndex::Aux1Tattoo),
        (BakeType::BakedAux2, AvatarTextureIndex::Aux2Tattoo),
        (BakeType::BakedAux3, AvatarTextureIndex::Aux3Tattoo),
    ] {
        assert_eq!(
            AppearanceManager::morph_layer_for_bake_type(bake).expect("MorphLayerForBakeType"),
            texture
        );
    }
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.TextureProvider_Default_IsGridClientBakingTextureProvider::test afdfbf516988c4ba35dfdb42158218a4815e1b8d146ef958225cf0fe73dfc3a9 translated
#[test]
fn appearance_manager_default_texture_provider_is_grid_client_provider() {
    let client = GridClient::new().expect("GridClient constructor");
    let provider = client.appearance().texture_provider();
    assert_eq!(
        provider.as_ref().type_id(),
        TypeId::of::<GridClientBakingTextureProvider>()
    );
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.TextureProvider_CanBeReplaced::test e9c27d5b8717fdac1a9f3d22930709c0dee326909cb3c777b769b0ed2dc35b0c translated
#[test]
fn appearance_manager_texture_provider_can_be_replaced() {
    let client = GridClient::new().expect("GridClient constructor");
    let replacement: Arc<dyn IBakingTextureProvider> = Arc::new(RecordingTextureProvider);
    client
        .appearance()
        .set_texture_provider(Arc::clone(&replacement));
    let actual = client.appearance().texture_provider();
    assert!(Arc::ptr_eq(&actual, &replacement));
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.LastUpdateReceivedCOFVersion_InitialValue_IsMinusOne::test f43c2d8c99cb597ba7a5d4c87d4585560e888931d84741de2882d05ab17b310a translated
#[test]
fn appearance_manager_initial_cof_version_is_minus_one() {
    let client = GridClient::new().expect("GridClient constructor");
    assert_eq!(client.appearance().last_update_received_cof_version(), -1);
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.UpdateLastReceivedCOFVersion_HigherVersion_Updates::test 62d82f9ec6d12ec9354f02e7b7e86adb29ccecb8b104ed39a66937495bab1438 translated
#[test]
fn appearance_manager_higher_cof_version_updates() {
    let client = GridClient::new().expect("GridClient constructor");
    client
        .appearance()
        .update_last_received_cof_version(5)
        .expect("UpdateLastReceivedCOFVersion");
    assert_eq!(client.appearance().last_update_received_cof_version(), 5);
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.UpdateLastReceivedCOFVersion_LowerVersion_DoesNotUpdate::test ec5831c7d2c03132d3c7e50f6c0d088ca57f2726b7caf77ac240a9d5e92a77b6 translated
#[test]
fn appearance_manager_lower_cof_version_does_not_update() {
    let client = GridClient::new().expect("GridClient constructor");
    client
        .appearance()
        .update_last_received_cof_version(10)
        .expect("UpdateLastReceivedCOFVersion");
    client
        .appearance()
        .update_last_received_cof_version(7)
        .expect("UpdateLastReceivedCOFVersion");
    assert_eq!(client.appearance().last_update_received_cof_version(), 10);
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.UpdateLastReceivedCOFVersion_SameVersion_DoesNotChange::test 2e607b33bb8be04e6710e26fe62510f0634900579260accd9ffd720023636deb translated
#[test]
fn appearance_manager_same_cof_version_does_not_change() {
    let client = GridClient::new().expect("GridClient constructor");
    client
        .appearance()
        .update_last_received_cof_version(5)
        .expect("UpdateLastReceivedCOFVersion");
    client
        .appearance()
        .update_last_received_cof_version(5)
        .expect("UpdateLastReceivedCOFVersion");
    assert_eq!(client.appearance().last_update_received_cof_version(), 5);
}

// parity-case: LibreMetaverse.Tests/AppearanceManagerTests.cs::AppearanceManagerTests.UpdateLastReceivedCOFVersion_MonotonicallyIncreasing_TracksPeak::test 5789831f288823729299944a8796c67348b932570392f59bf8b6d562cfa1c90c translated
#[test]
fn appearance_manager_cof_version_tracks_peak() {
    let client = GridClient::new().expect("GridClient constructor");
    for version in [1, 3, 2, 7, 5, 10, 9] {
        client
            .appearance()
            .update_last_received_cof_version(version)
            .expect("UpdateLastReceivedCOFVersion");
    }
    assert_eq!(client.appearance().last_update_received_cof_version(), 10);
}
