// Exact Rust translations of RlvRestrictionManagerTests.

use libremetaverse_compat_tests::block_on;
use libremetaverse_compat_tests::rlv_support::{RlvHarness, guid};
use libremetaverse_rlv::RlvRestrictionType;
use libremetaverse_types::compat::Guid;
use std::sync::{Arc, Mutex};

const PRIM_1: &str = "11111111-1111-1111-1111-111111111111";
const PRIM_2: &str = "22222222-2222-2222-2222-222222222222";
const PRIM_3: &str = "33333333-3333-3333-3333-333333333333";
const MISSING: &str = "99999999-9999-9999-9999-999999999999";

fn harness() -> RlvHarness {
    RlvHarness::new().expect("RlvService constructor")
}

fn process_from(h: &RlvHarness, sender: &str, commands: &[&str]) {
    for command in commands {
        block_on(h.process_from(command, sender, "TestObject")).unwrap();
    }
}

fn tracked(h: &RlvHarness) -> Vec<Guid> {
    h.service
        .restrictions()
        .get_tracked_prim_ids()
        .unwrap()
        .collect()
}

async fn remove(h: &RlvHarness, ids: Vec<Guid>) {
    h.service
        .restrictions()
        .remove_restrictions_for_objects(Box::new(ids.into_iter()), None)
        .await
        .unwrap();
}

// parity-case: LibreMetaverse.Tests/RLV/RlvRestrictionManagerTests.cs::RlvRestrictionManagerTests.GetTrackedPrimIds_NoRestrictions_ReturnsEmptyCollection::test 327086a5aa514ddf8b2cc40891f7ae5e8039788f5cbfeda86f04bd9161f39ded translated
#[test]
fn tracked_prim_ids_empty() {
    assert!(tracked(&harness()).is_empty());
}

// parity-case: LibreMetaverse.Tests/RLV/RlvRestrictionManagerTests.cs::RlvRestrictionManagerTests.GetTrackedPrimIds_SingleRestriction_ReturnsSingleId::test 85a822c9e2e8beb2e70748513a397b8ccbb2977c0581b0519bd8ad43e699ad57 translated
#[test]
fn tracked_prim_ids_single_restriction() {
    let h = harness();
    process_from(&h, PRIM_1, &["@fly=n"]);
    assert_eq!(tracked(&h), vec![guid(PRIM_1)]);
}

// parity-case: LibreMetaverse.Tests/RLV/RlvRestrictionManagerTests.cs::RlvRestrictionManagerTests.GetTrackedPrimIds_MultipleRestrictionsFromSameObject_ReturnsSingleId::test 933070f933b0a5a39b3d0cd5fd9be9423a538609d0106b352a7d9c339665d6d1 translated
#[test]
fn tracked_prim_ids_deduplicates_one_object() {
    let h = harness();
    process_from(&h, PRIM_1, &["@fly=n", "@jump=n", "@sit=n"]);
    assert_eq!(tracked(&h), vec![guid(PRIM_1)]);
}

// parity-case: LibreMetaverse.Tests/RLV/RlvRestrictionManagerTests.cs::RlvRestrictionManagerTests.GetTrackedPrimIds_MultipleObjects_ReturnsAllUniqueIds::test 8c2afe66145e4815e73d7849f83cfec50e4a7a49793cc757917cd608c9387419 translated
#[test]
fn tracked_prim_ids_multiple_objects() {
    let h = harness();
    process_from(&h, PRIM_1, &["@fly=n"]);
    process_from(&h, PRIM_2, &["@jump=n"]);
    process_from(&h, PRIM_3, &["@sit=n"]);
    let ids = tracked(&h);
    assert_eq!(ids.len(), 3);
    assert!(ids.contains(&guid(PRIM_1)));
    assert!(ids.contains(&guid(PRIM_2)));
    assert!(ids.contains(&guid(PRIM_3)));
}

// parity-case: LibreMetaverse.Tests/RLV/RlvRestrictionManagerTests.cs::RlvRestrictionManagerTests.GetTrackedPrimIds_MixedRestrictionsAndExceptions_ReturnsAllUniqueIds::test 25ea3c36aba93d5183816962bf49bfdbc3e17e6b06d73a41dee5b51407a84746 translated
#[test]
fn tracked_prim_ids_include_restrictions_and_exceptions() {
    let h = harness();
    process_from(&h, PRIM_1, &["@detachthis=n"]);
    process_from(&h, PRIM_2, &["@detachthis_except:folder=n"]);
    let ids = tracked(&h);
    assert_eq!(ids.len(), 2);
    assert!(ids.contains(&guid(PRIM_1)));
    assert!(ids.contains(&guid(PRIM_2)));
}

