//! Phase 8d: End-to-End Integration Tests
//!
//! Comprehensive integration tests covering complete workflows across all Phase
//! 7-8 features:
//! - Multi-turn conversations with hybrid search
//! - Batch processing with caching
//! - Tool execution with orchestration
//! - Failure scenarios and recovery
//! - API + Orchestrator integration
//! - Performance under load

use std::sync::Arc;
use std::time::Duration;

use provisioning_rag::{
    batch_processing::{BatchJob, BatchQuery},
    caching::ResponseCache,
    conversations::{ConversationContext, ConversationTurn},
    orchestrator::OrchestratorManager,
    query_optimization::QueryOptimizer,
    tools::ToolInput,
};

// ============================================================================
// TEST 1: CACHE + SEARCH INTEGRATION
// ============================================================================

#[tokio::test]
async fn test_cache_with_hybrid_search() {
    // Setup
    let cache = ResponseCache::new(100, Duration::from_secs(3600)).expect("Failed to create cache");

    // Initial stats should be empty
    let initial_stats = cache.stats();
    assert_eq!(initial_stats.hits, 0);
    assert_eq!(initial_stats.misses, 0);
    assert_eq!(initial_stats.total_queries, 0);

    // Cache should be created successfully
    assert!(initial_stats.hit_rate.is_finite());
}

// ============================================================================
// TEST 2: MULTI-TURN CONVERSATION INTEGRATION
// ============================================================================

#[test]
fn test_multiturn_conversation_with_context() {
    // Create conversation context
    let mut conversation = ConversationContext::new("conv-123".to_string());

    // Add first turn
    let turn1 = ConversationTurn::new(
        0,
        "What is Kubernetes?".to_string(),
        "Kubernetes is an orchestration platform".to_string(),
        vec!["docs.k8s.io".to_string()],
        0.95,
    );
    conversation.add_turn(turn1);

    let turns = conversation.get_turns();
    assert_eq!(turns.len(), 1);

    // Add follow-up turn
    let turn2 = ConversationTurn::new(
        1,
        "How to deploy?".to_string(),
        "Use kubectl or Helm charts...".to_string(),
        vec!["helm.sh".to_string()],
        0.92,
    );
    conversation.add_turn(turn2);

    let turns = conversation.get_turns();
    assert_eq!(turns.len(), 2);

    // Verify conversation context string building
    let context_str = conversation.build_context_string();
    assert!(context_str.contains("What is Kubernetes?"));
    assert!(context_str.contains("Kubernetes is an orchestration platform"));
}

// ============================================================================
// TEST 3: BATCH PROCESSING INTEGRATION
// ============================================================================

#[test]
fn test_batch_processing_workflow() {
    // Create batch with 5 queries
    let queries = vec![
        BatchQuery::new("What is Kubernetes?".into()),
        BatchQuery::new("How to deploy?".into()),
        BatchQuery::new("What about networking?".into()),
        BatchQuery::new("Explain storage?".into()),
        BatchQuery::new("Security best practices?".into()),
    ];

    let batch = BatchJob::new(queries);

    // Verify batch properties
    assert_eq!(batch.queries.len(), 5);
    assert!(!batch.job_id.is_empty());
    assert!(batch.max_concurrent > 0);
    assert!(batch.timeout_secs > 0);

    // Test priority sorting
    let mut batch_with_priorities = BatchJob::new(vec![
        BatchQuery::new("q1".into()).with_priority(50),
        BatchQuery::new("q2".into()).with_priority(80),
        BatchQuery::new("q3".into()).with_priority(30),
    ]);

    batch_with_priorities.sort_by_priority();

    // After sort, highest priority first
    assert_eq!(batch_with_priorities.queries[0].priority, 80);
    assert_eq!(batch_with_priorities.queries[1].priority, 50);
    assert_eq!(batch_with_priorities.queries[2].priority, 30);
}

// ============================================================================
// TEST 4: BATCH PROCESSING WITH RETRIES
// ============================================================================

#[test]
fn test_batch_with_retry_strategy() {
    let query = BatchQuery::new("Deploy Kubernetes".into()).with_retries(3);

    assert_eq!(query.retry_count, 3);

    // Simulate retry logic
    let max_retries = query.retry_count;
    let mut retry_count = 0;
    while retry_count < max_retries {
        retry_count += 1;
    }
    assert_eq!(retry_count, 3);
}

// ============================================================================
// TEST 5: QUERY OPTIMIZATION INTEGRATION
// ============================================================================

