//! TDD Tests for FXT ML Trading Commands
//!
//! RED Phase: These tests are EXPECTED TO FAIL initially.
//! The implementation will be created after these tests are written.
//!
//! Test Coverage:
//! - `fxt trade ml submit` - Submit ML-generated order
//! - `fxt trade ml predictions` - View prediction history
//! - `fxt trade ml performance` - View model performance
//! - Error handling for missing required arguments
//! - Model filtering and limit options

// Suppress false-positive unused_crate_dependencies warnings
// dev-dependencies are shared across ALL test targets in the crate
// This test may not use all deps, but they are required by other integration tests
#![allow(unused_crate_dependencies)]
#![allow(clippy::tests_outside_test_module, clippy::str_to_string, clippy::non_ascii_literal, clippy::shadow_unrelated, clippy::shadow_reuse, clippy::unwrap_used, clippy::expect_used, clippy::assertions_on_result_states, clippy::use_debug, clippy::let_underscore_must_use, clippy::string_add, clippy::string_add_assign, clippy::wildcard_enum_match_arm, clippy::unseparated_literal_suffix, clippy::indexing_slicing, clippy::doc_markdown, clippy::similar_names, clippy::impl_trait_in_params, unused_imports, dead_code, clippy::panic, unreachable_pub)]

use assert_cmd::Command;
use predicates::prelude::*;
use serial_test::serial;
use std::path::PathBuf;

// ============================================================================
// Test Authentication Helper Module
// ============================================================================
//
// Provides test setup/teardown for JWT authentication in integration tests.
// Uses real JWT token generation and FileTokenStorage for authenticity.
//
// ANTI-WORKAROUND COMPLIANCE:
// ✅ Real JWT token generation using jsonwebtoken crate
// ✅ Real FileTokenStorage (just with test tokens in temp directory)
// ✅ Proper cleanup after tests complete
// ❌ NO STUBS or mocks
// ❌ NO PLACEHOLDERS or simplified tokens

mod test_auth {
    use anyhow::{Context, Result};
    use std::path::PathBuf;

    /// Setup test authentication environment
    ///
    /// Creates valid JWT tokens in a temporary directory and returns the path
    /// for cleanup. Tokens are valid for 1 hour.
    ///
    /// # Returns
    /// * `Ok(PathBuf)` - Path to temporary token directory (for cleanup)
    /// * `Err(anyhow::Error)` - Failed to setup authentication
    pub fn setup_test_auth() -> Result<PathBuf> {
        use fxt::auth::jwt_generator;
        use fxt::auth::token_manager::{FileTokenStorage, TokenStorage};

        // Create isolated temp directory for this test run
        let temp_dir =
            std::env::temp_dir().join(format!("foxhunt_fxt_test_{}", std::process::id()));

        // Create FileTokenStorage in temp directory
        let storage = FileTokenStorage::with_directory(temp_dir.clone())
            .context("Failed to create FileTokenStorage for tests")?;

        // Generate valid access token (1 hour expiry)
        let (access_token, _jti) = jwt_generator::generate_access_token(
            "test_user",
            vec!["trader".to_string()],
            vec![
                "api.access".to_string(),
                "trading.submit".to_string(),
                "trading.view".to_string(),
            ],
            3600, // 1 hour
        )
        .context("Failed to generate test access token")?;

        // Generate valid refresh token (2 hours expiry)
        let (refresh_token, _jti) = jwt_generator::generate_refresh_token(
            "test_user",
            7200, // 2 hours
        )
        .context("Failed to generate test refresh token")?;

        // Use tokio runtime to store tokens (FileTokenStorage is async)
        tokio::runtime::Runtime::new()
            .context("Failed to create tokio runtime")?
            .block_on(async {
                storage
                    .store_access_token(&access_token)
                    .await
                    .context("Failed to store test access token")?;

                storage
                    .store_refresh_token(&refresh_token)
                    .await
                    .context("Failed to store test refresh token")?;

                Ok::<(), anyhow::Error>(())
            })?;

        println!(
            "✓ Test authentication setup complete in: {}",
            temp_dir.display()
        );
        Ok(temp_dir)
    }