// parity-case: LibreMetaverse.Tests/RLV/RlvRestrictionManagerTests.cs::RlvRestrictionManagerTests.GetTrackedPrimIds_AfterRemovingRestriction_DoesNotReturnRemovedObjectId::test af3d5328e5c829bd2de2b72c3f4ede6f083efa789ccef1d73d752612a177360f translated
#[test]
fn tracked_prim_ids_after_removing_restriction() {
    let h = harness();
    process_from(&h, PRIM_1, &["@fly=n"]);
    process_from(&h, PRIM_2, &["@jump=n"]);
    process_from(&h, PRIM_1, &["@fly=y"]);
    let ids = tracked(&h);
    assert_eq!(ids, vec![guid(PRIM_2)]);
    assert!(!ids.contains(&guid(PRIM_1)));
}

// parity-case: LibreMetaverse.Tests/RLV/RlvRestrictionManagerTests.cs::RlvRestrictionManagerTests.GetTrackedPrimIds_AfterClearingAllRestrictions_ReturnsEmptyCollection::test 191a8cc3dba7e68155083a6892f1b4a29ab589023e22cffbc8cd69c005884cca translated
#[test]
fn tracked_prim_ids_after_clear() {
    let h = harness();
    process_from(&h, PRIM_1, &["@fly=n", "@jump=n", "@clear"]);
    assert!(tracked(&h).is_empty());
}

// parity-case: LibreMetaverse.Tests/RLV/RlvRestrictionManagerTests.cs::RlvRestrictionManagerTests.RemoveRestrictionsForObjects_EmptyInput_NoEffect::test 8718e08bb2bf951a94803b4947943b5f7f9eb85e906b4ac6c639cb8cd065cf9d translated
#[test]
fn remove_restrictions_empty_input() {
    let h = harness();
    process_from(&h, PRIM_1, &["@fly=n"]);
    block_on(remove(&h, vec![]));
    assert!(!h.service.permissions().can_fly().unwrap());
    assert_eq!(tracked(&h).len(), 1);
}

// parity-case: LibreMetaverse.Tests/RLV/RlvRestrictionManagerTests.cs::RlvRestrictionManagerTests.RemoveRestrictionsForObjects_NonExistentId_NoEffect::test 882d2315f40317cce9c9a026eda498325b6227886652795ed45e87cc4e5697ac translated
#[test]
fn remove_restrictions_missing_id() {
    let h = harness();
    process_from(&h, PRIM_1, &["@fly=n"]);
    block_on(remove(&h, vec![guid(MISSING)]));
    assert!(!h.service.permissions().can_fly().unwrap());
    assert_eq!(tracked(&h), vec![guid(PRIM_1)]);
}

// parity-case: LibreMetaverse.Tests/RLV/RlvRestrictionManagerTests.cs::RlvRestrictionManagerTests.RemoveRestrictionsForObjects_SingleObject_RemovesAllRestrictionsFromObject::test 73ab6ee30a336372f6aef5bc4a99dbb58215b9445daea5d9708d0ea9cabe8bd1 translated
#[test]
fn remove_restrictions_single_object() {
    let h = harness();
    process_from(&h, PRIM_1, &["@fly=n", "@jump=n", "@sit=n"]);
    assert!(!h.service.permissions().can_fly().unwrap());
    assert!(!h.service.permissions().can_jump().unwrap());
    block_on(remove(&h, vec![guid(PRIM_1)]));
    assert!(h.service.permissions().can_fly().unwrap());
    assert!(h.service.permissions().can_jump().unwrap());
    assert!(tracked(&h).is_empty());
}

// parity-case: LibreMetaverse.Tests/RLV/RlvRestrictionManagerTests.cs::RlvRestrictionManagerTests.RemoveRestrictionsForObjects_MultipleObjects_RemovesOnlySpecifiedObjects::test 03b6a6a3e02f6a6a5f296ff1d6cca31c9fc53982cbc9c27e73cfb698ed82acf5 translated
#[test]
fn remove_restrictions_multiple_objects() {
    let h = harness();
    process_from(&h, PRIM_1, &["@fly=n"]);
    process_from(&h, PRIM_2, &["@jump=n"]);
    process_from(&h, PRIM_3, &["@sit=n"]);
    assert!(!h.service.permissions().can_fly().unwrap());
    assert!(!h.service.permissions().can_jump().unwrap());
    block_on(remove(&h, vec![guid(PRIM_1), guid(PRIM_3)]));
    assert!(h.service.permissions().can_fly().unwrap());
    assert!(!h.service.permissions().can_jump().unwrap());
    assert_eq!(tracked(&h), vec![guid(PRIM_2)]);
}

