//! Comprehensive concurrency and race condition tests.
//!
//! These tests verify thread safety, data race prevention, and correct
//! behavior under concurrent access patterns.

use mockforge_core::conditions::{evaluate_condition, ConditionContext};
use mockforge_core::routing::{HttpMethod, Route, RouteRegistry};
use mockforge_core::templating::expand_str;
use mockforge_core::validation::validate_json_schema;
use serde_json::json;
use std::sync::{Arc, Mutex};
use std::thread;

#[cfg(test)]
mod route_registry_concurrency {
    use super::*;

    #[test]
    fn concurrent_route_addition() {
        let registry = Arc::new(Mutex::new(RouteRegistry::new()));
        let mut handles = vec![];

        // Spawn multiple threads adding routes concurrently
        for i in 0..20 {
            let registry_clone = Arc::clone(&registry);
            let handle = thread::spawn(move || {
                for j in 0..50 {
                    let route = Route::new(HttpMethod::GET, format!("/api/route_{}_{}", i, j));
                    let mut reg = registry_clone.lock().unwrap();
                    let _ = reg.add_http_route(route);
                }
            });
            handles.push(handle);
        }

        // Wait for all threads
        for handle in handles {
            handle.join().unwrap();
        }

        // Verify all routes were added by checking we can find them
        let reg = registry.lock().unwrap();
        // Try to find routes we added - if they exist, they were added successfully
        // Just verify no panic by calling the method
        let _ = reg.find_http_routes(&HttpMethod::GET, "/api/route_0_0");
    }

    #[test]
    fn concurrent_route_addition_and_lookup() {
        let registry = Arc::new(Mutex::new(RouteRegistry::new()));
        let mut handles = vec![];

        // Threads that add routes
        for i in 0..10 {
            let registry_clone = Arc::clone(&registry);
            let handle = thread::spawn(move || {
                for j in 0..100 {
                    let route = Route::new(HttpMethod::GET, format!("/api/add_{}_{}", i, j));
                    let mut reg = registry_clone.lock().unwrap();
                    let _ = reg.add_http_route(route);
                }
            });
            handles.push(handle);
        }

        // Threads that look up routes
        for _ in 0..10 {
            let registry_clone = Arc::clone(&registry);
            let handle = thread::spawn(move || {
                for _ in 0..100 {
                    let reg = registry_clone.lock().unwrap();
                    let _ = reg.find_http_routes(&HttpMethod::GET, "/api/add_0_0");
                }
            });
            handles.push(handle);
        }

        // Wait for all threads
        for handle in handles {
            handle.join().unwrap();
        }
    }

    #[test]
    fn concurrent_route_clear_and_add() {
        let registry = Arc::new(Mutex::new(RouteRegistry::new()));
        let mut handles = vec![];

        // Add some initial routes
        {
            let mut reg = registry.lock().unwrap();
            for i in 0..10 {
                let route = Route::new(HttpMethod::GET, format!("/api/route_{}", i));
                let _ = reg.add_http_route(route);
            }
        }

        // Threads that clear and add routes
        for i in 0..5 {
            let registry_clone = Arc::clone(&registry);
            let handle = thread::spawn(move || {
                // Clear
                {
                    let mut reg = registry_clone.lock().unwrap();
                    reg.clear();
                }
                // Add new routes
                for j in 0..20 {
                    let route = Route::new(HttpMethod::GET, format!("/api/new_{}_{}", i, j));
                    let mut reg = registry_clone.lock().unwrap();
                    let _ = reg.add_http_route(route);
                }
            });
            handles.push(handle);
        }

        // Wait for all threads
        for handle in handles {
            handle.join().unwrap();
        }
    }