    /// Cleanup test authentication environment
    ///
    /// Removes temporary token directory and all tokens.
    ///
    /// # Arguments
    /// * `token_dir` - Path to temporary token directory from setup_test_auth()
    pub fn cleanup_test_auth(token_dir: &PathBuf) {
        if token_dir.exists() {
            if let Err(e) = std::fs::remove_dir_all(token_dir) {
                eprintln!("⚠ Warning: Failed to cleanup test token directory: {}", e);
            } else {
                println!("✓ Test authentication cleanup complete");
            }
        }
    }

    /// Setup authentication with environment override
    ///
    /// Creates tokens in temporary directory and sets XDG_CONFIG_HOME to
    /// redirect FileTokenStorage to that temp directory.
    ///
    /// Also sets FOXHUNT_ENCRYPTION_KEY to ensure consistent encryption/decryption
    /// between test process and FXT binary process.
    ///
    /// This approach allows the actual FXT binary to find the test tokens
    /// without code modifications.
    ///
    /// # Returns
    /// * `Ok((PathBuf, Option<String>, Option<String>))` - (temp_dir, original_config_home, original_encryption_key) for cleanup
    pub fn setup_test_auth_with_env_override() -> Result<(PathBuf, Option<String>, Option<String>)>
    {
        // Save original environment variables
        let original_config_home = std::env::var("XDG_CONFIG_HOME").ok();
        let original_encryption_key = std::env::var("FOXHUNT_ENCRYPTION_KEY").ok();

        // Generate a consistent encryption key for this test run
        // This ensures both the test process and FXT binary use the same key
        let encryption_key = hex::encode([42u8; 32]); // Simple deterministic key for tests
        std::env::set_var("FOXHUNT_ENCRYPTION_KEY", &encryption_key);

        // Create temp directory structure: temp/foxhunt-fxt/tokens/
        let temp_base =
            std::env::temp_dir().join(format!("foxhunt_fxt_config_{}", std::process::id()));
        let config_home = temp_base.clone();
        let token_dir = config_home.join("foxhunt-fxt").join("tokens");

        std::fs::create_dir_all(&token_dir)
            .context("Failed to create token directory structure")?;

        // Set XDG_CONFIG_HOME to temp directory
        std::env::set_var("XDG_CONFIG_HOME", &config_home);

        // Generate and store tokens
        use fxt::auth::jwt_generator;
        use fxt::auth::token_manager::{FileTokenStorage, TokenStorage};

        let storage = FileTokenStorage::new()
            .context("Failed to create FileTokenStorage (should use temp XDG_CONFIG_HOME)")?;

        let (access_token, _) = jwt_generator::generate_access_token(
            "test_user",
            vec!["trader".to_string()],
            vec![
                "api.access".to_string(),
                "trading.submit".to_string(),
                "trading.view".to_string(),
            ],
            3600,
        )?;

        let (refresh_token, _) = jwt_generator::generate_refresh_token("test_user", 7200)?;

        tokio::runtime::Runtime::new()?.block_on(async {
            storage.store_access_token(&access_token).await?;
            storage.store_refresh_token(&refresh_token).await?;
            Ok::<(), anyhow::Error>(())
        })?;

        println!(
            "✓ Test auth with env override: XDG_CONFIG_HOME={}, FOXHUNT_ENCRYPTION_KEY=<set>",
            config_home.display()
        );
        Ok((temp_base, original_config_home, original_encryption_key))
    }

    /// Cleanup authentication environment override
    pub fn cleanup_test_auth_with_env_override(
        temp_base: &PathBuf,
        original_config_home: Option<String>,
        original_encryption_key: Option<String>,
    ) {
        // Restore original XDG_CONFIG_HOME
        match original_config_home {
            Some(original) => std::env::set_var("XDG_CONFIG_HOME", original),
            None => std::env::remove_var("XDG_CONFIG_HOME"),
        }

        // Restore original FOXHUNT_ENCRYPTION_KEY
        match original_encryption_key {
            Some(original) => std::env::set_var("FOXHUNT_ENCRYPTION_KEY", original),
            None => std::env::remove_var("FOXHUNT_ENCRYPTION_KEY"),
        }

        // Cleanup temp directory
        if temp_base.exists() {
            let _ = std::fs::remove_dir_all(temp_base);
        }
    }
}

