#![allow(clippy::explicit_auto_deref, clippy::let_underscore_must_use, clippy::non_ascii_literal, clippy::use_debug)]
//! Redis integration test to verify HFT-optimized connection management
//!
//! This module contains tests that demonstrate the Redis connection performance
//! improvements and validate that the optimized connection management works correctly.

use super::redis::{RedisConfig, RedisPool};
use serde::{Deserialize, Serialize};
use std::time::{Duration, Instant};

/// Redis URL from env (CI injects via sidecar), fallback to localhost.
fn redis_url() -> String {
    std::env::var("REDIS_URL").unwrap_or_else(|_| "redis://localhost:6379".to_string())
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
struct TestData {
    id: u64,
    symbol: String,
    price: f64,
    timestamp: u64,
}

impl TestData {
    fn new(id: u64, symbol: &str, price: f64) -> Self {
        Self {
            id,
            symbol: symbol.to_string(),
            price,
            timestamp: std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .unwrap()
                .as_secs(),
        }
    }
}

/// Test `Redis` connection pool with `HFT` optimizations
/// CI provides Redis via sidecar; locally gracefully skips if unavailable.
#[tokio::test]
async fn test_redis_hft_performance() {
    // Skip test if Redis is not available (for CI/CD environments)
    let config = RedisConfig {
        url: redis_url(),
        max_connections: 30, // Increased for test reliability
        min_connections: 10,
        connect_timeout_ms: 200,      // Relaxed for test environment
        command_timeout_micros: 5000, // 5ms timeout for test environment
        acquire_timeout_ms: 100,      // Relaxed pool acquisition timeout
        default_ttl_seconds: 60,
        enable_prewarming: true,
        enable_pipelining: true,
        pipeline_batch_size: 50,
        ..Default::default()
    };

    // Initialize Redis pool
    let pool = match RedisPool::new(config).await {
        Ok(pool) => pool,
        Err(_) => {
            println!("Redis not available, skipping integration test");
            return;
        },
    };

    // Test basic operations
    let test_data = TestData::new(1, "AAPL", 150.25);
    let key = "test:hft:data:1";

    // Test SET operation
    let start = Instant::now();
    pool.set(key, &test_data, Some(Duration::from_secs(60)))
        .await
        .expect("Failed to set data");
    let set_duration = start.elapsed();
    println!("SET operation took: {:?}", set_duration);

    // Test GET operation
    let start = Instant::now();
    let retrieved: Option<TestData> = pool.get(key).await.expect("Failed to get data");
    let get_duration = start.elapsed();
    println!("GET operation took: {:?}", get_duration);

    assert_eq!(retrieved, Some(test_data.clone()));

    // Test EXISTS operation
    let start = Instant::now();
    let exists = pool.exists(key).await.expect("Failed to check existence");
    let exists_duration = start.elapsed();
    println!("EXISTS operation took: {:?}", exists_duration);
    assert!(exists);

    // Test batch operations for HFT performance
    let batch_keys: Vec<&str> = vec!["test:batch:1", "test:batch:2", "test:batch:3"];
    let batch_data = vec![
        TestData::new(1, "MSFT", 300.50),
        TestData::new(2, "GOOGL", 2500.75),
        TestData::new(3, "TSLA", 800.25),
    ];

    // Clone batch_data for later verification
    let expected_data = batch_data.clone();

    // Set batch data
    for (key, data) in batch_keys.iter().zip(batch_data.into_iter()) {
        pool.set_with_default_ttl(*key, &data)
            .await
            .expect("Failed to set batch data");
    }

    // Test batch GET
    let start = Instant::now();
    let batch_results: Vec<Option<TestData>> =
        pool.batch_get(&batch_keys).await.expect("Failed batch get");
    let batch_duration = start.elapsed();
    println!("BATCH GET operation took: {:?}", batch_duration);

    assert_eq!(batch_results.len(), 3);
    for (i, result) in batch_results.into_iter().enumerate() {
        assert_eq!(result, Some(expected_data[i].clone()));
    }

    // Test DELETE operation
    let start = Instant::now();
    let deleted = pool.delete(key).await.expect("Failed to delete");
    let delete_duration = start.elapsed();
    println!("DELETE operation took: {:?}", delete_duration);
    assert!(deleted);

    // Verify deletion
    let retrieved: Option<TestData> = pool.get(key).await.expect("Failed to get after delete");
    assert_eq!(retrieved, None);

    // Test performance metrics
    let metrics = pool.get_metrics().await.expect("Failed to get metrics");
    println!("Redis Performance Metrics:");
    println!("  Total operations: {}", metrics.total_operations);
    println!("  Success rate: {:.2}%", metrics.success_rate());
    println!(
        "  Average latency: {:.2} µs",
        metrics.average_latency_micros()
    );
    println!("  Sub-1ms operations: {:.2}%", metrics.sub_1ms_percentage());

    // Assert HFT performance requirements
    assert!(
        metrics.success_rate() > 95.0,
        "Success rate should be > 95%"
    );
    assert!(
        metrics.average_latency_micros() < 1000.0,
        "Average latency should be < 1ms for HFT"
    );

    // Cleanup batch data
    for key in &batch_keys {
        let _ = pool.delete(key).await;
    }

    println!("✅ Redis HFT integration test completed successfully!");
}

/// Test `Redis` connection pool under load
/// CI provides Redis via sidecar; locally gracefully skips if unavailable.
#[tokio::test]
async fn test_redis_concurrent_load() {
    let config = RedisConfig {
        url: redis_url(),
        max_connections: 60, // Increased to handle 50 concurrent tasks
        min_connections: 10,
        command_timeout_micros: 10000, // 10ms timeout for test reliability
        acquire_timeout_ms: 500,       // Increased wait time for pool acquisition
        ..Default::default()
    };

    let pool = match RedisPool::new(config).await {
        Ok(pool) => pool,
        Err(_) => {
            println!("Redis not available, skipping load test");
            return;
        },
    };

    let pool = std::sync::Arc::new(pool);
    let num_tasks = 50;
    let operations_per_task = 10;

    let start_time = Instant::now();
    let mut tasks = Vec::new();

    for task_id in 0..num_tasks {
        let pool_clone = pool.clone();
        let task = tokio::spawn(async move {
            for op_id in 0..operations_per_task {
                let key = format!("load_test:{}:{}", task_id, op_id);
                let data = TestData::new(task_id as u64 * 1000 + op_id as u64, "LOAD", 100.0);

                // Set data
                pool_clone
                    .set_with_default_ttl(&key, &data)
                    .await
                    .expect("Failed to set data in load test");

                // Get data immediately to simulate HFT read-after-write
                let retrieved: Option<TestData> = pool_clone
                    .get(&key)
                    .await
                    .expect("Failed to get data in load test");

                assert_eq!(retrieved, Some(data));

                // Cleanup
                let _ = pool_clone.delete(&key).await;
            }
        });
        tasks.push(task);
    }

    // Wait for all tasks to complete
    for task in tasks {
        task.await.expect("Task failed");
    }

    let total_duration = start_time.elapsed();
    let total_operations = num_tasks * operations_per_task * 3; // SET, GET, DELETE

    println!("Load Test Results:");
    println!("  Total operations: {}", total_operations);
    println!("  Total duration: {:?}", total_duration);
    println!(
        "  Operations per second: {:.2}",
        total_operations as f64 / total_duration.as_secs_f64()
    );

    let metrics = pool.get_metrics().await.expect("Failed to get metrics");
    println!("  Final success rate: {:.2}%", metrics.success_rate());
    println!(
        "  Final average latency: {:.2} µs",
        metrics.average_latency_micros()
    );

    assert!(
        metrics.success_rate() > 90.0,
        "Success rate should be > 90% under load"
    );

    println!("✅ Redis concurrent load test completed successfully!");
}

/// Benchmark `Redis` connection manager vs direct connection performance
/// CI provides Redis via sidecar; locally gracefully skips if unavailable.
#[tokio::test]
async fn test_redis_connection_manager_performance() {
    let config = RedisConfig {
        url: redis_url(),
        max_connections: 30, // Increased for test reliability
        enable_prewarming: true,
        min_connections: 10,
        command_timeout_micros: 5000, // 5ms timeout for test environment
        acquire_timeout_ms: 100,      // Relaxed pool acquisition timeout
        ..Default::default()
    };

    let pool = match RedisPool::new(config).await {
        Ok(pool) => pool,
        Err(_) => {
            println!("Redis not available, skipping performance benchmark");
            return;
        },
    };

    let num_operations = 100;
    let test_data = TestData::new(999, "PERF", 123.45);

    // Warm up
    for i in 0..10 {
        let key = format!("warmup:{}", i);
        let _ = pool.set_with_default_ttl(&key, &test_data).await;
        let _ = pool.delete(&key).await;
    }

    // Benchmark SET operations
    let start = Instant::now();
    for i in 0..num_operations {
        let key = format!("benchmark:set:{}", i);
        pool.set_with_default_ttl(&key, &test_data)
            .await
            .expect("Benchmark SET failed");
    }
    let set_duration = start.elapsed();

    // Benchmark GET operations
    let start = Instant::now();
    for i in 0..num_operations {
        let key = format!("benchmark:set:{}", i);
        let _: Option<TestData> = pool.get(&key).await.expect("Benchmark GET failed");
    }
    let get_duration = start.elapsed();

    // Cleanup
    for i in 0..num_operations {
        let key = format!("benchmark:set:{}", i);
        let _ = pool.delete(&key).await;
    }

    let avg_set_latency = set_duration.as_micros() as f64 / num_operations as f64;
    let avg_get_latency = get_duration.as_micros() as f64 / num_operations as f64;

    println!("Performance Benchmark Results:");
    println!(
        "  SET operations: {} ops in {:?}",
        num_operations, set_duration
    );
    println!("  Average SET latency: {:.2} µs", avg_set_latency);
    println!(
        "  GET operations: {} ops in {:?}",
        num_operations, get_duration
    );
    println!("  Average GET latency: {:.2} µs", avg_get_latency);

    // HFT performance assertions
    assert!(
        avg_set_latency < 2000.0,
        "SET latency should be < 2ms for HFT"
    );
    assert!(
        avg_get_latency < 1000.0,
        "GET latency should be < 1ms for HFT"
    );

    println!("✅ Redis connection manager performance benchmark completed!");
}