    #[test]
    fn concurrent_wildcard_route_matching() {
        let registry = Arc::new(Mutex::new(RouteRegistry::new()));

        // Add wildcard routes
        {
            let mut reg = registry.lock().unwrap();
            let route1 = Route::new(HttpMethod::GET, "/api/*/users".to_string());
            let route2 = Route::new(HttpMethod::GET, "/api/users/*".to_string());
            let route3 = Route::new(HttpMethod::GET, "/api/*/users/*".to_string());
            let _ = reg.add_http_route(route1);
            let _ = reg.add_http_route(route2);
            let _ = reg.add_http_route(route3);
        }

        let mut handles = vec![];

        // Multiple threads matching routes concurrently
        for _ in 0..20 {
            let registry_clone = Arc::clone(&registry);
            let handle = thread::spawn(move || {
                for _ in 0..100 {
                    let reg = registry_clone.lock().unwrap();
                    let _ = reg.find_http_routes(&HttpMethod::GET, "/api/v1/users");
                    let _ = reg.find_http_routes(&HttpMethod::GET, "/api/users/123");
                    let _ = reg.find_http_routes(&HttpMethod::GET, "/api/v1/users/123");
                }
            });
            handles.push(handle);
        }

        // Wait for all threads
        for handle in handles {
            handle.join().unwrap();
        }
    }
}

#[cfg(test)]
mod condition_evaluation_concurrency {
    use super::*;

    #[test]
    fn concurrent_condition_evaluation() {
        let context = Arc::new(ConditionContext::new());
        let mut handles = vec![];

        // Multiple threads evaluating conditions concurrently
        for _ in 0..20 {
            let context_clone = Arc::clone(&context);
            let handle = thread::spawn(move || {
                for _ in 0..100 {
                    let _ = evaluate_condition("true", &context_clone);
                    let _ = evaluate_condition("false", &context_clone);
                    let _ = evaluate_condition("$.field == value", &context_clone);
                    let _ = evaluate_condition("headers.test.exists", &context_clone);
                }
            });
            handles.push(handle);
        }

        // Wait for all threads
        for handle in handles {
            handle.join().unwrap();
        }
    }

    #[test]
    fn concurrent_condition_evaluation_with_shared_context() {
        let context = Arc::new(
            ConditionContext::new()
                .with_request_body(json!({"id": 123, "name": "test"}))
                .with_response_body(json!({"status": "ok"})),
        );

        let mut handles = vec![];

        // Multiple threads evaluating different conditions on same context
        for i in 0..20 {
            let context_clone = Arc::clone(&context);
            let handle = thread::spawn(move || {
                for j in 0..50 {
                    let condition = format!("$.id == {}", i * 50 + j);
                    let _ = evaluate_condition(&condition, &context_clone);
                }
            });
            handles.push(handle);
        }

        // Wait for all threads
        for handle in handles {
            handle.join().unwrap();
        }
    }

    #[test]
    fn concurrent_complex_condition_evaluation() {
        let context = Arc::new(ConditionContext::new().with_request_body(json!({
            "user": {"id": 123, "name": "test"},
            "items": [1, 2, 3, 4, 5]
        })));

        let mut handles = vec![];

        // Multiple threads evaluating complex conditions
        for _ in 0..10 {
            let context_clone = Arc::clone(&context);
            let handle = thread::spawn(move || {
                for _ in 0..100 {
                    let _ = evaluate_condition("$.user.id == 123", &context_clone);
                    let _ = evaluate_condition("$.items.length > 0", &context_clone);
                    let _ = evaluate_condition(
                        "AND($.user.id == 123, $.items.length > 0)",
                        &context_clone,
                    );
                    let _ = evaluate_condition(
                        "OR($.user.id == 123, $.items.length == 0)",
                        &context_clone,
                    );
                }
            });
            handles.push(handle);
        }

        // Wait for all threads
        for handle in handles {
            handle.join().unwrap();
        }
    }
}

#[cfg(test)]
mod validation_concurrency {
    use super::*;

