#![cfg(feature = "__integration_tests")]
#![allow(unexpected_cfgs)]
//! End-to-End Integration Tests: API Gateway → Backtesting Service
//!
//! This test suite validates the complete backtesting service flow through the API Gateway:
//! - Backtest start/stop/status operations
//! - Strategy execution with historical data
//! - Results retrieval and validation
//! - Real-time progress monitoring
//! - Backtest listing and filtering
//!
//! Test Count: 12 tests
//! Coverage: Full backtesting service integration

use anyhow::Result;
use chrono::{Duration, Utc};
use std::collections::HashMap;
use std::time::Duration as StdDuration;
use tokio::time::timeout;
use tonic::{metadata::MetadataValue, transport::Channel, Request, Status};

// Common test utilities (JWT auth helpers)
mod common;
use common::auth_helpers::{create_test_jwt, get_api_addr, TestAuthConfig};

// Generated proto code
pub mod trading {
    tonic::include_proto!("trading");
}

use trading::{
    backtesting_service_client::BacktestingServiceClient, BacktestStatus,
    GetBacktestResultsRequest, GetBacktestStatusRequest, ListBacktestsRequest,
    StartBacktestRequest, StopBacktestRequest, SubscribeBacktestProgressRequest,
};

/// Create an authenticated backtesting service client
async fn create_authenticated_client() -> Result<
    BacktestingServiceClient<
        tonic::service::interceptor::InterceptedService<
            Channel,
            impl Fn(Request<()>) -> Result<Request<()>, Status> + Clone,
        >,
    >,
> {
    let user_id = "test_backtester_001";
    let role = "analyst";

    // Use auth_helpers to create JWT token with correct secret
    let config = TestAuthConfig::trader()
        .with_user_id(user_id)
        .with_roles(vec![role.to_string()])
        .with_permissions(vec![
            "api.access".to_string(),
            "backtesting.execute".to_string(),
            "backtesting.view".to_string(),
        ]);

    let token = create_test_jwt(config)?;

    let api_addr = get_api_addr();
    let channel = Channel::from_shared(api_addr)?.connect().await?;

    // Create interceptor that injects JWT token AND user context into request metadata
    let user_id_owned = user_id.to_string();
    let role_owned = role.to_string();

    let interceptor = move |mut req: Request<()>| -> Result<Request<()>, Status> {
        // JWT token in authorization header
        let token_value = format!("Bearer {}", token);
        let metadata_value = MetadataValue::try_from(token_value)
            .map_err(|_| Status::internal("Failed to create metadata value"))?;
        req.metadata_mut().insert("authorization", metadata_value);

        // User context in metadata headers
        let user_id_value = MetadataValue::try_from(user_id_owned.clone())
            .map_err(|_| Status::internal("Failed to create user_id metadata"))?;
        req.metadata_mut().insert("x-user-id", user_id_value);

        let role_value = MetadataValue::try_from(role_owned.clone())
            .map_err(|_| Status::internal("Failed to create role metadata"))?;
        req.metadata_mut().insert("x-user-role", role_value);

        Ok(req)
    };

    let client = BacktestingServiceClient::with_interceptor(channel, interceptor);

    Ok(client)
}

// ============================================================================
// SECTION 1: BACKTEST LIFECYCLE E2E TESTS (5 tests)
// ============================================================================

#[tokio::test]
async fn test_e2e_backtest_start() -> Result<()> {
    println!("\n=== E2E Test: Start Backtest via API Gateway ===");

    let mut client = create_authenticated_client().await?;

    let start_date = (Utc::now() - Duration::days(30))
        .timestamp_nanos_opt()
        .unwrap_or(0);
    let end_date = Utc::now().timestamp_nanos_opt().unwrap_or(0);

    let mut parameters = HashMap::new();
    parameters.insert("fast_ma".to_string(), "10".to_string());
    parameters.insert("slow_ma".to_string(), "30".to_string());
    parameters.insert("risk_per_trade".to_string(), "0.02".to_string());

    let request = Request::new(StartBacktestRequest {
        strategy_name: "moving_average_crossover".to_string(),
        symbols: vec!["BTC/USD".to_string(), "ETH/USD".to_string()],
        start_date_unix_nanos: start_date,
        end_date_unix_nanos: end_date,
        initial_capital: 100000.0,
        parameters,
        save_results: true,
        description: "E2E test backtest - MA crossover strategy".to_string(),
    });

    let response = client.start_backtest(request).await?;
    let result = response.into_inner();

    assert!(result.success, "Backtest should start successfully");
    assert!(!result.backtest_id.is_empty(), "Should return backtest ID");

    println!("✓ Backtest started successfully");
    println!("  Backtest ID: {}", result.backtest_id);
    println!(
        "  Estimated Duration: {}s",
        result.estimated_duration_seconds
    );
    println!("  Message: {}", result.message);

    Ok(())
}