// parity-case: LibreMetaverse.Tests/RLV/RlvRestrictionManagerTests.cs::RlvRestrictionManagerTests.RemoveRestrictionsForObjects_PartialMatch_RemovesOnlyMatchingObjects::test 00a26d807882b2bbb375c5ce7cf1074d3227acd891e859602e78abbdc6eefb6a translated
#[test]
fn remove_restrictions_partial_match() {
    let h = harness();
    process_from(&h, PRIM_1, &["@fly=n"]);
    process_from(&h, PRIM_2, &["@jump=n"]);
    block_on(remove(&h, vec![guid(PRIM_1), guid(MISSING)]));
    assert!(h.service.permissions().can_fly().unwrap());
    assert!(!h.service.permissions().can_jump().unwrap());
    assert_eq!(tracked(&h), vec![guid(PRIM_2)]);
}

// parity-case: LibreMetaverse.Tests/RLV/RlvRestrictionManagerTests.cs::RlvRestrictionManagerTests.RemoveRestrictionsForObjects_WithNotificationRestrictions_SendsNotifications::test ba82b183dfdb01b36f9b45520e1808290c360f327d4afa833ba3b8c7a7cecffb translated
#[test]
fn remove_restrictions_sends_notification() {
    let h = harness();
    process_from(&h, PRIM_2, &["@notify:123=n"]);
    process_from(&h, PRIM_1, &["@fly=n"]);
    block_on(remove(&h, vec![guid(PRIM_1)]));
    assert_eq!(
        h.actions
            .lock()
            .unwrap()
            .replies
            .iter()
            .filter(|reply| **reply == (123, "/fly=y".into()))
            .count(),
        1
    );
}

// parity-case: LibreMetaverse.Tests/RLV/RlvRestrictionManagerTests.cs::RlvRestrictionManagerTests.RemoveRestrictionsForObjects_WithComplexRestrictions_RemovesCorrectly::test 631de2d39fa56b29014fae4ce3b57c480f540ad41ce08a0037b7f8bbfcb3c502 translated
#[test]
fn remove_restrictions_complex_types() {
    let h = harness();
    process_from(
        &h,
        PRIM_1,
        &[
            "@detachthis=n",
            "@detachthis_except:folder=n",
            "@touchfar:1.5=n",
        ],
    );
    process_from(&h, PRIM_2, &["@fly=n"]);
    assert_eq!(
        h.service
            .restrictions()
            .find_restrictions(None, Some(Some(guid(PRIM_1))))
            .unwrap()
            .len(),
        3
    );
    assert_eq!(
        h.service
            .restrictions()
            .find_restrictions(None, Some(Some(guid(PRIM_2))))
            .unwrap()
            .len(),
        1
    );
    block_on(remove(&h, vec![guid(PRIM_1)]));
    assert!(
        h.service
            .restrictions()
            .find_restrictions(None, Some(Some(guid(PRIM_1))))
            .unwrap()
            .is_empty()
    );
    assert_eq!(
        h.service
            .restrictions()
            .find_restrictions(None, Some(Some(guid(PRIM_2))))
            .unwrap()
            .len(),
        1
    );
}

// parity-case: LibreMetaverse.Tests/RLV/RlvRestrictionManagerTests.cs::RlvRestrictionManagerTests.RemoveRestrictionsForObjects_TriggersRestrictionUpdatedEvent::test 318f1f4207b04d01076d3498aceea781c4889b05cdbef199cdbea43dde355f4c translated
#[test]
fn remove_restrictions_triggers_update_event() {
    let h = harness();
    process_from(&h, PRIM_1, &["@fly=n"]);
    let removed = Arc::new(Mutex::new(None));
    let captured = Arc::clone(&removed);
    let _subscription = h
        .service
        .restrictions()
        .subscribe_restriction_updated(Some(Arc::new(move |args| {
            if !args.is_new() && args.is_deleted() {
                let restriction = args.restriction();
                *captured.lock().unwrap() = Some((restriction.behavior(), restriction.sender()));
            }
        })));
    block_on(remove(&h, vec![guid(PRIM_1)]));
    assert_eq!(
        *removed.lock().unwrap(),
        Some((RlvRestrictionType::Fly, guid(PRIM_1)))
    );
}