#[test]
fn test_query_optimization_workflow() {
    let optimizer = QueryOptimizer::new();

    // Test intent detection
    let simple_query = "What is Kubernetes?";
    let optimized = optimizer.optimize(simple_query);
    assert!(optimized.is_ok());

    let optimized_query = optimized.unwrap();
    assert!(!optimized_query.optimized.is_empty());
    assert!(!optimized_query.key_terms.is_empty());

    // Test with context
    let context_query = "Tell me more about networking";
    let previous_context = Some("We discussed Kubernetes deployment");
    let with_context = optimizer.optimize_with_context(context_query, previous_context);
    assert!(with_context.is_ok());
}

// ============================================================================
// TEST 6: ORCHESTRATOR + BATCH INTEGRATION
// ============================================================================

#[tokio::test]
async fn test_orchestrator_with_batch_processing() {
    let orchestrator = OrchestratorManager::new();

    // Create batch job
    let batch = BatchJob::new(vec![
        BatchQuery::new("Query 1".into()),
        BatchQuery::new("Query 2".into()),
        BatchQuery::new("Query 3".into()),
    ]);

    // Submit to orchestrator
    let task_id = orchestrator
        .submit_batch_task(&batch)
        .await
        .expect("Failed to submit batch");

    assert!(!task_id.is_empty());

    // Get task status
    let status = orchestrator
        .get_task_status(&task_id)
        .await
        .expect("Failed to get status");

    // Should be pending initially
    assert_eq!(status, provisioning_rag::orchestrator::TaskStatus::Pending);

    // Update progress
    orchestrator
        .update_batch_progress(&task_id, 1, 0)
        .await
        .expect("Failed to update progress");

    // Get task details
    let task = orchestrator
        .get_task(&task_id)
        .await
        .expect("Failed to get task");

    assert!(matches!(
        task,
        provisioning_rag::orchestrator::OrchestratorTask::BatchQuery(_)
    ));
}

// ============================================================================
// TEST 7: CONVERSATION + ORCHESTRATOR INTEGRATION
// ============================================================================

#[tokio::test]
async fn test_conversation_with_orchestrator() {
    let orchestrator = OrchestratorManager::new();

    // Submit conversation task
    let task_id = orchestrator
        .submit_conversation_task("conv-123".into(), "What is Kubernetes?".into())
        .await
        .expect("Failed to submit conversation");

    assert!(!task_id.is_empty());

    // Complete conversation with response
    orchestrator
        .complete_conversation_task(
            &task_id,
            "Kubernetes is a container orchestration platform...".into(),
        )
        .await
        .expect("Failed to complete conversation");

    // Verify status changed to completed
    let status = orchestrator
        .get_task_status(&task_id)
        .await
        .expect("Failed to get status");

    assert_eq!(
        status,
        provisioning_rag::orchestrator::TaskStatus::Completed
    );

    // Get task and verify response
    let task = orchestrator
        .get_task(&task_id)
        .await
        .expect("Failed to get task");

    assert!(matches!(
        task,
        provisioning_rag::orchestrator::OrchestratorTask::Conversation(_)
    ));
}

// ============================================================================
// TEST 8: TOOL EXECUTION + ORCHESTRATOR
// ============================================================================

#[tokio::test]
async fn test_tool_execution_with_orchestrator() {
    let orchestrator = OrchestratorManager::new();

    // Create tool input
    let tool_input = ToolInput {
        params: serde_json::json!({
            "server_name": "web-01",
            "cores": 4
        }),
    };

    // Submit tool task
    let task_id = orchestrator
        .submit_tool_task("create_server".into(), tool_input)
        .await
        .expect("Failed to submit tool task");

    assert!(!task_id.is_empty());

    // Verify task was created
    let task = orchestrator
        .get_task(&task_id)
        .await
        .expect("Failed to get task");

    assert!(matches!(
        task,
        provisioning_rag::orchestrator::OrchestratorTask::ToolExecution(_)
    ));
}

// ============================================================================
// TEST 9: MULTIPLE CONCURRENT TASKS
// ============================================================================

