// Exact Rust translations of the recursive folder exception tests.

use libremetaverse_compat_tests::block_on;
use libremetaverse_compat_tests::rlv_support::{RlvHarness, SampleInventoryTree, guid};
use libremetaverse_rlv::{InventoryMap, RlvAttachmentPoint};

const PRIM: &str = "11111111-0003-4aaa-8aaa-ffffffffffff";
const HATS_EXCEPTED: [bool; 7] = [true, true, false, false, false, true, true];
const CLOTHING_EXCEPTED: [bool; 7] = [true; 7];
const NO_EXCEPTIONS: [bool; 7] = [false, false, false, false, false, true, true];

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

fn exception_case_matches(
    commands: &[&str],
    mode: Mode,
    expected_items: [bool; 7],
    expected_exception_counts: Option<[usize; 3]>,
) -> bool {
    let h = RlvHarness::new().expect("RlvService constructor");
    let mut tree = SampleInventoryTree::build().expect("sample inventory tree");
    tree.business_pants
        .set_attached_to(Some(Some(RlvAttachmentPoint::Pelvis)));
    tree.business_pants
        .set_attached_prim_id(Some(Some(guid(PRIM))));
    let folder_ids = [tree.clothing.id(), tree.hats.id(), tree.sub_hats.id()];
    h.queries.lock().unwrap().inventory_map = Some(InventoryMap::new(tree.root, vec![]).unwrap());
    for command in commands {
        if !block_on(h.process_from(command, PRIM, "Business Pants (Pelvis)")).unwrap() {
            return false;
        }
    }

    let permissions = h.service.permissions();
    let actual = match mode {
        Mode::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(),
        ],
        Mode::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(),
        ],
    };
    if actual != expected_items {
        return false;
    }

    if let Some(expected_counts) = expected_exception_counts {
        let locked = h.service.restrictions().get_locked_folders().unwrap();
        if locked.len() != 3 {
            return false;
        }
        for (index, id) in folder_ids.iter().enumerate() {
            let Some(folder) = locked.get(id) else {
                return false;
            };
            let matches = folder.is_locked()
                && match mode {
                    Mode::Attach => {
                        folder.attach_exceptions().len() == expected_counts[index]
                            && folder.attach_restrictions().len() == 1
                            && folder.detach_exceptions().is_empty()
                            && folder.detach_restrictions().is_empty()
                    }
                    Mode::Detach => {
                        folder.attach_exceptions().is_empty()
                            && folder.attach_restrictions().is_empty()
                            && folder.detach_exceptions().len() == expected_counts[index]
                            && folder.detach_restrictions().len() == 1
                    }
                };
            if !matches {
                return false;
            }
        }
    }
    true
}