/// RED TEST 1: ML order submission command
/// Expected to FAIL - command doesn't exist yet
#[test]
#[serial]
fn test_fxt_trade_ml_submit_command() {
    // Setup test authentication
    let (temp_base, original_config, original_key) = test_auth::setup_test_auth_with_env_override()
        .expect("Failed to setup test authentication");

    let mut cmd = Command::cargo_bin("fxt").unwrap();

    cmd.arg("trade")
        .arg("ml")
        .arg("submit")
        .arg("--symbol")
        .arg("ES.FUT")
        .arg("--account")
        .arg("test_account");

    // This will FAIL because the command doesn't exist yet (RED phase)
    cmd.assert()
        .success()
        .stdout(predicate::str::contains("ML order submitted"))
        .stdout(predicate::str::contains("Order ID:"))
        .stdout(predicate::str::contains("Confidence:"));

    // Cleanup
    test_auth::cleanup_test_auth_with_env_override(&temp_base, original_config, original_key);
}

/// RED TEST 2: ML predictions viewing command
/// Expected to FAIL - command doesn't exist yet
#[serial]
#[test]
fn test_fxt_trade_ml_predictions_command() {
    // Setup test authentication
    let (temp_base, original_config, original_key) = test_auth::setup_test_auth_with_env_override()
        .expect("Failed to setup test authentication");

    let mut cmd = Command::cargo_bin("fxt").unwrap();

    cmd.arg("trade")
        .arg("ml")
        .arg("predictions")
        .arg("--symbol")
        .arg("ES.FUT")
        .arg("--limit")
        .arg("10");

    // This will FAIL because the command doesn't exist yet (RED phase)
    cmd.assert()
        .success()
        .stdout(predicate::str::contains("ML Predictions for ES.FUT"))
        .stdout(predicate::str::contains("Predicted Action"))
        .stdout(predicate::str::contains("Confidence"));

    // Cleanup
    test_auth::cleanup_test_auth_with_env_override(&temp_base, original_config, original_key);
}

/// RED TEST 3: ML performance metrics command
/// Expected to FAIL - command doesn't exist yet
#[serial]
#[test]
fn test_fxt_trade_ml_performance_command() {
    // Setup test authentication
    let (temp_base, original_config, original_key) = test_auth::setup_test_auth_with_env_override()
        .expect("Failed to setup test authentication");

    let mut cmd = Command::cargo_bin("fxt").unwrap();

    cmd.arg("trade").arg("ml").arg("performance");

    // This will FAIL because the command doesn't exist yet (RED phase)
    cmd.assert()
        .success()
        .stdout(predicate::str::contains("ML Model Performance"))
        .stdout(predicate::str::contains("Accuracy"))
        .stdout(predicate::str::contains("Sharpe Ratio"));

    // Cleanup
    test_auth::cleanup_test_auth_with_env_override(&temp_base, original_config, original_key);
}

/// RED TEST 4: ML order submission with specific model selection
/// Expected to FAIL - command doesn't exist yet
#[serial]
#[test]
fn test_fxt_trade_ml_submit_with_model_filter() {
    // Setup test authentication
    let (temp_base, original_config, original_key) = test_auth::setup_test_auth_with_env_override()
        .expect("Failed to setup test authentication");

    let mut cmd = Command::cargo_bin("fxt").unwrap();

    cmd.arg("trade")
       .arg("ml")
       .arg("submit")
       .arg("--symbol").arg("ES.FUT")
       .arg("--model").arg("DQN")  // Use DQN only, not ensemble
       .arg("--account").arg("test_account");

    // This will FAIL because the command doesn't exist yet (RED phase)
    cmd.assert()
        .success()
        .stdout(predicate::str::contains("Model:"))
        .stdout(predicate::str::contains("DQN"));

    // Cleanup
    test_auth::cleanup_test_auth_with_env_override(&temp_base, original_config, original_key);
}