#[tokio::test]
async fn test_concurrent_task_submissions() {
    let orchestrator = Arc::new(OrchestratorManager::new());

    // Submit 10 batch tasks concurrently
    let mut handles = vec![];

    for i in 0..10 {
        let orch = orchestrator.clone();
        let handle = tokio::spawn(async move {
            let batch = BatchJob::new(vec![
                BatchQuery::new(format!("query_{}_1", i)),
                BatchQuery::new(format!("query_{}_2", i)),
            ]);

            orch.submit_batch_task(&batch).await
        });
        handles.push(handle);
    }

    // Wait for all submissions
    let mut task_ids = vec![];
    for handle in handles {
        let result = handle
            .await
            .expect("Failed to await")
            .expect("Failed to submit");
        task_ids.push(result);
    }

    assert_eq!(task_ids.len(), 10);

    // Verify all tasks exist
    for task_id in &task_ids {
        let status = orchestrator
            .get_task_status(task_id)
            .await
            .expect("Failed to get status");
        assert_eq!(status, provisioning_rag::orchestrator::TaskStatus::Pending);
    }

    // Get stats
    let stats = orchestrator.get_stats().await.expect("Failed to get stats");

    assert_eq!(stats.total_tasks, 10);
    assert_eq!(stats.pending, 10);
}

// ============================================================================
// TEST 10: TASK FILTERING AND LISTING
// ============================================================================

#[tokio::test]
async fn test_task_listing_and_filtering() {
    let orchestrator = OrchestratorManager::new();

    // Submit mixed task types
    let batch = BatchJob::new(vec![BatchQuery::new("test".into())]);
    let _batch_task_id = orchestrator
        .submit_batch_task(&batch)
        .await
        .expect("Failed to submit batch");

    let conv_task_id = orchestrator
        .submit_conversation_task("conv-1".into(), "Hello".into())
        .await
        .expect("Failed to submit conversation");

    // List all tasks
    let all_tasks = orchestrator
        .list_tasks()
        .await
        .expect("Failed to list tasks");

    assert_eq!(all_tasks.len(), 2);

    // List pending tasks
    let pending = orchestrator
        .list_tasks_by_status(provisioning_rag::orchestrator::TaskStatus::Pending)
        .await
        .expect("Failed to list pending");

    assert_eq!(pending.len(), 2);

    // Complete one task
    orchestrator
        .complete_conversation_task(&conv_task_id, "Hi!".into())
        .await
        .expect("Failed to complete");

    // List completed tasks
    let completed = orchestrator
        .list_tasks_by_status(provisioning_rag::orchestrator::TaskStatus::Completed)
        .await
        .expect("Failed to list completed");

    assert_eq!(completed.len(), 1);

    // Verify stats updated
    let stats = orchestrator.get_stats().await.expect("Failed to get stats");

    assert_eq!(stats.total_tasks, 2);
    assert_eq!(stats.pending, 1);
    assert_eq!(stats.completed, 1);
    // Success rate only counts completed vs (completed + failed)
    // 1 completed, 0 failed = 100% success rate
    assert_eq!(stats.success_rate, 100.0);
}

// ============================================================================
// TEST 11: FAILURE HANDLING AND ERROR RECOVERY
// ============================================================================

#[tokio::test]
async fn test_error_handling_with_nonexistent_task() {
    let orchestrator = OrchestratorManager::new();

    // Try to get status of non-existent task
    let result = orchestrator.get_task_status("nonexistent").await;
    assert!(result.is_err());

    // Verify error message contains task ID
    if let Err(e) = result {
        let error_msg = format!("{}", e);
        assert!(error_msg.contains("nonexistent"));
    }
}

// ============================================================================
// TEST 12: BATCH PROGRESS TRACKING
// ============================================================================

#[tokio::test]
async fn test_batch_progress_tracking() {
    let orchestrator = OrchestratorManager::new();

    let batch = BatchJob::new(vec![
        BatchQuery::new("q1".into()),
        BatchQuery::new("q2".into()),
        BatchQuery::new("q3".into()),
    ]);

    let task_id = orchestrator
        .submit_batch_task(&batch)
        .await
        .expect("Failed to submit");

    // Update progress - 1 completed
    orchestrator
        .update_batch_progress(&task_id, 1, 0)
        .await
        .expect("Failed to update");

    let task = orchestrator
        .get_task(&task_id)
        .await
        .expect("Failed to get task");

    if let provisioning_rag::orchestrator::OrchestratorTask::BatchQuery(batch_task) = task {
        assert_eq!(batch_task.completed, 1);
        assert_eq!(batch_task.failed, 0);
        assert_eq!(batch_task.progress_percent(), 33); // 1 out of 3
    } else {
        panic!("Expected BatchQueryTask");
    }

    // Update progress - 2 completed, 1 failed
    orchestrator
        .update_batch_progress(&task_id, 2, 1)
        .await
        .expect("Failed to update");

    let task = orchestrator
        .get_task(&task_id)
        .await
        .expect("Failed to get task");

    if let provisioning_rag::orchestrator::OrchestratorTask::BatchQuery(batch_task) = task {
        assert_eq!(batch_task.completed, 2);
        assert_eq!(batch_task.failed, 1);
        assert_eq!(batch_task.progress_percent(), 66); // 2 out of 3
    } else {
        panic!("Expected BatchQueryTask");
    }
}

