// Exact Rust translations of recursive and folder-targeted attachment restrictions.

use libremetaverse_compat_tests::block_on;
use libremetaverse_compat_tests::rlv_support::{RlvHarness, SampleInventoryTree, guid};
use libremetaverse_rlv::{InventoryMap, RlvAttachmentPoint, RlvWearableType};
use libremetaverse_types::compat::Guid;

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";

#[derive(Clone, Copy)]
enum Setup {
    None,
    PartySpine,
    PartyChin,
    BusinessPelvis,
    BusinessChin,
    BusinessChest,
    RetroPants,
    WatchTattoo,
    PartyAndBusinessPelvis,
    PartyAndBusinessPelvisDetach,
}

#[derive(Clone, Copy)]
enum FolderKey {
    Clothing,
    Hats,
    SubHats,
    Accessories,
}

#[derive(Clone, Copy)]
enum LockMode {
    Attach,
    Detach,
}

struct FolderIdentity {
    clothing: (Guid, String),
    hats: (Guid, String),
    sub_hats: (Guid, String),
    accessories: (Guid, String),
}

fn apply_setup(tree: &mut SampleInventoryTree, setup: Setup) {
    match setup {
        Setup::None => {}
        Setup::PartySpine => {
            tree.party_hat
                .set_attached_to(Some(Some(RlvAttachmentPoint::Spine)));
            tree.party_hat
                .set_attached_prim_id(Some(Some(guid(PRIM_3))));
        }
        Setup::PartyChin => {
            tree.party_hat
                .set_attached_to(Some(Some(RlvAttachmentPoint::Chin)));
            tree.party_hat
                .set_attached_prim_id(Some(Some(guid(PRIM_3))));
        }
        Setup::BusinessPelvis => {
            tree.business_pants
                .set_attached_to(Some(Some(RlvAttachmentPoint::Pelvis)));
            tree.business_pants
                .set_attached_prim_id(Some(Some(guid(PRIM_3))));
        }
        Setup::BusinessChin => {
            tree.business_pants
                .set_attached_to(Some(Some(RlvAttachmentPoint::Chin)));
            tree.business_pants
                .set_attached_prim_id(Some(Some(guid(PRIM_3))));
        }
        Setup::BusinessChest => {
            tree.business_pants
                .set_attached_to(Some(Some(RlvAttachmentPoint::Chest)));
            tree.business_pants
                .set_attached_prim_id(Some(Some(guid(PRIM_3))));
        }
        Setup::RetroPants => tree
            .retro_pants
            .set_worn_on(Some(Some(RlvWearableType::Pants))),
        Setup::WatchTattoo => tree.watch.set_worn_on(Some(Some(RlvWearableType::Tattoo))),
        Setup::PartyAndBusinessPelvis => {
            tree.party_hat
                .set_attached_to(Some(Some(RlvAttachmentPoint::Pelvis)));
            tree.party_hat
                .set_attached_prim_id(Some(Some(guid(PRIM_2))));
            tree.business_pants
                .set_attached_to(Some(Some(RlvAttachmentPoint::Pelvis)));
            tree.business_pants
                .set_attached_prim_id(Some(Some(guid(PRIM_1))));
        }
        Setup::PartyAndBusinessPelvisDetach => {
            tree.party_hat
                .set_attached_to(Some(Some(RlvAttachmentPoint::Pelvis)));
            tree.party_hat
                .set_attached_prim_id(Some(Some(guid(PRIM_1))));
            tree.business_pants
                .set_attached_to(Some(Some(RlvAttachmentPoint::Pelvis)));
            tree.business_pants
                .set_attached_prim_id(Some(Some(guid(PRIM_3))));
        }
    }
}

fn locked_folders_match(
    h: &RlvHarness,
    identities: &FolderIdentity,
    expected: &[FolderKey],
    mode: LockMode,
) -> bool {
    let locked = h.service.restrictions().get_locked_folders().unwrap();
    if locked.len() != expected.len() {
        return false;
    }
    for key in expected {
        let (id, name) = match key {
            FolderKey::Clothing => &identities.clothing,
            FolderKey::Hats => &identities.hats,
            FolderKey::SubHats => &identities.sub_hats,
            FolderKey::Accessories => &identities.accessories,
        };
        let Some(folder) = locked.get(id) else {
            return false;
        };
        let common = folder.id() == *id
            && folder.name() == *name
            && folder.is_locked()
            && folder.attach_exceptions().is_empty()
            && folder.detach_exceptions().is_empty();
        let mode_matches = match mode {
            LockMode::Attach => {
                !folder.can_attach()
                    && folder.can_detach()
                    && folder.attach_restrictions().len() == 1
                    && folder.detach_restrictions().is_empty()
            }
            LockMode::Detach => {
                folder.can_attach()
                    && !folder.can_detach()
                    && folder.attach_restrictions().is_empty()
                    && folder.detach_restrictions().len() == 1
            }
        };
        if !common || !mode_matches {
            return false;
        }
    }
    true
}