    #[test]
    fn concurrent_schema_validation() {
        let schema = Arc::new(json!({
            "type": "object",
            "properties": {
                "id": {"type": "integer"},
                "name": {"type": "string"}
            },
            "required": ["id", "name"]
        }));

        let mut handles = vec![];

        // Multiple threads validating data concurrently
        for i in 0..20 {
            let schema_clone = Arc::clone(&schema);
            let handle = thread::spawn(move || {
                for j in 0..50 {
                    let data = json!({
                        "id": i * 50 + j,
                        "name": format!("test_{}", j)
                    });
                    let _ = validate_json_schema(&data, &schema_clone);
                }
            });
            handles.push(handle);
        }

        // Wait for all threads
        for handle in handles {
            handle.join().unwrap();
        }
    }

    #[test]
    fn concurrent_validation_with_different_schemas() {
        let mut handles = vec![];

        // Each thread validates with its own schema
        for i in 0..10 {
            let handle = thread::spawn(move || {
                let schema = json!({
                    "type": "object",
                    "properties": {
                        "value": {"type": "integer", "minimum": 0, "maximum": 1000}
                    }
                });

                for j in 0..100 {
                    let data = json!({
                        "value": (i * 100 + j) % 1000
                    });
                    let _ = validate_json_schema(&data, &schema);
                }
            });
            handles.push(handle);
        }

        // Wait for all threads
        for handle in handles {
            handle.join().unwrap();
        }
    }
}

#[cfg(test)]
mod template_expansion_concurrency {
    use super::*;

    #[test]
    fn concurrent_template_expansion() {
        let mut handles = vec![];

        // Multiple threads expanding templates concurrently
        for _i in 0..20 {
            let handle = thread::spawn(move || {
                for j in 0..50 {
                    let template = format!("Hello {{name}}_{}", j);
                    let _ = expand_str(&template);
                }
            });
            handles.push(handle);
        }

        // Wait for all threads
        for handle in handles {
            handle.join().unwrap();
        }
    }

    #[test]
    fn concurrent_complex_template_expansion() {
        let mut handles = vec![];

        // Multiple threads expanding complex templates
        for _ in 0..10 {
            let handle = thread::spawn(move || {
                for _ in 0..100 {
                    let templates = vec![
                        "{{field}}",
                        "{{nested.field}}",
                        "{{array.0}}",
                        "{{field}}_{{other}}",
                        "{{#if condition}}{{value}}{{/if}}",
                    ];

                    for template in templates {
                        let _ = expand_str(template);
                    }
                }
            });
            handles.push(handle);
        }

        // Wait for all threads
        for handle in handles {
            handle.join().unwrap();
        }
    }
}

#[cfg(test)]
mod mixed_operations_concurrency {
    use super::*;

    #[test]
    fn concurrent_mixed_operations() {
        let registry = Arc::new(Mutex::new(RouteRegistry::new()));
        let context = Arc::new(ConditionContext::new());
        let schema = Arc::new(json!({
            "type": "object",
            "properties": {
                "test": {"type": "string"}
            }
        }));

        let mut handles = vec![];

        // Threads performing different operations concurrently
        for i in 0..5 {
            let registry_clone = Arc::clone(&registry);
            let handle = thread::spawn(move || {
                // Add routes
                for j in 0..20 {
                    let route = Route::new(HttpMethod::GET, format!("/api/route_{}_{}", i, j));
                    let mut reg = registry_clone.lock().unwrap();
                    let _ = reg.add_http_route(route);
                }
            });
            handles.push(handle);
        }

        for _ in 0..5 {
            let context_clone = Arc::clone(&context);
            let handle = thread::spawn(move || {
                // Evaluate conditions
                for _ in 0..100 {
                    let _ = evaluate_condition("true", &context_clone);
                }
            });
            handles.push(handle);
        }

        for _ in 0..5 {
            let schema_clone = Arc::clone(&schema);
            let handle = thread::spawn(move || {
                // Validate schemas
                for _ in 0..100 {
                    let data = json!({"test": "value"});
                    let _ = validate_json_schema(&data, &schema_clone);
                }
            });
            handles.push(handle);
        }

        // Wait for all threads
        for handle in handles {
            handle.join().unwrap();
        }
    }
}

#[cfg(test)]
mod stress_tests {
    use super::*;