// ============================================================================
// TEST 13: CACHE + OPTIMIZER + SEARCH WORKFLOW
// ============================================================================

#[test]
fn test_complete_query_processing_workflow() {
    let optimizer = QueryOptimizer::new();

    // Test complete workflow
    let raw_query = "What is Kubernetes?";

    // Step 1: Extract key terms
    let key_terms = optimizer.extract_key_terms(raw_query);
    assert!(!key_terms.is_empty());

    // Step 2: Full optimization
    let optimized = optimizer.optimize(raw_query);
    assert!(optimized.is_ok());

    let opt_query = optimized.unwrap();
    assert!(!opt_query.key_terms.is_empty());
    assert!(!opt_query.optimized.is_empty());
}

// ============================================================================
// TEST 14: STRESS TEST - MANY CONCURRENT OPERATIONS
// ============================================================================

#[tokio::test]
async fn test_stress_many_concurrent_batch_tasks() {
    let orchestrator = Arc::new(OrchestratorManager::new());

    // Create 50 concurrent batch submissions
    let mut handles = vec![];

    for i in 0..50 {
        let orch = orchestrator.clone();
        let handle = tokio::spawn(async move {
            let batch = BatchJob::new(vec![
                BatchQuery::new(format!("stress_test_{}_q1", i)),
                BatchQuery::new(format!("stress_test_{}_q2", i)),
                BatchQuery::new(format!("stress_test_{}_q3", i)),
            ]);

            orch.submit_batch_task(&batch).await
        });
        handles.push(handle);
    }

    // Collect all results
    let mut count = 0;
    for handle in handles {
        if let Ok(Ok(_)) = handle.await {
            count += 1;
        }
    }

    assert_eq!(count, 50);

    // Verify orchestrator stats
    let stats = orchestrator.get_stats().await.expect("Failed to get stats");

    assert_eq!(stats.total_tasks, 50);
    assert_eq!(stats.pending, 50);
}

// ============================================================================
// TEST 15: ORCHESTRATOR STATISTICS AND METRICS
// ============================================================================

#[tokio::test]
async fn test_orchestrator_statistics_computation() {
    let orchestrator = OrchestratorManager::new();

    // Submit 5 tasks
    let mut task_ids = vec![];
    for i in 0..5 {
        let batch = BatchJob::new(vec![BatchQuery::new(format!("q{}", i))]);
        let task_id = orchestrator
            .submit_batch_task(&batch)
            .await
            .expect("Failed");
        task_ids.push(task_id);
    }

    // Complete 3 tasks
    for task_id in task_ids.iter().take(3) {
        let _ = orchestrator.complete_batch_task(task_id).await;
    }

    // Get stats
    let stats = orchestrator.get_stats().await.expect("Failed to get stats");

    assert_eq!(stats.total_tasks, 5);
    assert_eq!(stats.completed, 3);
    assert_eq!(stats.pending, 2);
}

// ============================================================================
// TEST 16: CONTEXT-AWARE OPTIMIZATION
// ============================================================================

#[test]
fn test_context_aware_query_optimization() {
    let optimizer = QueryOptimizer::new();

    let follow_up_query = "Tell me more";
    let previous_context = Some("We were discussing Kubernetes networking");

    let result = optimizer.optimize_with_context(follow_up_query, previous_context);
    assert!(result.is_ok());

    let optimized = result.unwrap();
    // Context should be integrated into optimization
    assert!(!optimized.optimized.is_empty());
}

// ============================================================================
// TEST 17: BATCH SIZE VARIATIONS
// ============================================================================

#[test]
fn test_batch_processing_various_sizes() {
    // Small batch
    let small_batch = BatchJob::new(vec![BatchQuery::new("q1".into())]);
    assert_eq!(small_batch.queries.len(), 1);

    // Medium batch
    let medium_batch = BatchJob::new(
        (0..50)
            .map(|i| BatchQuery::new(format!("q{}", i)))
            .collect(),
    );
    assert_eq!(medium_batch.queries.len(), 50);

    // Large batch
    let large_batch = BatchJob::new(
        (0..1000)
            .map(|i| BatchQuery::new(format!("q{}", i)))
            .collect(),
    );
    assert_eq!(large_batch.queries.len(), 1000);

    // Verify properties scale correctly
    assert!(small_batch.max_concurrent > 0);
    assert_eq!(medium_batch.queries.len(), 50);
    assert_eq!(large_batch.queries.len(), 1000);
}