#[tokio::test]
async fn test_e2e_backtest_status() -> Result<()> {
    println!("\n=== E2E Test: Get Backtest Status via API Gateway ===");

    let mut client = create_authenticated_client().await?;

    // First start a backtest
    let start_date = (Utc::now() - Duration::days(7))
        .timestamp_nanos_opt()
        .unwrap_or(0);
    let end_date = Utc::now().timestamp_nanos_opt().unwrap_or(0);

    let start_request = Request::new(StartBacktestRequest {
        strategy_name: "mean_reversion".to_string(),
        symbols: vec!["BTC/USD".to_string()],
        start_date_unix_nanos: start_date,
        end_date_unix_nanos: end_date,
        initial_capital: 50000.0,
        parameters: HashMap::new(),
        save_results: true,
        description: "E2E status test".to_string(),
    });

    let start_response = client.start_backtest(start_request).await?;
    let backtest_id = start_response.into_inner().backtest_id;

    println!("✓ Backtest started: {}", backtest_id);

    // Wait a moment for backtest to start processing
    tokio::time::sleep(StdDuration::from_millis(500)).await;

    // Query status
    let status_request = Request::new(GetBacktestStatusRequest {
        backtest_id: backtest_id.clone(),
    });

    let status_response = client.get_backtest_status(status_request).await?;
    let status = status_response.into_inner();

    assert_eq!(status.backtest_id, backtest_id);
    assert!(status.status != BacktestStatus::Unspecified as i32);

    println!("✓ Backtest status retrieved");
    println!("  Status: {:?}", status.status);
    println!("  Progress: {:.2}%", status.progress_percentage);
    println!("  Trades Executed: {}", status.trades_executed);
    println!("  Current PnL: ${:.2}", status.current_pnl);

    Ok(())
}

#[tokio::test]
async fn test_e2e_backtest_stop() -> Result<()> {
    println!("\n=== E2E Test: Stop Running Backtest via API Gateway ===");

    let mut client = create_authenticated_client().await?;

    // Start a long-running backtest
    let start_date = (Utc::now() - Duration::days(90))
        .timestamp_nanos_opt()
        .unwrap_or(0);
    let end_date = Utc::now().timestamp_nanos_opt().unwrap_or(0);

    let start_request = Request::new(StartBacktestRequest {
        strategy_name: "momentum".to_string(),
        symbols: vec!["BTC/USD".to_string(), "ETH/USD".to_string()],
        start_date_unix_nanos: start_date,
        end_date_unix_nanos: end_date,
        initial_capital: 100000.0,
        parameters: HashMap::new(),
        save_results: true,
        description: "E2E stop test - long backtest".to_string(),
    });

    let start_response = client.start_backtest(start_request).await?;
    let backtest_id = start_response.into_inner().backtest_id;

    println!("✓ Long backtest started: {}", backtest_id);

    // Wait for backtest to begin processing
    tokio::time::sleep(StdDuration::from_secs(1)).await;

    // Stop the backtest
    let stop_request = Request::new(StopBacktestRequest {
        backtest_id: backtest_id.clone(),
        save_partial_results: true,
    });

    let stop_response = client.stop_backtest(stop_request).await?;
    let stop_result = stop_response.into_inner();

    assert!(stop_result.success, "Backtest should stop successfully");

    println!("✓ Backtest stopped successfully");
    println!("  Message: {}", stop_result.message);
    println!("  Partial Results Saved: {}", stop_result.results_saved);

    Ok(())
}

