// Exact Rust translations of the direct attachment, outfit, detach, edit, and permissive restrictions.

use libremetaverse_compat_tests::block_on;
use libremetaverse_compat_tests::rlv_support::{RlvHarness, SampleInventoryTree, guid};
use libremetaverse_rlv::{
    InventoryMap, RlvAttachmentPoint, RlvPermissionsServiceObjectLocation as ObjectLocation,
    RlvWearableType,
};

const PRIM_1: &str = "11111111-0001-4aaa-8aaa-ffffffffffff";
const PRIM_2: &str = "11111111-0002-4aaa-8aaa-ffffffffffff";
const PRIM_3: &str = "11111111-0003-4aaa-8aaa-ffffffffffff";

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

fn provide_inventory(h: &RlvHarness, root: libremetaverse_rlv::RlvSharedFolder) {
    h.queries.lock().unwrap().inventory_map = Some(InventoryMap::new(root, vec![]).unwrap());
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/AddAttachRestrictionTests.cs::AddAttachRestrictionTests.AddAttach::test bb2c3495d7b6862fabe1007bcfccc2850d29eef1d9aa4eac6d13d6004dcc4e6b translated
#[test]
fn add_attach_restricts_all_attachment_points() {
    let h = harness();
    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(PRIM_1))));
    tree.fancy_hat
        .set_attached_to(Some(Some(RlvAttachmentPoint::Chin)));
    tree.fancy_hat
        .set_attached_prim_id(Some(Some(guid(PRIM_3))));
    provide_inventory(&h, tree.root);
    assert!(block_on(h.process("@addattach=n")).unwrap());
    let permissions = h.service.permissions();
    assert!(
        !permissions
            .can_attach_with_rlv_inventory_item_boolean(tree.fancy_hat, true)
            .unwrap()
    );
    assert!(
        !permissions
            .can_attach_with_rlv_inventory_item_boolean(tree.happy_shirt, true)
            .unwrap()
    );
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/AddAttachRestrictionTests.cs::AddAttachRestrictionTests.AddAttach_Specific::test 0201965931ebba1c13a23bbb43a8787c1639b0b98851141faffd963173c51035 translated
#[test]
fn add_attach_restricts_one_attachment_point() {
    let h = harness();
    let mut tree = SampleInventoryTree::build().unwrap();
    tree.happy_shirt
        .set_attached_to(Some(Some(RlvAttachmentPoint::Groin)));
    tree.happy_shirt
        .set_attached_prim_id(Some(Some(guid(PRIM_1))));
    tree.fancy_hat
        .set_attached_to(Some(Some(RlvAttachmentPoint::Chin)));
    tree.fancy_hat
        .set_attached_prim_id(Some(Some(guid(PRIM_3))));
    provide_inventory(&h, tree.root);
    assert!(block_on(h.process("@addattach:groin=n")).unwrap());
    let permissions = h.service.permissions();
    assert!(
        permissions
            .can_attach_with_rlv_inventory_item_boolean(tree.fancy_hat, true)
            .unwrap()
    );
    assert!(
        !permissions
            .can_attach_with_rlv_inventory_item_boolean(tree.happy_shirt, true)
            .unwrap()
    );
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/AddOutfitRestrictionTests.cs::AddOutfitRestrictionTests.AddOutfit::test d4902b4dda0a640d240c88af30431f6d247aae2a74a81126fd91ba1d930de3fb translated
#[test]
fn add_outfit_restricts_all_layers() {
    let h = harness();
    let mut tree = SampleInventoryTree::build().unwrap();
    tree.party_hat
        .set_attached_to(Some(Some(RlvAttachmentPoint::Skull)));
    tree.party_hat
        .set_attached_prim_id(Some(Some(guid(PRIM_3))));
    tree.happy_shirt
        .set_worn_on(Some(Some(RlvWearableType::Shirt)));
    tree.fancy_hat
        .set_worn_on(Some(Some(RlvWearableType::Hair)));
    provide_inventory(&h, tree.root);
    assert!(block_on(h.process_from("@addoutfit=n", PRIM_3, "Party Hat (Spine)")).unwrap());
    let permissions = h.service.permissions();
    assert!(
        !permissions
            .can_attach_with_rlv_inventory_item_boolean(tree.happy_shirt, true)
            .unwrap()
    );
    assert!(
        !permissions
            .can_attach_with_rlv_inventory_item_boolean(tree.fancy_hat, true)
            .unwrap()
    );
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/AddOutfitRestrictionTests.cs::AddOutfitRestrictionTests.AddOutfit_part::test e7e5467b36484fb54b0336abccf682905fbe5bff9e04195645981815e2ffadeb translated
#[test]
fn add_outfit_restricts_one_layer() {
    let h = harness();
    let mut tree = SampleInventoryTree::build().unwrap();
    tree.happy_shirt
        .set_worn_on(Some(Some(RlvWearableType::Shirt)));
    tree.fancy_hat
        .set_worn_on(Some(Some(RlvWearableType::Hair)));
    provide_inventory(&h, tree.root);
    assert!(block_on(h.process("@addoutfit:shirt=n")).unwrap());
    let permissions = h.service.permissions();
    assert!(
        !permissions
            .can_attach_with_rlv_inventory_item_boolean(tree.happy_shirt, true)
            .unwrap()
    );
    assert!(
        permissions
            .can_attach_with_rlv_inventory_item_boolean(tree.fancy_hat, true)
            .unwrap()
    );
}

const OBJECT_1: &str = "00000000-0000-4000-8000-000000000000";
const OBJECT_PRIM_1: &str = "00000000-0000-4000-8000-ffffffffffff";
const OBJECT_2: &str = "11111111-1111-4111-8111-111111111111";
const OBJECT_PRIM_2: &str = "11111111-1111-4111-8111-ffffffffffff";
const FOLDER: &str = "99999999-9999-4999-8999-999999999999";

fn can_detach(
    h: &RlvHarness,
    item: &str,
    prim: Option<&str>,
    shared: bool,
    point: Option<RlvAttachmentPoint>,
    wearable: Option<RlvWearableType>,
) -> bool {
    h.service
        .permissions()
        .can_detach_with_nullable_nullable_nullable_boolean_nullable_nullable(
            Some(Some(guid(item))),
            prim.map(|value| Some(guid(value))),
            Some(Some(guid(FOLDER))),
            shared,
            point.map(Some),
            wearable.map(Some),
        )
        .unwrap()
}

fn detach_defaults(h: &RlvHarness) -> [bool; 6] {
    [
        can_detach(h, OBJECT_1, None, false, None, None),
        can_detach(
            h,
            OBJECT_1,
            Some(OBJECT_PRIM_1),
            false,
            Some(RlvAttachmentPoint::Chest),
            None,
        ),
        can_detach(h, OBJECT_1, None, false, None, Some(RlvWearableType::Shirt)),
        can_detach(h, OBJECT_2, None, true, None, None),
        can_detach(
            h,
            OBJECT_2,
            Some(OBJECT_PRIM_2),
            true,
            Some(RlvAttachmentPoint::Chest),
            None,
        ),
        can_detach(h, OBJECT_2, None, true, None, Some(RlvWearableType::Shirt)),
    ]
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/DetachRestrictionTests.cs::DetachRestrictionTests.Detach_Default::test 8c5b165329ab6b46eb3ec7c9a07383e8ea1e54edf0882b43b14166d67cc45617 translated
#[test]
fn detach_default() {
    assert_eq!(detach_defaults(&harness()), [true; 6]);
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/DetachRestrictionTests.cs::DetachRestrictionTests.Detach::test 50f5419aea3ea41aa9ebedd29202a549deb4ae6089c38965300a46fd5abf94bf translated
#[test]
fn detach_restricts_sender_prim() {
    let h = harness();
    assert!(block_on(h.process_from("@detach=n", OBJECT_PRIM_2, "objectPrimId2")).unwrap());
    assert_eq!(detach_defaults(&h), [true, true, true, true, false, true]);
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/DetachRestrictionTests.cs::DetachRestrictionTests.Detach_AttachPoint::test a75fea134b92bca704b2e140135076d699d645c0d2854022d5b20d14be372589 translated
#[test]
fn detach_restricts_attachment_point() {
    let h = harness();
    assert!(block_on(h.process("@detach:skull=n")).unwrap());
    for shared in [false, true] {
        assert!(can_detach(&h, OBJECT_1, None, shared, None, None));
        assert!(can_detach(
            &h,
            OBJECT_1,
            Some(if shared { OBJECT_PRIM_2 } else { OBJECT_PRIM_1 }),
            shared,
            Some(RlvAttachmentPoint::Chest),
            None
        ));
        assert!(!can_detach(
            &h,
            OBJECT_1,
            Some(if shared { OBJECT_PRIM_2 } else { OBJECT_PRIM_1 }),
            shared,
            Some(RlvAttachmentPoint::Skull),
            None
        ));
        assert!(can_detach(
            &h,
            OBJECT_1,
            None,
            shared,
            None,
            Some(RlvWearableType::Shirt)
        ));
    }
}

fn edit_matrix(command: &str) -> [[bool; 3]; 2] {
    let h = harness();
    block_on(h.process(command)).unwrap();
    [None, Some(Some(guid(OBJECT_1)))].map(|object| {
        [
            h.service
                .permissions()
                .can_edit(ObjectLocation::Hud, object)
                .unwrap(),
            h.service
                .permissions()
                .can_edit(ObjectLocation::Attached, object)
                .unwrap(),
            h.service
                .permissions()
                .can_edit(ObjectLocation::RezzedInWorld, object)
                .unwrap(),
        ]
    })
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/EditAttachRestrictionTests.cs::EditAttachRestrictionTests.CanEdit_Attachment::test 22bc9e267e270c2c762561851249af4ef64c0b5bd288950a95b1b636b2975b6e translated
#[test]
fn edit_attachment_restriction() {
    assert_eq!(edit_matrix("@editattach=n"), [[true, false, true]; 2]);
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/EditRestrictionTests.cs::EditRestrictionTests.CanEditFolderNameSpecifiesToAddInsteadOfReplace::test 6d82d0869826bbc935d89967445dfc991619bdaf3fa631550c5d3f91a13c4935 translated
#[test]
fn edit_everywhere_restriction() {
    assert_eq!(edit_matrix("@edit=n"), [[false, false, false]; 2]);
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/EditWorldRestrictionTests.cs::EditWorldRestrictionTests.CanEdit_World::test 1f335a2c81735bf58294b97ceedfd85192113e69c78a38804f0db923ce7a0241 translated
#[test]
fn edit_world_restriction() {
    assert_eq!(edit_matrix("@editworld=n"), [[true, true, false]; 2]);
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/PermissiveRestrictionTests.cs::PermissiveRestrictionTests.Permissive_On::test 923519a1220e5b3007321fb9ee8cc9db27bc0bbc38bed69df0659e4365c610e8 translated
#[test]
fn permissive_on_command_disables_permissive_mode() {
    let h = harness();
    block_on(h.process("@permissive=n")).unwrap();
    assert!(!h.service.permissions().is_permissive().unwrap());
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/PermissiveRestrictionTests.cs::PermissiveRestrictionTests.Permissive_Off::test b995698351bf04e00ab5aed459c8ee08292897545b8684338a90eaf42cd6c4aa translated
#[test]
fn permissive_off_command_restores_permissive_mode() {
    let h = harness();
    block_on(h.process("@permissive=n")).unwrap();
    block_on(h.process("@permissive=y")).unwrap();
    assert!(h.service.permissions().is_permissive().unwrap());
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/RemAttachRestrictionTests.cs::RemAttachRestrictionTests.RemAttach::test 7005409bfff88f7fb75e5cf87f6f52f4c6f048de32f651b844cf62a1cc16f55c translated
#[test]
fn remove_attachment_restriction() {
    let h = harness();
    let mut tree = SampleInventoryTree::build().unwrap();
    tree.fancy_hat
        .set_attached_to(Some(Some(RlvAttachmentPoint::Chin)));
    tree.fancy_hat
        .set_attached_prim_id(Some(Some(guid(PRIM_3))));
    tree.watch.set_worn_on(Some(Some(RlvWearableType::Tattoo)));
    provide_inventory(&h, tree.root);
    assert!(block_on(h.process("@remattach=n")).unwrap());
    let permissions = h.service.permissions();
    assert!(
        !permissions
            .can_detach_with_rlv_inventory_item(tree.fancy_hat)
            .unwrap()
    );
    assert!(
        permissions
            .can_detach_with_rlv_inventory_item(tree.watch)
            .unwrap()
    );
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/RemAttachRestrictionTests.cs::RemAttachRestrictionTests.RemAttach_Specific::test 407cb5627753c42af2b469644ddc2857416fb4b181bfbb742b199ad7239d5e6f translated
#[test]
fn remove_specific_attachment_restriction() {
    let h = harness();
    let mut tree = SampleInventoryTree::build().unwrap();
    tree.party_hat
        .set_attached_to(Some(Some(RlvAttachmentPoint::Spine)));
    tree.party_hat
        .set_attached_prim_id(Some(Some(guid(PRIM_3))));
    tree.fancy_hat
        .set_attached_to(Some(Some(RlvAttachmentPoint::Chin)));
    tree.fancy_hat
        .set_attached_prim_id(Some(Some(guid(PRIM_2))));
    tree.watch.set_worn_on(Some(Some(RlvWearableType::Tattoo)));
    provide_inventory(&h, tree.root);
    assert!(block_on(h.process("@remattach:spine=n")).unwrap());
    let permissions = h.service.permissions();
    assert!(
        !permissions
            .can_detach_with_rlv_inventory_item(tree.party_hat)
            .unwrap()
    );
    assert!(
        permissions
            .can_detach_with_rlv_inventory_item(tree.fancy_hat)
            .unwrap()
    );
    assert!(
        permissions
            .can_detach_with_rlv_inventory_item(tree.watch)
            .unwrap()
    );
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/RemOutfitRestrictionTests.cs::RemOutfitRestrictionTests.RemOutfit::test 2aa5056c8aec09fb28d0da37bf6861220595cacb3190542d865f99034fc1c488 translated
#[test]
fn remove_outfit_restriction() {
    let h = harness();
    let mut tree = SampleInventoryTree::build().unwrap();
    tree.fancy_hat
        .set_attached_to(Some(Some(RlvAttachmentPoint::Chin)));
    tree.fancy_hat
        .set_attached_prim_id(Some(Some(guid(PRIM_3))));
    tree.watch.set_worn_on(Some(Some(RlvWearableType::Tattoo)));
    provide_inventory(&h, tree.root);
    assert!(block_on(h.process("@remoutfit=n")).unwrap());
    let permissions = h.service.permissions();
    assert!(
        permissions
            .can_detach_with_rlv_inventory_item(tree.fancy_hat)
            .unwrap()
    );
    assert!(
        !permissions
            .can_detach_with_rlv_inventory_item(tree.watch)
            .unwrap()
    );
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/RemOutfitRestrictionTests.cs::RemOutfitRestrictionTests.RemOutfit_part::test f0c36209e33c9961b10ff685c8d4a32e279b4852e1c16a0e724f3cd481089576 translated
#[test]
fn remove_specific_outfit_layer_restriction() {
    let h = harness();
    let mut tree = SampleInventoryTree::build().unwrap();
    tree.fancy_hat
        .set_attached_to(Some(Some(RlvAttachmentPoint::Chin)));
    tree.fancy_hat
        .set_attached_prim_id(Some(Some(guid(PRIM_3))));
    tree.watch.set_worn_on(Some(Some(RlvWearableType::Tattoo)));
    tree.retro_pants
        .set_worn_on(Some(Some(RlvWearableType::Pants)));
    provide_inventory(&h, tree.root);
    assert!(block_on(h.process("@remoutfit:pants=n")).unwrap());
    let permissions = h.service.permissions();
    assert!(
        permissions
            .can_detach_with_rlv_inventory_item(tree.fancy_hat)
            .unwrap()
    );
    assert!(
        !permissions
            .can_detach_with_rlv_inventory_item(tree.retro_pants)
            .unwrap()
    );
    assert!(
        permissions
            .can_detach_with_rlv_inventory_item(tree.watch)
            .unwrap()
    );
}
