// Exact Rust translations of LibreMetaverse.Tests/AppearanceLiveTests.cs.
// Source SHA-256: 9f4ec256d11290cbad70e209d2f9043d4c6afb18255796c09c4cb26415f971a6

use libremetaverse::appearance::CurrentOutfitFolder;
use libremetaverse::{GridClient, NetworkManager};
use libremetaverse_compat_tests::{block_on, live_grid_credentials};
use libremetaverse_types::{UUID, WearableType};
use std::sync::Arc;
use std::time::{Duration, Instant};

struct LiveAppearance {
    client: Arc<GridClient>,
    cof: CurrentOutfitFolder,
}

impl LiveAppearance {
    fn login() -> Self {
        let mut client = GridClient::new().expect("GridClient constructor");
        client.settings().timing().login_timeout = 30_000;
        let network = client.network();
        let (first, last, password, login_url) = live_grid_credentials();
        let start = NetworkManager::start_location("Hooper".into(), 179, 18, 32)
            .expect("NetworkManager StartLocation");
        let mut login = network
            .default_login_params(
                first,
                last,
                password,
                "Unit Test Framework".into(),
                "admin@radegast.life".into(),
            )
            .expect("NetworkManager DefaultLoginParams");
        login.start = start;
        login.uri = login_url;
        let logged_in = block_on(network.login_with_login_params_cancellation_token(login, None))
            .expect("NetworkManager LoginAsync");
        assert!(
            logged_in,
            "client failed to login: {}",
            network.login_message()
        );
        assert!(network.connected(), "client is not connected to the grid");
        let client = Arc::new(client);
        let cof = CurrentOutfitFolder::new(Some(Arc::clone(&client)))
            .expect("CurrentOutfitFolder constructor");
        Self { client, cof }
    }
}

impl Drop for LiveAppearance {
    fn drop(&mut self) {
        let _ = self.cof.dispose();
        let _ = self.client.network().logout_with_method();
        let _ = self.client.dispose_with_method();
    }
}

// parity-case: LibreMetaverse.Tests/AppearanceLiveTests.cs::AppearanceLiveTests.COF_IsInitializedAfterLogin::test 7029854dc6f4e03016e2ea274140cd9adbbad6b8c11b58cf9e728d9a4cf37f81 ignored-live
#[test]
#[cfg_attr(
    not(live_grid_credentials),
    ignore = "requires GRID_USER, GRID_PASSWORD, and GRID_LOGIN_URL in the environment or workspace .env"
)]
fn cof_is_initialized_after_login() {
    let live = LiveAppearance::login();
    block_on(live.cof.get_current_outfit_links(None)).unwrap();
    let folder = live.cof.cof().expect("COF folder is null");
    assert_ne!(folder.base.uuid(), UUID::zero());
}

// parity-case: LibreMetaverse.Tests/AppearanceLiveTests.cs::AppearanceLiveTests.COF_HasExpectedFolderName::test 9a8cee3c3958b8631921aaa6b43f69dc430eee9bd3544c8f667aa8f5e858b8ac ignored-live
#[test]
#[cfg_attr(
    not(live_grid_credentials),
    ignore = "requires GRID_USER, GRID_PASSWORD, and GRID_LOGIN_URL in the environment or workspace .env"
)]
fn cof_has_expected_folder_name() {
    let live = LiveAppearance::login();
    block_on(live.cof.get_current_outfit_links(None)).unwrap();
    let folder = live.cof.cof().expect("COF folder is null");
    assert!(folder.base.name().eq_ignore_ascii_case("Current Outfit"));
}

// parity-case: LibreMetaverse.Tests/AppearanceLiveTests.cs::AppearanceLiveTests.GetCurrentOutfitLinksAsync_ReturnsLinks::test a1a1c9f926a8b9f4cc4287ab751c4eb70d25a83c1024ef76e53f95c351db8a35 ignored-live
#[test]
#[cfg_attr(
    not(live_grid_credentials),
    ignore = "requires GRID_USER, GRID_PASSWORD, and GRID_LOGIN_URL in the environment or workspace .env"
)]
fn get_current_outfit_links_returns_links() {
    let live = LiveAppearance::login();
    let links = block_on(live.cof.get_current_outfit_links(None)).unwrap();
    for link in links {
        assert!(link.is_link().unwrap(), "returned COF item is not a link");
    }
}

