//! Integration test for concurrent agent requests (T050)
//!
//! This test verifies that the system can handle multiple agent requests
//! simultaneously without cross-contamination.
//!
//! Test scenario:
//! 1. Register multiple pending requests concurrently
//! 2. Complete them in random/different order
//! 3. Verify each response goes to the correct request
//! 4. Verify no cross-contamination

use dirigent_acp_api::agent_requests::AgentRequestTracker;
use serde_json::json;
use tokio::task::JoinSet;

#[tokio::test]
async fn test_concurrent_requests_basic() {
    let tracker = AgentRequestTracker::new();

    let client_id = "test-client";

    // Register 5 concurrent requests
    let mut receivers = Vec::new();
    for i in 0..5 {
        let rx = tracker.register(client_id, json!(i));
        receivers.push((i, rx));
    }

    assert_eq!(tracker.pending_count(), 5);

    // Complete them in reverse order
    for i in (0..5).rev() {
        let response = json!({"request": i, "result": "success"});
        tracker.complete(client_id, json!(i), response).unwrap();
    }

    assert_eq!(tracker.pending_count(), 0);

    // Verify each receiver got the correct response
    for (i, rx) in receivers {
        let response = rx.await.unwrap();
        assert_eq!(response["request"], i);
    }
}

#[tokio::test]
async fn test_concurrent_requests_multiple_clients() {
    let tracker = AgentRequestTracker::new();

    // Two clients, each with 3 requests
    let client1 = "client-1";
    let client2 = "client-2";

    let mut receivers1 = Vec::new();
    let mut receivers2 = Vec::new();

    for i in 0..3 {
        let rx1 = tracker.register(client1, json!(i));
        let rx2 = tracker.register(client2, json!(i));
        receivers1.push((i, rx1));
        receivers2.push((i, rx2));
    }

    assert_eq!(tracker.pending_count(), 6);
    assert_eq!(tracker.client_pending_count(client1), 3);
    assert_eq!(tracker.client_pending_count(client2), 3);

    // Complete client1's requests
    for i in 0..3 {
        let response = json!({"client": 1, "request": i});
        tracker.complete(client1, json!(i), response).unwrap();
    }

    assert_eq!(tracker.client_pending_count(client1), 0);
    assert_eq!(tracker.client_pending_count(client2), 3);

    // Complete client2's requests
    for i in 0..3 {
        let response = json!({"client": 2, "request": i});
        tracker.complete(client2, json!(i), response).unwrap();
    }

    assert_eq!(tracker.pending_count(), 0);

    // Verify each receiver got the correct response
    for (i, rx) in receivers1 {
        let response = rx.await.unwrap();
        assert_eq!(response["client"], 1);
        assert_eq!(response["request"], i);
    }

    for (i, rx) in receivers2 {
        let response = rx.await.unwrap();
        assert_eq!(response["client"], 2);
        assert_eq!(response["request"], i);
    }
}

#[tokio::test]
async fn test_concurrent_requests_same_id_different_clients() {
    // Test that same request_id for different clients are handled independently
    let tracker = AgentRequestTracker::new();

    let client1 = "client-1";
    let client2 = "client-2";
    let request_id = json!(0); // Same ID for both

    let rx1 = tracker.register(client1, request_id.clone());
    let rx2 = tracker.register(client2, request_id.clone());

    assert_eq!(tracker.pending_count(), 2);

    // Complete client1's request
    let response1 = json!({"client": "client-1"});
    tracker.complete(client1, request_id.clone(), response1.clone()).unwrap();

    // Complete client2's request
    let response2 = json!({"client": "client-2"});
    tracker.complete(client2, request_id, response2.clone()).unwrap();

    assert_eq!(tracker.pending_count(), 0);

    // Verify each got the correct response
    let received1 = rx1.await.unwrap();
    let received2 = rx2.await.unwrap();

    assert_eq!(received1["client"], "client-1");
    assert_eq!(received2["client"], "client-2");
}

#[tokio::test]
async fn test_concurrent_async_completion() {
    // Test completing requests from multiple async tasks concurrently
    let tracker = AgentRequestTracker::new();

    let client_id = "test-client";
    let num_requests = 10;

    // Register requests
    let mut receivers = Vec::new();
    for i in 0..num_requests {
        let rx = tracker.register(client_id, json!(i));
        receivers.push((i, rx));
    }

    assert_eq!(tracker.pending_count(), num_requests);

    // Spawn tasks to complete requests concurrently
    let mut join_set = JoinSet::new();

    for i in 0..num_requests {
        let tracker_clone = tracker.clone();
        join_set.spawn(async move {
            // Small delay to ensure concurrency
            tokio::time::sleep(tokio::time::Duration::from_millis(((i % 3) * 10) as u64)).await;
            let response = json!({"request": i, "result": "success"});
            tracker_clone.complete(client_id, json!(i), response)
        });
    }

    // Wait for all completions
    while let Some(result) = join_set.join_next().await {
        assert!(result.unwrap().is_ok());
    }

    assert_eq!(tracker.pending_count(), 0);

    // Verify all receivers got correct responses
    for (i, rx) in receivers {
        let response = rx.await.unwrap();
        assert_eq!(response["request"], i);
    }
}