#[tokio::test]
async fn test_e2e_backtest_results() -> Result<()> {
    println!("\n=== E2E Test: Get Backtest Results via API Gateway ===");

    let mut client = create_authenticated_client().await?;

    // Start a short backtest that will complete quickly
    let start_date = (Utc::now() - Duration::days(7))
        .timestamp_nanos_opt()
        .unwrap_or(0);
    let end_date = (Utc::now() - Duration::days(6))
        .timestamp_nanos_opt()
        .unwrap_or(0);

    let start_request = Request::new(StartBacktestRequest {
        strategy_name: "buy_and_hold".to_string(),
        symbols: vec!["BTC/USD".to_string()],
        start_date_unix_nanos: start_date,
        end_date_unix_nanos: end_date,
        initial_capital: 10000.0,
        parameters: HashMap::new(),
        save_results: true,
        description: "E2E results test - short backtest".to_string(),
    });

    let start_response = client.start_backtest(start_request).await?;
    let backtest_id = start_response.into_inner().backtest_id;

    println!("✓ Short backtest started: {}", backtest_id);

    // Wait for backtest to complete (with timeout)
    let mut completed = false;
    for _ in 0..20 {
        tokio::time::sleep(StdDuration::from_millis(500)).await;

        let status_request = Request::new(GetBacktestStatusRequest {
            backtest_id: backtest_id.clone(),
        });

        if let Ok(response) = client.get_backtest_status(status_request).await {
            let status = response.into_inner();
            if status.status == BacktestStatus::Completed as i32 {
                completed = true;
                println!("✓ Backtest completed");
                break;
            }
        }
    }

    if !completed {
        println!("⚠ Backtest did not complete in expected time, skipping results check");
        return Ok(());
    }

    // Get results
    let results_request = Request::new(GetBacktestResultsRequest {
        backtest_id: backtest_id.clone(),
        include_trades: true,
        include_metrics: true,
    });

    let results_response = client.get_backtest_results(results_request).await?;
    let results = results_response.into_inner();

    assert_eq!(results.backtest_id, backtest_id);

    if let Some(metrics) = results.metrics {
        println!("✓ Backtest results retrieved");
        println!("  Total Return: {:.2}%", metrics.total_return * 100.0);
        println!("  Sharpe Ratio: {:.2}", metrics.sharpe_ratio);
        println!("  Max Drawdown: {:.2}%", metrics.max_drawdown * 100.0);
        println!("  Total Trades: {}", metrics.total_trades);
        println!("  Win Rate: {:.2}%", metrics.win_rate * 100.0);
    }

    println!("  Trade Count: {}", results.trades.len());
    println!("  Equity Curve Points: {}", results.equity_curve.len());

    Ok(())
}

#[tokio::test]
async fn test_e2e_backtest_list() -> Result<()> {
    println!("\n=== E2E Test: List Backtests via API Gateway ===");

    let mut client = create_authenticated_client().await?;

    let request = Request::new(ListBacktestsRequest {
        limit: 10,
        offset: 0,
        strategy_name: None,
        status_filter: None,
    });

    let response = client.list_backtests(request).await?;
    let list_result = response.into_inner();

    println!("✓ Backtest list retrieved");
    println!("  Total Count: {}", list_result.total_count);
    println!("  Returned: {}", list_result.backtests.len());

    for (i, backtest) in list_result.backtests.into_iter().enumerate().take(5) {
        println!(
            "  {}. {} - {} ({})",
            i + 1,
            backtest.backtest_id,
            backtest.strategy_name,
            backtest.status
        );
    }

    Ok(())
}

// ============================================================================
// SECTION 2: REAL-TIME MONITORING E2E TESTS (3 tests)
// ============================================================================

