// Exact Rust translations of the C# tests that deliberately invoke internal handlers.
// Source SHA-256: 74fa1275cd1f6c2a372af2fefc011a3e764f028a8957291d61f599bcb023a8ff
// Source SHA-256: 7b67b72c2ad652626126d0e4eda7e8c26fc94c28f0f915238b2925f80a3d4af2

use super::*;
use libremetaverse_structured_data::{OSD, OSDMap};
use libremetaverse_types::UUID;
use packets::{
    EstateOwnerMessagePacket, EstateOwnerMessagePacketAgentDataBlock,
    EstateOwnerMessagePacketMethodDataBlock, EstateOwnerMessagePacketParamListBlock,
};
use std::collections::HashMap;
use std::future::Future;
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll, Wake, Waker};
use std::time::Duration;

fn uuid() -> UUID {
    UUID::random().expect("UUID Random")
}

fn ids(values: impl IntoIterator<Item = UUID>) -> OSD {
    OSD::Array(values.into_iter().map(OSD::UUID).collect())
}

fn map(entries: impl IntoIterator<Item = (&'static str, OSD)>) -> OSDMap {
    OSDMap::new_with_dictionary(
        entries
            .into_iter()
            .map(|(key, value)| (key.into(), value))
            .collect(),
    )
    .expect("OSDMap dictionary constructor")
}

fn simulator() -> Simulator {
    Simulator::new(
        GridClient::new().expect("simulator GridClient constructor"),
        "127.0.0.1:13".parse().unwrap(),
        0,
        None,
        None,
    )
    .expect("Simulator constructor")
}

fn estate_packet(blocked: &[UUID], trusted: &[UUID], allowed: &[UUID]) -> EstateOwnerMessagePacket {
    let counts = [
        "1".to_owned(),
        "0".to_owned(),
        blocked.len().to_string(),
        trusted.len().to_string(),
        allowed.len().to_string(),
    ];
    let mut parameters = counts
        .into_iter()
        .map(|value| EstateOwnerMessagePacketParamListBlock {
            parameter: value.as_bytes().to_vec(),
        })
        .collect::<Vec<_>>();
    parameters.extend(blocked.iter().chain(trusted).chain(allowed).map(|id| {
        EstateOwnerMessagePacketParamListBlock {
            parameter: id.get_bytes().expect("UUID bytes"),
        }
    }));
    EstateOwnerMessagePacket {
        agent_data: EstateOwnerMessagePacketAgentDataBlock {
            agent_id: uuid(),
            session_id: uuid(),
            transaction_id: uuid(),
        },
        method_data: EstateOwnerMessagePacketMethodDataBlock {
            method: b"setexperience".to_vec(),
            invoice: UUID::zero(),
        },
        param_list: parameters,
    }
}

fn block_on_with<F: Future>(future: F, mut after_pending: impl FnMut()) -> F::Output {
    struct ThreadWake(std::thread::Thread);
    impl Wake for ThreadWake {
        fn wake(self: Arc<Self>) {
            self.0.unpark();
        }
    }
    let waker = Waker::from(Arc::new(ThreadWake(std::thread::current())));
    let mut context = Context::from_waker(&waker);
    let mut future = std::pin::pin!(future);
    let mut invoked = false;
    loop {
        match future.as_mut().poll(&mut context) {
            Poll::Ready(value) => return value,
            Poll::Pending if !invoked => {
                invoked = true;
                after_pending();
            }
            Poll::Pending => std::thread::park_timeout(Duration::from_millis(10)),
        }
    }
}

// parity-case: LibreMetaverse.Tests/EstateExperienceReplyTests.cs::EstateExperienceReplyTests.SetExperience_ParsesBlockedTrustedAndAllowedLists::test 49a77aa9d7c5bf1232082a08594e7ebcfafad11ab110c9e15d2ad4ea1831c649 translated
#[test]
fn set_experience_parses_blocked_trusted_and_allowed_lists() {
    let blocked = uuid();
    let trusted = uuid();
    let allowed = [uuid(), uuid()];
    let client = GridClient::new().expect("GridClient constructor");
    let estate = client.estate();
    let received = Arc::new(Mutex::new(None));
    let captured = Arc::clone(&received);
    let _subscription = estate.subscribe_estate_experience_reply(Arc::new(move |reply| {
        *captured.lock().unwrap() = Some((reply.blocked(), reply.trusted(), reply.allowed()));
    }));
    estate
        .estate_owner_message_handler(estate_packet(&[blocked], &[trusted], &allowed), simulator())
        .unwrap();
    let reply = received.lock().unwrap();
    let (actual_blocked, actual_trusted, actual_allowed) =
        reply.as_ref().expect("EstateExperienceReply event");
    assert_eq!(actual_blocked, &[blocked]);
    assert_eq!(actual_trusted, &[trusted]);
    assert_eq!(actual_allowed, &allowed);
}

// parity-case: LibreMetaverse.Tests/EstateExperienceReplyTests.cs::EstateExperienceReplyTests.SetExperience_AllEmptyLists_ProducesEmptyResult::test 711eaebb11f5d7ece92946d3de240a341a4b1fd8f30617fd97cb059d0dc6a0b9 translated
#[test]
fn set_experience_all_empty_lists_produces_empty_result() {
    let client = GridClient::new().expect("GridClient constructor");
    let estate = client.estate();
    let received = Arc::new(Mutex::new(None));
    let captured = Arc::clone(&received);
    let _subscription = estate.subscribe_estate_experience_reply(Arc::new(move |reply| {
        *captured.lock().unwrap() = Some((reply.blocked(), reply.trusted(), reply.allowed()));
    }));
    estate
        .estate_owner_message_handler(estate_packet(&[], &[], &[]), simulator())
        .unwrap();
    let reply = received.lock().unwrap();
    let (blocked, trusted, allowed) = reply.as_ref().expect("EstateExperienceReply event");
    assert!(blocked.is_empty());
    assert!(trusted.is_empty());
    assert!(allowed.is_empty());
}

// parity-case: LibreMetaverse.Tests/ExperiencePreferencesMessageTests.cs::ExperiencePreferencesMessageTests.ExtractExperiencePermission_Allowed_ReturnsAllow::test cf095ab428713004ebac6e1915a47ae0c03bd2958addbba2a39fd2f0edf51a74 translated
#[test]
fn extract_experience_permission_allowed_returns_allow() {
    let id = uuid();
    assert_eq!(
        AgentManager::extract_experience_permission(map([("experiences", ids([id]))]), id).unwrap(),
        "Allow"
    );
}

// parity-case: LibreMetaverse.Tests/ExperiencePreferencesMessageTests.cs::ExperiencePreferencesMessageTests.ExtractExperiencePermission_Blocked_ReturnsBlock::test af084d1ab7a11079e6251865e8fd5aecfde38d2df359ebc1e208bc74925935c0 translated
#[test]
fn extract_experience_permission_blocked_returns_block() {
    let id = uuid();
    assert_eq!(
        AgentManager::extract_experience_permission(map([("blocked", ids([id]))]), id).unwrap(),
        "Block"
    );
}

// parity-case: LibreMetaverse.Tests/ExperiencePreferencesMessageTests.cs::ExperiencePreferencesMessageTests.ExtractExperiencePermission_NeitherList_ReturnsForget::test 93204469ecfec2f3f4eb6838c99f6e2baf09bf76a075958d1ed369ba03767158 translated
#[test]
fn extract_experience_permission_neither_list_returns_forget() {
    let id = uuid();
    let input = OSDMap::new_with_dictionary(HashMap::from([
        ("experiences".into(), ids([uuid()])),
        ("blocked".into(), ids([uuid()])),
    ]))
    .expect("OSDMap dictionary constructor");
    assert_eq!(
        AgentManager::extract_experience_permission(input, id).unwrap(),
        "Forget"
    );
}

// parity-case: LibreMetaverse.Tests/SimConsoleTests.cs::SimConsoleTests.SendSimConsoleCommandAsync_IgnoresPostBody_UsesSimConsoleResponseEvent::test df6eb48241f71c34074be66869c9ae1d5980186583259568728261b5eedd7c48 translated
#[test]
fn send_sim_console_command_ignores_post_body_and_uses_response_event() {
    let client = GridClient::new().expect("GridClient constructor");
    let estate = client.estate();
    let network = client.network();
    let result = block_on_with(
        estate.send_sim_console_command("show name".into(), Some(Duration::from_secs(5)), None),
        || {
            let mut message = messages::linden::SimConsoleResponseMessage::new()
                .expect("SimConsoleResponseMessage constructor");
            message.body = "Agent Name".into();
            network
                .raise_sim_console_response(message, simulator())
                .unwrap();
        },
    )
    .unwrap();
    assert_eq!(result.as_deref(), Some("Agent Name"));
}

// parity-case: LibreMetaverse.Tests/SimConsoleTests.cs::SimConsoleTests.SendSimConsoleCommandAsync_NoResponseEvent_ReturnsNullAfterTimeout::test 2ac5a6c96f3acec341056387d727288fd3661565bd2a7c385078602efe8661da translated
#[test]
fn send_sim_console_command_no_response_event_returns_none_after_timeout() {
    let client = GridClient::new().expect("GridClient constructor");
    let result = block_on_with(
        client.estate().send_sim_console_command(
            "show name".into(),
            Some(Duration::from_millis(200)),
            None,
        ),
        || {},
    )
    .unwrap();
    assert!(result.is_none());
}