// parity-case: LibreMetaverse.Tests/RLV/Exceptions/AttachAllThisExceptExceptionTests.cs::AttachAllThisExceptExceptionTests.AttachAllThis_Recursive_ExceptAll::test 1daa414beb2a767518f335f6ae59e711395375ed9d7912fbbc139fa0998dfdb1 translated
#[test]
fn attach_all_this_except_subtree() {
    assert!(exception_case_matches(
        &[
            "@attachallthis=n",
            "@attachallthis_except:Clothing/Hats=add",
        ],
        Mode::Attach,
        HATS_EXCEPTED,
        Some([0, 1, 1]),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Exceptions/AttachAllThisExceptExceptionTests.cs::AttachAllThisExceptExceptionTests.AttachAllThis_Recursive_ExceptAll_Recursive::test 6e0c78d6ed78bedca9a8c6a9a83fd9f90f7123da433eb4c560e6f5c0b6fe6d94 translated
#[test]
fn attach_all_this_except_recursive_subtree() {
    assert!(exception_case_matches(
        &["@attachallthis=n", "@attachallthis_except:Clothing=add"],
        Mode::Attach,
        CLOTHING_EXCEPTED,
        Some([1, 1, 1]),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Exceptions/AttachAllThisExceptExceptionTests.cs::AttachAllThisExceptExceptionTests.AttachAllThis_Recursive_ExceptAll_Recursive_AddRem::test 9423ed205501604501d31909414ecf6ef99a0d56d4efae100f5576b31b71a858 translated
#[test]
fn attach_all_this_recursive_exception_removed() {
    assert!(exception_case_matches(
        &[
            "@attachallthis=n",
            "@attachallthis_except:Clothing=add",
            "@attachallthis_except:Clothing=rem",
        ],
        Mode::Attach,
        NO_EXCEPTIONS,
        Some([0, 0, 0]),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Exceptions/AttachThisExceptExceptionTests.cs::AttachThisExceptExceptionTests.AttachThisExcept_Default::test b8e6e55d8f2654429c9826e38ffc8522eec44ab2521891b68616db61ac662db0 translated
#[test]
fn attach_this_except_folder() {
    assert!(exception_case_matches(
        &["@attachallthis=n", "@attachthis_except:Clothing/Hats=add"],
        Mode::Attach,
        HATS_EXCEPTED,
        Some([0, 1, 0]),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Exceptions/AttachThisExceptExceptionTests.cs::AttachThisExceptExceptionTests.AttachThisExcept_AddRem::test 05bc7eab5dd02a0a3e60f85eb51b374623aaa29c9ce2b522748a3478b8ee44ea translated
#[test]
fn attach_this_exception_removed() {
    assert!(exception_case_matches(
        &[
            "@attachallthis=n",
            "@attachthis_except:Clothing/Hats=add",
            "@attachthis_except:Clothing/Hats=rem",
        ],
        Mode::Attach,
        NO_EXCEPTIONS,
        Some([0, 0, 0]),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Exceptions/DetachAllThisExceptExceptionTests.cs::DetachAllThisExceptExceptionTests.DetachAllThis_Recursive_ExceptAll::test 75e7ab22f6d9d058580fd83741d65f7e5babb7ad54805eaeb16bec8cd65370ed translated
#[test]
fn detach_all_this_except_subtree() {
    assert!(exception_case_matches(
        &[
            "@detachallthis=n",
            "@detachallthis_except:Clothing/Hats=add",
        ],
        Mode::Detach,
        HATS_EXCEPTED,
        None,
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Exceptions/DetachAllThisExceptExceptionTests.cs::DetachAllThisExceptExceptionTests.DetachAllThis_Recursive_ExceptAll_Recursive::test 3fb8bb13d6a917d9be18265d90767488cde66746badc6584025bbbaf84f020ad translated
#[test]
fn detach_all_this_except_recursive_subtree() {
    assert!(exception_case_matches(
        &["@detachallthis=n", "@detachallthis_except:Clothing=add"],
        Mode::Detach,
        CLOTHING_EXCEPTED,
        Some([1, 1, 1]),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Exceptions/DetachAllThisExceptExceptionTests.cs::DetachAllThisExceptExceptionTests.DetachAllThis_Recursive_ExceptAll_Recursive_AddRem::test fb5fd8a4eda181d22d1756ec0cae700c4b90895a74dc79c24c67c1705cfd37c8 translated
#[test]
fn detach_all_this_recursive_exception_removed() {
    assert!(exception_case_matches(
        &[
            "@detachallthis=n",
            "@detachallthis_except:Clothing=add",
            "@detachallthis_except:Clothing=rem",
        ],
        Mode::Detach,
        NO_EXCEPTIONS,
        Some([0, 0, 0]),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Exceptions/DetachThisExceptExceptionTests.cs::DetachThisExceptExceptionTests.DetachAllThis_Recursive_Except::test 3222d97d73d8b54bd11212f8cd3de26279040321f0bc330e061ff93880053f3e translated
#[test]
fn detach_this_except_folder() {
    assert!(exception_case_matches(
        &["@detachallthis=n", "@detachthis_except:Clothing/Hats=add"],
        Mode::Detach,
        HATS_EXCEPTED,
        Some([0, 1, 0]),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/Exceptions/DetachThisExceptExceptionTests.cs::DetachThisExceptExceptionTests.DetachAllThis_Recursive_Except_AddRem::test d25b2c533b9b51428c0c2414d687c466a3c80c4a573dd36763504de19e8dc6f8 translated
#[test]
fn detach_this_exception_removed() {
    assert!(exception_case_matches(
        &[
            "@detachallthis=n",
            "@detachthis_except:Clothing/Hats=add",
            "@detachthis_except:Clothing/Hats=rem",
        ],
        Mode::Detach,
        NO_EXCEPTIONS,
        Some([0, 0, 0]),
    ));
}