fn folder_case_matches(
    command: &str,
    sender: Option<(&str, &str)>,
    setup: Setup,
    mode: LockMode,
    expected_items: [bool; 7],
    expected_folders: &[FolderKey],
) -> bool {
    let h = RlvHarness::new().expect("RlvService constructor");
    let mut tree = SampleInventoryTree::build().expect("sample inventory tree");
    apply_setup(&mut tree, setup);
    let identities = FolderIdentity {
        clothing: (tree.clothing.id(), tree.clothing.name()),
        hats: (tree.hats.id(), tree.hats.name()),
        sub_hats: (tree.sub_hats.id(), tree.sub_hats.name()),
        accessories: (tree.accessories.id(), tree.accessories.name()),
    };
    h.queries.lock().unwrap().inventory_map = Some(InventoryMap::new(tree.root, vec![]).unwrap());
    for command in command.split(',') {
        let processed = sender.map_or_else(
            || block_on(h.process(command)),
            |(id, name)| block_on(h.process_from(command, id, name)),
        );
        if !processed.unwrap() {
            return false;
        }
    }

    let permissions = h.service.permissions();
    let actual = match mode {
        LockMode::Attach => [
            permissions
                .can_attach_with_rlv_inventory_item_boolean(tree.party_hat, true)
                .unwrap(),
            permissions
                .can_attach_with_rlv_inventory_item_boolean(tree.fancy_hat, true)
                .unwrap(),
            permissions
                .can_attach_with_rlv_inventory_item_boolean(tree.business_pants, true)
                .unwrap(),
            permissions
                .can_attach_with_rlv_inventory_item_boolean(tree.happy_shirt, true)
                .unwrap(),
            permissions
                .can_attach_with_rlv_inventory_item_boolean(tree.retro_pants, true)
                .unwrap(),
            permissions
                .can_attach_with_rlv_inventory_item_boolean(tree.glasses, true)
                .unwrap(),
            permissions
                .can_attach_with_rlv_inventory_item_boolean(tree.watch, true)
                .unwrap(),
        ],
        LockMode::Detach => [
            permissions
                .can_detach_with_rlv_inventory_item(tree.party_hat)
                .unwrap(),
            permissions
                .can_detach_with_rlv_inventory_item(tree.fancy_hat)
                .unwrap(),
            permissions
                .can_detach_with_rlv_inventory_item(tree.business_pants)
                .unwrap(),
            permissions
                .can_detach_with_rlv_inventory_item(tree.happy_shirt)
                .unwrap(),
            permissions
                .can_detach_with_rlv_inventory_item(tree.retro_pants)
                .unwrap(),
            permissions
                .can_detach_with_rlv_inventory_item(tree.glasses)
                .unwrap(),
            permissions
                .can_detach_with_rlv_inventory_item(tree.watch)
                .unwrap(),
        ],
    };
    actual == expected_items && locked_folders_match(&h, &identities, expected_folders, mode)
}