#[tokio::test]
async fn test_concurrent_register_and_complete() {
    // Test registering and completing requests concurrently
    let tracker = AgentRequestTracker::new();

    let client_id = "test-client";
    let num_requests = 20;

    let mut join_set = JoinSet::new();

    // Spawn tasks to register and complete requests
    for i in 0..num_requests {
        let tracker_clone = tracker.clone();
        join_set.spawn(async move {
            // Register
            let rx = tracker_clone.register(client_id, json!(i));

            // Small delay
            tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;

            // Complete
            let response = json!({"request": i});
            tracker_clone.complete(client_id, json!(i), response.clone()).unwrap();

            // Wait for response
            let received = rx.await.unwrap();
            assert_eq!(received["request"], i);

            i
        });
    }

    // Wait for all tasks
    let mut completed = Vec::new();
    while let Some(result) = join_set.join_next().await {
        completed.push(result.unwrap());
    }

    // All requests should have completed
    assert_eq!(completed.len(), num_requests);
    assert_eq!(tracker.pending_count(), 0);
}

#[tokio::test]
async fn test_concurrent_mixed_operations() {
    // Test mix of register, complete, and timeout operations
    let tracker = AgentRequestTracker::new();

    let client_id = "test-client";

    let mut join_set = JoinSet::new();

    // Spawn 15 tasks with different behaviors
    for i in 0..15 {
        let tracker_clone = tracker.clone();
        join_set.spawn(async move {
            let rx = tracker_clone.register(client_id, json!(i));

            tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;

            match i % 3 {
                0 => {
                    // Complete normally
                    let response = json!({"request": i, "type": "complete"});
                    tracker_clone.complete(client_id, json!(i), response.clone()).unwrap();
                    let received = rx.await.unwrap();
                    assert_eq!(received["type"], "complete");
                    "completed"
                }
                1 => {
                    // Timeout
                    tracker_clone.timeout(client_id, json!(i));
                    assert!(rx.await.is_err());
                    "timeout"
                }
                _ => {
                    // Complete with delay
                    tokio::time::sleep(tokio::time::Duration::from_millis(20)).await;
                    let response = json!({"request": i, "type": "delayed"});
                    tracker_clone.complete(client_id, json!(i), response.clone()).unwrap();
                    let received = rx.await.unwrap();
                    assert_eq!(received["type"], "delayed");
                    "delayed"
                }
            }
        });
    }

    // Wait for all tasks
    let mut results = Vec::new();
    while let Some(result) = join_set.join_next().await {
        results.push(result.unwrap());
    }

    assert_eq!(results.len(), 15);

    // Count outcomes
    let completed = results.iter().filter(|&r| r == &"completed").count();
    let timeout = results.iter().filter(|&r| r == &"timeout").count();
    let delayed = results.iter().filter(|&r| r == &"delayed").count();

    assert_eq!(completed, 5);
    assert_eq!(timeout, 5);
    assert_eq!(delayed, 5);

    // All should be cleaned up
    assert_eq!(tracker.pending_count(), 0);
}

#[tokio::test]
async fn test_high_concurrency() {
    // Stress test with many concurrent requests
    let tracker = AgentRequestTracker::new();

    let client_id = "test-client";
    let num_requests = 100;

    let mut join_set = JoinSet::new();

    for i in 0..num_requests {
        let tracker_clone = tracker.clone();
        join_set.spawn(async move {
            let rx = tracker_clone.register(client_id, json!(i));

            // Random-ish delay
            let delay = ((i * 7) % 20) as u64;
            tokio::time::sleep(tokio::time::Duration::from_millis(delay)).await;

            let response = json!({"request": i});
            tracker_clone.complete(client_id, json!(i), response).unwrap();

            rx.await.unwrap()["request"].as_u64().unwrap()
        });
    }

    // Collect all results
    let mut results = Vec::new();
    while let Some(result) = join_set.join_next().await {
        results.push(result.unwrap());
    }

    // Verify all requests completed
    assert_eq!(results.len(), num_requests);

    // Verify all request IDs are present
    results.sort();
    for (idx, &val) in results.iter().enumerate() {
        assert_eq!(val, idx as u64);
    }

    // All cleaned up
    assert_eq!(tracker.pending_count(), 0);
}