/// RED TEST 5: ML predictions with model and limit filters
/// Expected to FAIL - command doesn't exist yet
#[serial]
#[test]
fn test_fxt_trade_ml_predictions_with_filters() {
    // Setup test authentication
    let (temp_base, original_config, original_key) = test_auth::setup_test_auth_with_env_override()
        .expect("Failed to setup test authentication");

    let mut cmd = Command::cargo_bin("fxt").unwrap();

    cmd.arg("trade")
        .arg("ml")
        .arg("predictions")
        .arg("--symbol")
        .arg("ES.FUT")
        .arg("--model")
        .arg("MAMBA2")
        .arg("--limit")
        .arg("5");

    // This will FAIL because the command doesn't exist yet (RED phase)
    cmd.assert()
        .success()
        .stdout(predicate::str::contains("MAMBA2"));

    // Cleanup
    test_auth::cleanup_test_auth_with_env_override(&temp_base, original_config, original_key);
}

/// RED TEST 6: Error handling - missing required symbol argument
/// Expected to FAIL - command doesn't exist yet
#[test]
fn test_fxt_trade_ml_submit_requires_symbol() {
    let mut cmd = Command::cargo_bin("fxt").unwrap();

    cmd.arg("trade")
        .arg("ml")
        .arg("submit")
        .arg("--account")
        .arg("test_account");

    // This will FAIL because the command doesn't exist yet (RED phase)
    cmd.assert()
        .failure()
        .stderr(predicate::str::contains("required").or(predicate::str::contains("symbol")));
}

/// RED TEST 7: Error handling - missing required account argument
/// Expected to FAIL - command doesn't exist yet
#[test]
fn test_fxt_trade_ml_submit_requires_account() {
    let mut cmd = Command::cargo_bin("fxt").unwrap();

    cmd.arg("trade")
        .arg("ml")
        .arg("submit")
        .arg("--symbol")
        .arg("ES.FUT");

    // This will FAIL because the command doesn't exist yet (RED phase)
    cmd.assert()
        .failure()
        .stderr(predicate::str::contains("required").or(predicate::str::contains("account")));
}

/// RED TEST 8: ML performance with model filter
/// Expected to FAIL - command doesn't exist yet
#[serial]
#[test]
fn test_fxt_trade_ml_performance_with_model_filter() {
    // Setup test authentication
    let (temp_base, original_config, original_key) = test_auth::setup_test_auth_with_env_override()
        .expect("Failed to setup test authentication");

    let mut cmd = Command::cargo_bin("fxt").unwrap();

    cmd.arg("trade")
        .arg("ml")
        .arg("performance")
        .arg("--model")
        .arg("PPO");

    // This will FAIL because the command doesn't exist yet (RED phase)
    cmd.assert()
        .success()
        .stdout(predicate::str::contains("PPO"));

    // Cleanup
    test_auth::cleanup_test_auth_with_env_override(&temp_base, original_config, original_key);
}

/// RED TEST 9: Ensemble mode output verification
/// Expected to FAIL - command doesn't exist yet
#[test]
#[serial]
fn test_fxt_trade_ml_submit_ensemble_mode() {
    // Setup test authentication
    let (temp_base, original_config, original_key) = test_auth::setup_test_auth_with_env_override()
        .expect("Failed to setup test authentication");

    let mut cmd = Command::cargo_bin("fxt").unwrap();

    cmd.arg("trade")
        .arg("ml")
        .arg("submit")
        .arg("--symbol")
        .arg("ES.FUT")
        .arg("--account")
        .arg("test_account");
    // No --model flag = ensemble mode

    // This will FAIL because the command doesn't exist yet (RED phase)
    cmd.assert()
        .success()
        .stdout(predicate::str::contains("Ensemble"));

    // Cleanup
    test_auth::cleanup_test_auth_with_env_override(&temp_base, original_config, original_key);
}