// ============================================================================
// TEST 18: CONVERSATION HISTORY MANAGEMENT
// ============================================================================

#[test]
fn test_conversation_history_with_multiple_turns() {
    let mut conversation = ConversationContext::new("multi-turn-conv".to_string());

    // Add 5 conversation turns
    for i in 0..5 {
        let question = format!("Question {}", i + 1);
        let answer = format!("Answer {}", i + 1);
        let turn = ConversationTurn::new(i, question, answer, vec!["source".to_string()], 0.9);
        conversation.add_turn(turn);
    }

    let turns = conversation.get_turns();
    assert_eq!(turns.len(), 5);

    // Get recent turns
    let recent = conversation.get_recent_turns(3);
    assert_eq!(recent.len(), 3);

    // Get specific turn
    let turn_2 = conversation.get_turn(2);
    assert!(turn_2.is_some());
}

// ============================================================================
// TEST 19: TASK TYPE ENUMERATION
// ============================================================================

#[tokio::test]
async fn test_all_orchestrator_task_types() {
    let orchestrator = OrchestratorManager::new();

    // Submit all task types
    let batch = BatchJob::new(vec![BatchQuery::new("test".into())]);
    let batch_id = orchestrator
        .submit_batch_task(&batch)
        .await
        .expect("Failed");

    let conv_id = orchestrator
        .submit_conversation_task("conv".into(), "msg".into())
        .await
        .expect("Failed");

    let tool_id = orchestrator
        .submit_tool_task(
            "tool".into(),
            ToolInput {
                params: serde_json::json!({}),
            },
        )
        .await
        .expect("Failed");

    // Verify all types exist
    let batch_task = orchestrator.get_task(&batch_id).await.expect("Failed");
    assert!(matches!(
        batch_task,
        provisioning_rag::orchestrator::OrchestratorTask::BatchQuery(_)
    ));

    let conv_task = orchestrator.get_task(&conv_id).await.expect("Failed");
    assert!(matches!(
        conv_task,
        provisioning_rag::orchestrator::OrchestratorTask::Conversation(_)
    ));

    let tool_task = orchestrator.get_task(&tool_id).await.expect("Failed");
    assert!(matches!(
        tool_task,
        provisioning_rag::orchestrator::OrchestratorTask::ToolExecution(_)
    ));
}

// ============================================================================
// TEST 20: COMPLETE END-TO-END WORKFLOW
// ============================================================================

#[tokio::test]
async fn test_complete_end_to_end_workflow() {
    // Initialize all components
    let optimizer = QueryOptimizer::new();
    let cache = ResponseCache::new(100, Duration::from_secs(3600)).expect("Failed");
    let orchestrator = Arc::new(OrchestratorManager::new());

    // Simulate complete workflow:
    // 1. User submits multi-query batch
    let batch = BatchJob::new(vec![
        BatchQuery::new("What is Kubernetes?".into()),
        BatchQuery::new("How to deploy?".into()),
        BatchQuery::new("Networking best practices?".into()),
    ]);

    let task_id = orchestrator
        .submit_batch_task(&batch)
        .await
        .expect("Failed to submit");

    // 2. Verify task submitted
    let status = orchestrator
        .get_task_status(&task_id)
        .await
        .expect("Failed");

    assert_eq!(status, provisioning_rag::orchestrator::TaskStatus::Pending);

    // 3. Optimize first query
    let optimized = optimizer.optimize("What is Kubernetes?");
    assert!(optimized.is_ok());

    // 4. Simulate processing progress
    orchestrator
        .update_batch_progress(&task_id, 1, 0)
        .await
        .expect("Failed");

    // 5. Add to cache (simulated)
    let cache_stats = cache.stats();
    assert_eq!(cache_stats.total_queries, 0); // Not yet used

    // 6. Verify final state
    let task = orchestrator.get_task(&task_id).await.expect("Failed");

    assert!(matches!(
        task,
        provisioning_rag::orchestrator::OrchestratorTask::BatchQuery(_)
    ));

    // 7. Get final statistics
    let stats = orchestrator.get_stats().await.expect("Failed");

    assert_eq!(stats.total_tasks, 1);
    assert_eq!(stats.pending, 1);
}