    #[test]
    fn high_contention_route_registry() {
        let registry = Arc::new(Mutex::new(RouteRegistry::new()));
        let mut handles = vec![];

        // Many threads competing for the same lock
        for i in 0..50 {
            let registry_clone = Arc::clone(&registry);
            let handle = thread::spawn(move || {
                for j in 0..100 {
                    // Add route
                    {
                        let route = Route::new(HttpMethod::GET, format!("/api/route_{}_{}", i, j));
                        let mut reg = registry_clone.lock().unwrap();
                        let _ = reg.add_http_route(route);
                    }
                    // Lookup route
                    {
                        let reg = registry_clone.lock().unwrap();
                        let _ = reg.find_http_routes(&HttpMethod::GET, "/api/route_0_0");
                    }
                }
            });
            handles.push(handle);
        }

        // Wait for all threads
        for handle in handles {
            handle.join().unwrap();
        }

        // Verify final state by checking we can find routes (no panic)
        let reg = registry.lock().unwrap();
        let _ = reg.find_http_routes(&HttpMethod::GET, "/api/route_0_0");
    }

    #[test]
    fn rapid_route_churn() {
        let registry = Arc::new(Mutex::new(RouteRegistry::new()));
        let mut handles = vec![];

        // Threads rapidly adding and removing routes
        for i in 0..10 {
            let registry_clone = Arc::clone(&registry);
            let handle = thread::spawn(move || {
                for _ in 0..50 {
                    // Add routes
                    for j in 0..10 {
                        let route = Route::new(HttpMethod::GET, format!("/api/route_{}_{}", i, j));
                        let mut reg = registry_clone.lock().unwrap();
                        let _ = reg.add_http_route(route);
                    }
                    // Clear and re-add
                    {
                        let mut reg = registry_clone.lock().unwrap();
                        reg.clear();
                    }
                }
            });
            handles.push(handle);
        }

        // Wait for all threads
        for handle in handles {
            handle.join().unwrap();
        }
    }
}

#[cfg(test)]
mod data_race_prevention {
    use super::*;

    #[test]
    fn verify_no_data_races_in_condition_evaluation() {
        // This test verifies that condition evaluation doesn't have data races
        // by running many concurrent evaluations and checking for consistency
        let context = Arc::new(ConditionContext::new().with_request_body(json!({"value": 42})));

        let mut handles = vec![];

        // Many threads evaluating the same condition
        for _ in 0..100 {
            let context_clone = Arc::clone(&context);
            let handle = thread::spawn(move || {
                // All threads should get the same result
                // Result should be consistent (all true or all false, not mixed)
                evaluate_condition("$.value == 42", &context_clone)
            });
            handles.push(handle);
        }

        // Collect results
        let mut results = vec![];
        for handle in handles {
            results.push(handle.join().unwrap());
        }

        // All results should be the same (no data race)
        // Extract the bool values from Results for comparison
        let first_result = results[0].as_ref().ok();
        for result in results.iter().skip(1) {
            let result_bool = result.as_ref().ok();
            assert_eq!(
                result_bool, first_result,
                "Condition evaluation should be consistent across threads"
            );
        }
    }

    #[test]
    fn verify_route_registry_consistency() {
        let registry = Arc::new(Mutex::new(RouteRegistry::new()));

        // Add initial routes
        {
            let mut reg = registry.lock().unwrap();
            for i in 0..10 {
                let route = Route::new(HttpMethod::GET, format!("/api/route_{}", i));
                let _ = reg.add_http_route(route);
            }
        }

        let mut handles = vec![];

        // Multiple threads reading routes
        for _ in 0..20 {
            let registry_clone = Arc::clone(&registry);
            let handle = thread::spawn(move || {
                let reg = registry_clone.lock().unwrap();
                let routes = reg.find_http_routes(&HttpMethod::GET, "/api/route_0");
                // Should always find at least one route
                assert!(!routes.is_empty());
            });
            handles.push(handle);
        }

        // Wait for all threads
        for handle in handles {
            handle.join().unwrap();
        }
    }
}