#[tokio::test]
async fn test_e2e_backtest_progress_subscription() -> Result<()> {
    println!("\n=== E2E Test: Backtest Progress Subscription via API Gateway ===");

    let mut client = create_authenticated_client().await?;

    // Start a backtest
    let start_date = (Utc::now() - Duration::days(14))
        .timestamp_nanos_opt()
        .unwrap_or(0);
    let end_date = Utc::now().timestamp_nanos_opt().unwrap_or(0);

    // WAVE 152: Use moving_average_crossover strategy (grid_trading doesn't exist)
    let mut parameters = HashMap::new();
    parameters.insert("fast_ma".to_string(), "10".to_string());
    parameters.insert("slow_ma".to_string(), "30".to_string());
    parameters.insert("risk_per_trade".to_string(), "0.02".to_string());

    let start_request = Request::new(StartBacktestRequest {
        strategy_name: "moving_average_crossover".to_string(),
        symbols: vec!["BTC/USD".to_string()],
        start_date_unix_nanos: start_date,
        end_date_unix_nanos: end_date,
        initial_capital: 50000.0,
        parameters,
        save_results: true,
        description: "E2E progress subscription test".to_string(),
    });

    let start_response = client.start_backtest(start_request).await?;
    let backtest_id = start_response.into_inner().backtest_id;

    println!("✓ Backtest started: {}", backtest_id);

    // Subscribe to progress updates
    let progress_request = Request::new(SubscribeBacktestProgressRequest {
        backtest_id: backtest_id.clone(),
    });

    let mut stream = client
        .subscribe_backtest_progress(progress_request)
        .await?
        .into_inner();

    println!("✓ Progress stream established");

    // Receive progress updates (with timeout)
    let mut updates_received = 0;
    while let Ok(update_result) = timeout(StdDuration::from_secs(10), stream.message()).await {
        if let Ok(Some(update)) = update_result {
            updates_received += 1;
            println!(
                "  Progress Update #{}: {:.1}% - {} trades, PnL: ${:.2}",
                updates_received,
                update.progress_percentage,
                update.trades_executed,
                update.current_pnl
            );

            // Stop after receiving 5 updates or if completed
            if updates_received >= 5 || update.status == BacktestStatus::Completed as i32 {
                break;
            }
        }
    }

    assert!(
        updates_received > 0,
        "Should receive at least one progress update"
    );
    println!("✓ Received {} progress updates", updates_received);

    Ok(())
}

#[tokio::test]
async fn test_e2e_backtest_filtering_by_strategy() -> Result<()> {
    println!("\n=== E2E Test: Filter Backtests by Strategy via API Gateway ===");

    let mut client = create_authenticated_client().await?;

    let request = Request::new(ListBacktestsRequest {
        limit: 10,
        offset: 0,
        strategy_name: Some("moving_average_crossover".to_string()),
        status_filter: None,
    });

    let response = client.list_backtests(request).await?;
    let list_result = response.into_inner();

    println!("✓ Filtered backtest list retrieved");
    println!("  Strategy: moving_average_crossover");
    println!("  Count: {}", list_result.backtests.len());

    for backtest in &list_result.backtests {
        assert_eq!(backtest.strategy_name, "moving_average_crossover");
    }

    Ok(())
}

#[tokio::test]
async fn test_e2e_backtest_filtering_by_status() -> Result<()> {
    println!("\n=== E2E Test: Filter Backtests by Status via API Gateway ===");

    let mut client = create_authenticated_client().await?;

    let request = Request::new(ListBacktestsRequest {
        limit: 10,
        offset: 0,
        strategy_name: None,
        status_filter: Some(BacktestStatus::Completed as i32),
    });

    let response = client.list_backtests(request).await?;
    let list_result = response.into_inner();

    println!("✓ Filtered backtest list retrieved");
    println!("  Status Filter: COMPLETED");
    println!("  Count: {}", list_result.backtests.len());

    for backtest in &list_result.backtests {
        assert_eq!(backtest.status, BacktestStatus::Completed as i32);
    }

    Ok(())
}

// ============================================================================
// SECTION 3: ERROR HANDLING E2E TESTS (4 tests)
// ============================================================================