const HATS_LOCKED: [bool; 7] = [false, false, true, true, true, true, true];
const CLOTHING_LOCKED: [bool; 7] = [true, true, false, false, false, true, true];
const CLOTHING_RECURSIVE_LOCKED: [bool; 7] = [false, false, false, false, false, true, true];
const ACCESSORIES_LOCKED: [bool; 7] = [true, true, true, true, true, false, false];
const ALL_ALLOWED: [bool; 7] = [true; 7];

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/AttachAllThisRestrictionTests.cs::AttachAllThisRestrictionTests.AttachAllThis::test 39bc0c4581f8277f4d5c90ec715c40f4715b5ae7007a28a04fe762f2f6adfbfd translated
#[test]
fn attach_all_this() {
    assert!(folder_case_matches(
        "@attachallthis=n",
        Some((PRIM_3, "Party Hat (Spine)")),
        Setup::PartyChin,
        LockMode::Attach,
        HATS_LOCKED,
        &[FolderKey::Hats, FolderKey::SubHats],
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/AttachAllThisRestrictionTests.cs::AttachAllThisRestrictionTests.AttachAllThis_Recursive::test d051e7783e79bf91a8092cd2d29cf1aacec3b0af86ecf0c7ca590ce25bc8393c translated
#[test]
fn attach_all_this_recursive() {
    assert!(folder_case_matches(
        "@attachallthis=n",
        Some((PRIM_3, "Business Pants (Pelvis)")),
        Setup::BusinessChin,
        LockMode::Attach,
        CLOTHING_RECURSIVE_LOCKED,
        &[FolderKey::Clothing, FolderKey::Hats, FolderKey::SubHats],
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/AttachAllThisRestrictionTests.cs::AttachAllThisRestrictionTests.AttachAllThis_Recursive_Path::test b4db2b29ad376777b382f8c6f4f7b62643bb2dd43858de4be69ac2252f18c963 translated
#[test]
fn attach_all_this_recursive_path() {
    assert!(folder_case_matches(
        "@attachallthis:Clothing=n",
        None,
        Setup::BusinessChin,
        LockMode::Attach,
        CLOTHING_RECURSIVE_LOCKED,
        &[FolderKey::Clothing, FolderKey::Hats, FolderKey::SubHats],
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/AttachAllThisRestrictionTests.cs::AttachAllThisRestrictionTests.AttachAllThis_Recursive_Worn::test 6ecfc6ffe71896979a37dac6dd8149a015b8d3e7e952247954333c053da4961e translated
#[test]
fn attach_all_this_recursive_worn() {
    assert!(folder_case_matches(
        "@attachallthis:pants=n",
        None,
        Setup::RetroPants,
        LockMode::Attach,
        CLOTHING_RECURSIVE_LOCKED,
        &[FolderKey::Clothing, FolderKey::Hats, FolderKey::SubHats],
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/AttachAllThisRestrictionTests.cs::AttachAllThisRestrictionTests.AttachAllThis_Recursive_Attached::test 8ac38c77c6bdb03e4cbaa96600bead9a760eb5eec098504b05cfb3a1bdfb8557 translated
#[test]
fn attach_all_this_recursive_attached() {
    assert!(folder_case_matches(
        "@attachallthis:chest=n",
        Some((PRIM_3, "Business Pants (Pelvis)")),
        Setup::BusinessChest,
        LockMode::Attach,
        CLOTHING_RECURSIVE_LOCKED,
        &[FolderKey::Clothing, FolderKey::Hats, FolderKey::SubHats],
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/AttachAllThisRestrictionTests.cs::AttachAllThisRestrictionTests.AttachAllThis_Recursive_Attached_AddRem::test 495f288e0fa86059d13a027075700ba6308e9c9d585fa56e3d1adf4a71df912b translated
#[test]
fn attach_all_this_recursive_attached_removed() {
    assert!(folder_case_matches(
        "@attachallthis:chest=n,@attachallthis:chest=y",
        Some((PRIM_3, "Business Pants (Pelvis)")),
        Setup::BusinessChest,
        LockMode::Attach,
        ALL_ALLOWED,
        &[],
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/AttachThisRestrictionTests.cs::AttachThisRestrictionTests.AttachThis::test 3a8a5acc4ad5cbec76737239ea23330a34f54146a4ff9d5d25082c220527a3e4 translated
#[test]
fn attach_this() {
    assert!(folder_case_matches(
        "@attachthis=n",
        Some((PRIM_3, "Party Hat (Spine)")),
        Setup::PartySpine,
        LockMode::Attach,
        HATS_LOCKED,
        &[FolderKey::Hats],
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/AttachThisRestrictionTests.cs::AttachThisRestrictionTests.AttachThis_NotRecursive::test a5818b4e30d04aba7d6427b2b94e025d34e82eb3bdcb7ac80a23e55f2382ec29 translated
#[test]
fn attach_this_not_recursive() {
    assert!(folder_case_matches(
        "@attachthis=n",
        Some((PRIM_1, "Business Pants (Pelvis)")),
        Setup::BusinessPelvis,
        LockMode::Attach,
        CLOTHING_LOCKED,
        &[FolderKey::Clothing],
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/AttachThisRestrictionTests.cs::AttachThisRestrictionTests.AttachThis_ByPath::test 93e1721f171e24e540160e738f731b895ebd868d840bcdf7c16ba92f6eb4c49a translated
#[test]
fn attach_this_by_path() {
    assert!(folder_case_matches(
        "@attachthis:Clothing/Hats=n",
        None,
        Setup::None,
        LockMode::Attach,
        HATS_LOCKED,
        &[FolderKey::Hats],
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/AttachThisRestrictionTests.cs::AttachThisRestrictionTests.AttachThis_ByRlvAttachmentPoint::test 90a562b2c120daa0ea2612befe9e09649bc2ffac59c2cd360fe4a37dedf85ae3 translated
#[test]
fn attach_this_by_attachment_point() {
    assert!(folder_case_matches(
        "@attachthis:pelvis=n",
        None,
        Setup::PartyAndBusinessPelvis,
        LockMode::Attach,
        CLOTHING_RECURSIVE_LOCKED,
        &[FolderKey::Clothing, FolderKey::Hats],
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/AttachThisRestrictionTests.cs::AttachThisRestrictionTests.AttachThis_ByWornLayer::test a444d51c2091fa36f1a010e98ad896042aa0262153ea8a833a14dfcbf02de85e translated
#[test]
fn attach_this_by_worn_layer() {
    assert!(folder_case_matches(
        "@attachthis:tattoo=n",
        None,
        Setup::WatchTattoo,
        LockMode::Attach,
        ACCESSORIES_LOCKED,
        &[FolderKey::Accessories],
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/AttachThisRestrictionTests.cs::AttachThisRestrictionTests.AttachThis_ByWornLayer_AddRem::test dd3d9a0d9b56f08f6be7a6200e1f50e834ecc7613de0cdf44ee89262b3abdb30 translated
#[test]
fn attach_this_by_worn_layer_removed() {
    assert!(folder_case_matches(
        "@attachthis:tattoo=n,@attachthis:tattoo=y",
        None,
        Setup::WatchTattoo,
        LockMode::Attach,
        ALL_ALLOWED,
        &[],
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/DetachAllThisRestrictionTests.cs::DetachAllThisRestrictionTests.DetachAllThis::test ee74f952bd602bd23cfc7b203940a902e1b30e02cff60c1abc1f74da964c1f7d translated
#[test]
fn detach_all_this() {
    assert!(folder_case_matches(
        "@detachallthis=n",
        Some((PRIM_3, "Party Hat (Spine)")),
        Setup::PartySpine,
        LockMode::Detach,
        HATS_LOCKED,
        &[FolderKey::Hats, FolderKey::SubHats],
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/DetachAllThisRestrictionTests.cs::DetachAllThisRestrictionTests.DetachAllThis_Recursive::test 2b6a646ee82484a6d1901ea4425b0e32fe553827f5bea37b506ffe29d1aec192 translated
#[test]
fn detach_all_this_recursive() {
    assert!(folder_case_matches(
        "@detachallthis=n",
        Some((PRIM_3, "Business Pants (Pelvis)")),
        Setup::BusinessPelvis,
        LockMode::Detach,
        CLOTHING_RECURSIVE_LOCKED,
        &[FolderKey::Clothing, FolderKey::Hats, FolderKey::SubHats],
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/DetachAllThisRestrictionTests.cs::DetachAllThisRestrictionTests.DetachAllThis_Recursive_Path::test 731244b623f23a8870d011280a062add751607ce2c58be3261485bb61d488297 translated
#[test]
fn detach_all_this_recursive_path() {
    assert!(folder_case_matches(
        "@detachallthis:Clothing=n",
        None,
        Setup::None,
        LockMode::Detach,
        CLOTHING_RECURSIVE_LOCKED,
        &[FolderKey::Clothing, FolderKey::Hats, FolderKey::SubHats],
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/DetachAllThisRestrictionTests.cs::DetachAllThisRestrictionTests.DetachAllThis_Recursive_Worn::test d8424a48e64346edd683b7a68652dd5a0be615658f085b0390699ac7b4db5d2c translated
#[test]
fn detach_all_this_recursive_worn() {
    assert!(folder_case_matches(
        "@detachallthis:pants=n",
        None,
        Setup::RetroPants,
        LockMode::Detach,
        CLOTHING_RECURSIVE_LOCKED,
        &[FolderKey::Clothing, FolderKey::Hats, FolderKey::SubHats],
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/DetachAllThisRestrictionTests.cs::DetachAllThisRestrictionTests.DetachAllThis_Recursive_Attached::test f399592217e7a4638ea25d469681ab9cc58bf8a937af2bfff8895996f7bbc493 translated
#[test]
fn detach_all_this_recursive_attached() {
    assert!(folder_case_matches(
        "@detachallthis:chest=n",
        None,
        Setup::BusinessChest,
        LockMode::Detach,
        CLOTHING_RECURSIVE_LOCKED,
        &[FolderKey::Clothing, FolderKey::Hats, FolderKey::SubHats],
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/DetachAllThisRestrictionTests.cs::DetachAllThisRestrictionTests.DetachAllThis_Recursive_Attached_AddRem::test 507273250b932ff93fa34f87190ad1ab289edeed275be6d2259bab05c5b055d1 translated
#[test]
fn detach_all_this_recursive_attached_removed() {
    assert!(folder_case_matches(
        "@detachallthis:chest=n,@detachallthis:chest=y",
        None,
        Setup::BusinessChest,
        LockMode::Detach,
        ALL_ALLOWED,
        &[],
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/DetachThisRestrictionTests.cs::DetachThisRestrictionTests.DetachThis::test bf6cdb48a5271d5116db4d0cbf12dbf63fc3cc93862bf718cf849d4abf9eb7cc translated
#[test]
fn detach_this() {
    assert!(folder_case_matches(
        "@detachthis=n",
        Some((PRIM_3, "Party Hat (Spine)")),
        Setup::PartySpine,
        LockMode::Detach,
        HATS_LOCKED,
        &[FolderKey::Hats],
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/DetachThisRestrictionTests.cs::DetachThisRestrictionTests.DetachThis_NotRecursive::test a8452e2b0abcf82447394bae981a1d25677147fad17828d8f6d0705264efa429 translated
#[test]
fn detach_this_not_recursive() {
    assert!(folder_case_matches(
        "@detachthis=n",
        Some((PRIM_3, "Business Pants (Pelvis)")),
        Setup::BusinessPelvis,
        LockMode::Detach,
        CLOTHING_LOCKED,
        &[FolderKey::Clothing],
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/DetachThisRestrictionTests.cs::DetachThisRestrictionTests.DetachThis_ByPath::test e6378e0db310005b8426cabd25969818ed36252e1995de97c6b29d5ab155ee58 translated
#[test]
fn detach_this_by_path() {
    assert!(folder_case_matches(
        "@detachthis:Clothing=n",
        None,
        Setup::None,
        LockMode::Detach,
        CLOTHING_LOCKED,
        &[FolderKey::Clothing],
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/DetachThisRestrictionTests.cs::DetachThisRestrictionTests.DetachThis_ByRlvAttachmentPoint::test 278e4cbf0f9999efec04fcabade19da97439cca732e0e7e3e3c004e232c27224 translated
#[test]
fn detach_this_by_attachment_point() {
    assert!(folder_case_matches(
        "@detachthis:pelvis=n",
        None,
        Setup::PartyAndBusinessPelvisDetach,
        LockMode::Detach,
        CLOTHING_RECURSIVE_LOCKED,
        &[FolderKey::Clothing, FolderKey::Hats],
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/DetachThisRestrictionTests.cs::DetachThisRestrictionTests.DetachThis_ByWornLayer::test 66b7eba54e34ed58c59d594cdcb85d874c4db9bbbd871e5438c40fd3d47ad3f6 translated
#[test]
fn detach_this_by_worn_layer() {
    assert!(folder_case_matches(
        "@detachthis:tattoo=n",
        None,
        Setup::WatchTattoo,
        LockMode::Detach,
        ACCESSORIES_LOCKED,
        &[FolderKey::Accessories],
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/DetachThisRestrictionTests.cs::DetachThisRestrictionTests.DetachThis_ByWornLayer_AddRem::test 7424bf38db1abd74132618ee3bcf95a79c32942da26086760dc82355a815d37e translated
#[test]
fn detach_this_by_worn_layer_removed() {
    assert!(folder_case_matches(
        "@detachthis:tattoo=n,@detachthis:tattoo=y",
        None,
        Setup::WatchTattoo,
        LockMode::Detach,
        ALL_ALLOWED,
        &[],
    ));
}