// parity-case: LibreMetaverse.Tests/AppearanceLiveTests.cs::AppearanceLiveTests.GetCurrentOutfitLinksAsync_ContainsAtLeastOneLink::test ef91dfaf7b75f61c97c3957b4dac006a153c7f2d167d6732a9a6dceee08883f2 ignored-live
#[test]
#[cfg_attr(
    not(live_grid_credentials),
    ignore = "requires GRID_USER, GRID_PASSWORD, and GRID_LOGIN_URL in the environment or workspace .env"
)]
fn get_current_outfit_links_contains_at_least_one_link() {
    let live = LiveAppearance::login();
    let links = block_on(live.cof.get_current_outfit_links(None)).unwrap();
    if links.is_empty() {
        eprintln!("COF returned zero links; equip at least a default shape for this assertion");
        return;
    }
    assert!(!links.is_empty());
}

// parity-case: LibreMetaverse.Tests/AppearanceLiveTests.cs::AppearanceLiveTests.GetWornAt_Shape_ReturnsAtLeastOne::test abc01e346371910610312ff9352ee05c0d317a7c677c0fa8cb398d3b0522c3bf ignored-live
#[test]
#[cfg_attr(
    not(live_grid_credentials),
    ignore = "requires GRID_USER, GRID_PASSWORD, and GRID_LOGIN_URL in the environment or workspace .env"
)]
fn get_worn_at_shape_returns_at_least_one() {
    let live = LiveAppearance::login();
    let worn = block_on(live.cof.get_worn_at(WearableType::Shape, None)).unwrap();
    if worn.is_empty() {
        eprintln!("GetWornAtAsync(Shape) returned no items for this account");
        return;
    }
    assert!(!worn.is_empty());
}

// parity-case: LibreMetaverse.Tests/AppearanceLiveTests.cs::AppearanceLiveTests.LastUpdateReceivedCOFVersion_AdvancesAfterLogin::test 5371df8b02df9da20b413f4155f63967184dad23f57708beb56a2c6559e78520 ignored-live
#[test]
#[cfg_attr(
    not(live_grid_credentials),
    ignore = "requires GRID_USER, GRID_PASSWORD, and GRID_LOGIN_URL in the environment or workspace .env"
)]
fn last_update_received_cof_version_advances_after_login() {
    let live = LiveAppearance::login();
    let deadline = Instant::now() + Duration::from_secs(25);
    let mut version = -1;
    while Instant::now() < deadline {
        version = live.client.appearance().last_update_received_cof_version();
        if version > -1 {
            break;
        }
        std::thread::sleep(Duration::from_millis(500));
    }
    if version == -1 {
        eprintln!("the simulator did not send AvatarAppearance for self within 25 seconds");
        return;
    }
    assert!(version > -1);
}

// parity-case: LibreMetaverse.Tests/AppearanceLiveTests.cs::AppearanceLiveTests.RequestOwnAvatarTextures_DoesNotThrow_AndMaintainsVersionGuard::test ee8420690fc9714411450beb5be5755b82ddf7ed0d69e9da88b3184339bea744 ignored-live
#[test]
#[cfg_attr(
    not(live_grid_credentials),
    ignore = "requires GRID_USER, GRID_PASSWORD, and GRID_LOGIN_URL in the environment or workspace .env"
)]
fn request_own_avatar_textures_does_not_throw_and_maintains_version_guard() {
    let live = LiveAppearance::login();
    let before = live.client.appearance().last_update_received_cof_version();
    live.client.avatars().request_own_avatar_textures().unwrap();
    let deadline = Instant::now() + Duration::from_secs(15);
    let mut after = before;
    while Instant::now() < deadline {
        after = live.client.appearance().last_update_received_cof_version();
        if after > before {
            break;
        }
        std::thread::sleep(Duration::from_millis(500));
    }
    assert!(
        after >= before,
        "COF version regressed from {before} to {after}"
    );
}

// parity-case: LibreMetaverse.Tests/AppearanceLiveTests.cs::AppearanceLiveTests.COF_MaxClothingLayers_Is60::test aac1292781cb0a4cbf710535036fa6464065f72d1b2c5214a9b7dc918513e8cd ignored-live
#[test]
#[cfg_attr(
    not(live_grid_credentials),
    ignore = "requires GRID_USER, GRID_PASSWORD, and GRID_LOGIN_URL in the environment or workspace .env"
)]
fn cof_max_clothing_layers_is_60() {
    assert_eq!(LiveAppearance::login().cof.max_clothing_layers(), 60);
}