#[tokio::test]
async fn test_e2e_backtest_invalid_date_range() -> Result<()> {
    println!("\n=== E2E Test: Invalid Date Range Handling ===");

    let mut client = create_authenticated_client().await?;

    // Start date after end date (invalid)
    let start_date = Utc::now().timestamp_nanos_opt().unwrap_or(0);
    let end_date = (Utc::now() - Duration::days(30))
        .timestamp_nanos_opt()
        .unwrap_or(0);

    let request = Request::new(StartBacktestRequest {
        strategy_name: "test_strategy".to_string(),
        symbols: vec!["BTC/USD".to_string()],
        start_date_unix_nanos: start_date,
        end_date_unix_nanos: end_date,
        initial_capital: 10000.0,
        parameters: HashMap::new(),
        save_results: false,
        description: "Invalid date range test".to_string(),
    });

    let result = client.start_backtest(request).await;

    // Should either return error or unsuccessful response
    if let Err(status) = result {
        println!("✓ Invalid date range rejected with error");
        println!("  Error: {}", status.message());
    } else if let Ok(response) = result {
        assert!(
            !response.into_inner().success,
            "Invalid date range should not succeed"
        );
        println!("✓ Invalid date range rejected in response");
    }

    Ok(())
}

#[tokio::test]
async fn test_e2e_backtest_invalid_capital() -> Result<()> {
    println!("\n=== E2E Test: Invalid Initial Capital Handling ===");

    let mut client = create_authenticated_client().await?;

    let start_date = (Utc::now() - Duration::days(7))
        .timestamp_nanos_opt()
        .unwrap_or(0);
    let end_date = Utc::now().timestamp_nanos_opt().unwrap_or(0);

    let request = Request::new(StartBacktestRequest {
        strategy_name: "test_strategy".to_string(),
        symbols: vec!["BTC/USD".to_string()],
        start_date_unix_nanos: start_date,
        end_date_unix_nanos: end_date,
        initial_capital: -1000.0, // Invalid negative capital
        parameters: HashMap::new(),
        save_results: false,
        description: "Invalid capital test".to_string(),
    });

    let result = client.start_backtest(request).await;

    assert!(
        result.is_err() || !result.unwrap().into_inner().success,
        "Negative initial capital should be rejected"
    );

    println!("✓ Negative initial capital correctly rejected");

    Ok(())
}

#[tokio::test]
async fn test_e2e_backtest_nonexistent_status() -> Result<()> {
    println!("\n=== E2E Test: Query Status of Nonexistent Backtest ===");

    let mut client = create_authenticated_client().await?;

    let request = Request::new(GetBacktestStatusRequest {
        backtest_id: "nonexistent_backtest_12345".to_string(),
    });

    let result = client.get_backtest_status(request).await;

    assert!(result.is_err(), "Nonexistent backtest should return error");

    if let Err(status) = result {
        println!("✓ Nonexistent backtest correctly rejected");
        println!("  Error code: {:?}", status.code());
        println!("  Error message: {}", status.message());
    }

    Ok(())
}

#[tokio::test]
async fn test_e2e_backtest_unauthenticated_access() -> Result<()> {
    println!("\n=== E2E Test: Unauthenticated Backtest Access ===");

    // Create client without authentication
    let api_addr = get_api_addr();
    let channel = Channel::from_shared(api_addr)?.connect().await?;
    let mut client = BacktestingServiceClient::new(channel);

    let start_date = (Utc::now() - Duration::days(7))
        .timestamp_nanos_opt()
        .unwrap_or(0);
    let end_date = Utc::now().timestamp_nanos_opt().unwrap_or(0);

    let request = Request::new(StartBacktestRequest {
        strategy_name: "test".to_string(),
        symbols: vec!["BTC/USD".to_string()],
        start_date_unix_nanos: start_date,
        end_date_unix_nanos: end_date,
        initial_capital: 10000.0,
        parameters: HashMap::new(),
        save_results: false,
        description: "Unauthenticated test".to_string(),
    });

    let result = client.start_backtest(request).await;

    assert!(
        result.is_err(),
        "Unauthenticated request should be rejected"
    );

    if let Err(status) = result {
        assert_eq!(status.code(), tonic::Code::Unauthenticated);
        println!("✓ Unauthenticated request correctly rejected");
        println!("  Error: {}", status.message());
    }

    Ok(())
}
