//! JWT Integration Tests
//!
//! Comprehensive integration tests for JWT token system including:
//! - Token generation and validation
//! - Token rotation and refresh
//! - Token revocation and blacklist
//! - Concurrent access patterns
//! - Security edge cases

#![allow(unused_imports, clippy::needless_borrows_for_generic_args)]

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

use control_center::auth::{
    jwt::{BlacklistStats, JwtService, TokenType},
    password::PasswordService,
    user::{User, UserRole, UserService},
    AuthService,
};

/// Generate RSA key pair for testing (pre-generated to avoid rand_core
/// conflict)
fn generate_test_keys() -> (Vec<u8>, Vec<u8>) {
    let private_pem = b"-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC7F43HxrVfJJ+k
DQMEjENGqJLnBn6MvJnCu93A4ZNKEEpPGX1Y6V+qiqLH5B7wNMIJ2QVnLjYjKZu5
K5LkVCH8N7N1s9QXiLI7TzQVEH2TQx3MzQFRmVzGlyVFxF0qMHf/mNlZvE0Hb9oI
YdYnBW5k4TRmzrN4THEbWqLcGVVfMVFQqG1j/V3G5dTpV1sN8a9xqJG0dKNmVP2M
H5JG3kQ8CxFqp7pRsGvqH9IVjB6KXj8L3VPQYJxIBKHGZMlsP9SVqH3vwqLLKcBj
G2UvhkVYJ6ZLvYIj7LvZKrCBrP3h5Q7hYpXZqDGYd5dDwWFJ3xkVMkJMGR3D3VrK
R1jvJTEhAgMBAAECggEAXV2TZjfRbhR0IaqU3bpSZvb+6uL+5OgWRO5VbG0XGFmv
1v+2F5hQEZYKKxZvTlF4IxN1J0YQRKt4BG6PZqN9+Uj8z7DVEDqiWLIZSEhUPzVu
E3IKZaEqvGKvEwBglmfqnE6cEZ2Kb9aOx1a5N8UH5q7xQ2d4YxKfYwRBkDYEZmZp
R4tEb0qfKFgIGmCfK5fNvJqXvH/f1xq9QW1qrGYqGAqh7bkxvXGfkRBXh7Q8sAXW
B2XQ8L9CZV5lsP0F7n8nKvDVKfqZfGXlZJZQvJPF5L8p6g7V7uE+6bZlF4YZLRvW
2aq2xZR6bGCWxBpLb2lG1QU0aJLqVmVjAQYDM9PB0QKBgQDfhKn8F5aITVFJhVVu
BN8F2hLRtFDfSPTN0JM1WZq/EqYdLjnlAY8L7kBBzNaKgOGBRqfO1lVv7DG4vPtX
nrLaM3SU3F0lC8tVwqJTUvZS5/Pl3wqAZRlWbWm4Yp6gWXlGPTaWRpGRBOqB+rWP
b8rX2SnZjvFlQZlZPfvHGWpkFQKBgQDYtvXAGNdj8hMZ6mMUKYv3YbF7F2xUlf1x
H0f8I5QGPiZoqzNGJXCEGvNVNEUgR7LY6AeqWtLqRKJsQ5NYb2XzLhA2BVOV1BV2
Z1Y3s0V5XjFE4q7gWmEcLlWyEiKiRCRQC4T7qB+gUQ8lFBrGqQ4vZqE+7pQyYfCt
1yFQnvqrQQKBgQCcvCvE4wMhF2lYFxM4v8qCDXJ9nLMKjYPFn3C0YrP6Zn2Pm7r3
fJGDVBmN5wVLDXqZnSrmh0iBs7PdBqeHALF7jTj9X8tNe+hpXwQ/GWSF8b3q1b6k
wZLhKpJQGpYH0M3l7B6rrM3Vr1E/DQjXvXdqXrL3vKfHQrEk3c9zVCkfTQKBgGVl
qdvBYvdgWHvPzL3+vqvLPVKDnLjN6hXA2cJGaBNpOlm+bI5cJLLnWhJrjCkK+POo
0oPKvnO5qKQjRDWLVCJH3z8MxPjqV6Yk1hAg/4vqV3oQsJDfU3qiDPAHrBbKBlRm
4KYmEqIhqNYJqXN7nnQFOLYFxOaXKqe6HBh1g4QBAoGBAIRXPwKJhwIUfLJ8DQAO
9z9YH3eIiGkLxF8f7WZvVnNwMEpXl4IqN8N3qGXKJfXJI1YfBKXN7LOqCOVFGkVq
JkVN8qPTwAKU8E2U3dGzQVPP2JmGPPNyZJxQJ3sYtKnLkz2YdEbKDhkG5wPDHKKu
LfEK1YMqL/VvAJdIXxqZqDXf
-----END PRIVATE KEY-----";

    let public_pem = b"-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuxeNx8a1XySfpA0DBIxD
RqiS5wZ+jLyZwrvdwOGTShBKTxl9WOlfqoqix+Qe8DTCCdkFZy42IymbhSuS5FQh
/DezdLPUF4iyO080FRB9k0MdzM0BUZlcxpclRcRdKjB3/5jZWbxNB2/aCGHWJwVu
ZOE0Zs6zeExxG1qi3BlVXzFRUKhtY/1dxuXU6Vdbl/GvcaiRtHSjZlT9jB+SRt5E
PAsTaqe6UbBr6h/SFYweil4/C91T0GCcSAShxmTJbD/Ulah978KizynAYxtlL4ZF
WCemS72CI+y72SqwgaZ94eUO4WKV2agxmHeXQ8FhSd8ZFTJCTBKDW91Kykdo7yUx
IQIDAQAB
-----END PUBLIC KEY-----";

    (private_pem.to_vec(), public_pem.to_vec())
}

/// Create JWT service for testing
fn create_jwt_service() -> JwtService {
    let (private_key, public_key) = generate_test_keys();
    JwtService::new(
        &private_key,
        &public_key,
        "test-control-center",
        vec!["orchestrator".to_string(), "cli".to_string()],
    )
    .unwrap()
}

#[test]
fn test_token_pair_generation() {
    let jwt_service = create_jwt_service();

    let token_pair = jwt_service
        .generate_token_pair("user123", "workspace1", "perm_hash_abc", None)
        .unwrap();

    assert_eq!(token_pair.token_type, "Bearer");
    assert!(token_pair.expires_in > 0);
    assert!(!token_pair.access_token.is_empty());
    assert!(!token_pair.refresh_token.is_empty());
    assert_ne!(token_pair.access_token, token_pair.refresh_token);
}

#[test]
fn test_token_validation_success() {
    let jwt_service = create_jwt_service();

    let token_pair = jwt_service
        .generate_token_pair("user123", "workspace1", "perm_hash_abc", None)
        .unwrap();

    // Validate access token
    let access_claims = jwt_service
        .validate_token(&token_pair.access_token)
        .unwrap();
    assert_eq!(access_claims.sub, "user123");
    assert_eq!(access_claims.workspace, "workspace1");
    assert_eq!(access_claims.token_type, TokenType::Access);

    // Validate refresh token
    let refresh_claims = jwt_service
        .validate_token(&token_pair.refresh_token)
        .unwrap();
    assert_eq!(refresh_claims.sub, "user123");
    assert_eq!(refresh_claims.token_type, TokenType::Refresh);
}

#[test]
fn test_token_validation_with_metadata() {
    let jwt_service = create_jwt_service();

    let mut metadata = HashMap::new();
    metadata.insert("ip_address".to_string(), "192.168.1.100".to_string());
    metadata.insert("user_agent".to_string(), "test-client/1.0".to_string());
    metadata.insert("session_id".to_string(), "sess_xyz789".to_string());

    let token_pair = jwt_service
        .generate_token_pair(
            "user456",
            "workspace2",
            "perm_hash_def",
            Some(metadata.clone()),
        )
        .unwrap();

    let claims = jwt_service
        .validate_token(&token_pair.access_token)
        .unwrap();

    assert!(claims.metadata.is_some());
    let token_metadata = claims.metadata.unwrap();
    assert_eq!(
        token_metadata.get("ip_address"),
        Some(&"192.168.1.100".to_string())
    );
    assert_eq!(
        token_metadata.get("user_agent"),
        Some(&"test-client/1.0".to_string())
    );
    assert_eq!(
        token_metadata.get("session_id"),
        Some(&"sess_xyz789".to_string())
    );
}

#[test]
fn test_token_rotation() {
    let jwt_service = create_jwt_service();

    // Generate initial token pair
    let initial_pair = jwt_service
        .generate_token_pair("user123", "workspace1", "perm_hash_abc", None)
        .unwrap();

    // Rotate using refresh token
    let rotated_pair = jwt_service
        .rotate_token(&initial_pair.refresh_token)
        .unwrap();

    // New tokens should be different
    assert_ne!(rotated_pair.access_token, initial_pair.access_token);
    assert_ne!(rotated_pair.refresh_token, initial_pair.refresh_token);

    // New tokens should be valid
    let new_claims = jwt_service
        .validate_token(&rotated_pair.access_token)
        .unwrap();
    assert_eq!(new_claims.sub, "user123");
    assert_eq!(new_claims.workspace, "workspace1");

    // Old refresh token should be revoked
    let old_refresh_validation = jwt_service.validate_token(&initial_pair.refresh_token);
    assert!(old_refresh_validation.is_err());
}

#[test]
fn test_token_revocation() {
    let jwt_service = create_jwt_service();

    let token_pair = jwt_service
        .generate_token_pair("user123", "workspace1", "perm_hash_abc", None)
        .unwrap();

    // Initially valid
    let claims = jwt_service
        .validate_token(&token_pair.access_token)
        .unwrap();
    assert!(!jwt_service.is_revoked(&claims.jti).unwrap());

    // Revoke token
    jwt_service.revoke_token(&claims.jti, claims.exp).unwrap();

    // Check revoked
    assert!(jwt_service.is_revoked(&claims.jti).unwrap());

    // Validation should fail
    let validation_result = jwt_service.validate_token(&token_pair.access_token);
    assert!(validation_result.is_err());
}

#[test]
fn test_blacklist_cleanup() {
    let jwt_service = create_jwt_service();

    // Create expired token (simulate with past timestamp)
    let expired_jti = "expired_token_123";
    let expired_exp = chrono::Utc::now().timestamp() - 3600; // 1 hour ago
    jwt_service.revoke_token(expired_jti, expired_exp).unwrap();

    // Create valid token
    let token_pair = jwt_service
        .generate_token_pair("user123", "workspace1", "perm_hash_abc", None)
        .unwrap();
    let claims = jwt_service
        .validate_token(&token_pair.access_token)
        .unwrap();
    jwt_service.revoke_token(&claims.jti, claims.exp).unwrap();

    // Check stats before cleanup
    let stats_before = jwt_service.blacklist_stats().unwrap();
    assert_eq!(stats_before.total_revoked, 2);

    // Cleanup expired tokens
    let removed_count = jwt_service.cleanup_expired_tokens().unwrap();
    assert_eq!(removed_count, 1);

    // Check stats after cleanup
    let stats_after = jwt_service.blacklist_stats().unwrap();
    assert_eq!(stats_after.total_revoked, 1);
    assert_eq!(stats_after.active_revocations, 1);
}

#[test]
fn test_multiple_rotations() {
    let jwt_service = create_jwt_service();

    let mut current_pair = jwt_service
        .generate_token_pair("user123", "workspace1", "perm_hash_abc", None)
        .unwrap();

    // Perform 5 rotations
    for i in 0..5 {
        let new_pair = jwt_service
            .rotate_token(&current_pair.refresh_token)
            .unwrap();

        // Verify new tokens work
        let claims = jwt_service.validate_token(&new_pair.access_token).unwrap();
        assert_eq!(claims.sub, "user123");

        // Old refresh token should fail
        let old_validation = jwt_service.validate_token(&current_pair.refresh_token);
        assert!(old_validation.is_err(), "Rotation {} failed", i);

        current_pair = new_pair;
    }

    // Final tokens should still work
    let final_claims = jwt_service
        .validate_token(&current_pair.access_token)
        .unwrap();
    assert_eq!(final_claims.sub, "user123");
}

#[test]
fn test_extract_token_from_header() {
    let test_cases = vec![
        ("Bearer abc123xyz", Ok("abc123xyz")),
        ("bearer abc123xyz", Err(())), // Wrong case
        ("Token abc123xyz", Err(())),  // Wrong prefix
        ("Bearer", Err(())),           // Missing token
        ("abc123xyz", Err(())),        // No prefix
    ];

    for (header, expected) in test_cases {
        let result = JwtService::extract_token_from_header(header);
        match expected {
            Ok(token) => assert_eq!(result.unwrap(), token),
            Err(_) => assert!(result.is_err()),
        }
    }
}

#[test]
fn test_concurrent_token_operations() {
    use std::sync::Arc;
    use std::thread;

    let jwt_service = Arc::new(create_jwt_service());
    let mut handles = vec![];

    // Spawn 10 threads generating and validating tokens concurrently
    for i in 0..10 {
        let service = jwt_service.clone();
        let handle = thread::spawn(move || {
            let user_id = format!("user{}", i);
            let workspace = format!("workspace{}", i);

            // Generate token pair
            let token_pair = service
                .generate_token_pair(&user_id, &workspace, "perm_hash", None)
                .unwrap();

            // Validate access token
            let claims = service.validate_token(&token_pair.access_token).unwrap();
            assert_eq!(claims.sub, user_id);
            assert_eq!(claims.workspace, workspace);

            // Rotate token
            let new_pair = service.rotate_token(&token_pair.refresh_token).unwrap();
            let new_claims = service.validate_token(&new_pair.access_token).unwrap();
            assert_eq!(new_claims.sub, user_id);
        });
        handles.push(handle);
    }

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

#[test]
fn test_token_expiry_detection() {
    let jwt_service = create_jwt_service();

    let token_pair = jwt_service
        .generate_token_pair("user123", "workspace1", "perm_hash_abc", None)
        .unwrap();

    let claims = jwt_service
        .validate_token(&token_pair.access_token)
        .unwrap();

    // Token should not be expired immediately
    assert!(!claims.is_expired());

    // Token should have remaining validity
    let remaining = claims.remaining_validity();
    assert!(remaining.as_secs() > 0);
}

#[tokio::test]
async fn test_full_auth_flow() {
    let jwt_service = create_jwt_service();
    let password_service = PasswordService::new();
    let user_service = UserService::new();
    let auth_service = AuthService::new(jwt_service, password_service, user_service);

    // Create user
    let password = "SecurePassword123!";
    let password_hash = auth_service.password.hash_password(password).unwrap();

    let user = User::new(
        "testuser",
        "test@example.com",
        "Test User",
        password_hash,
        vec![UserRole::Developer],
    );

    auth_service.user.create_user(user).await.unwrap();

    // Login
    let token_pair = auth_service
        .login("testuser", password, "workspace1")
        .await
        .unwrap();

    // Validate access token
    let claims = auth_service.validate(&token_pair.access_token).unwrap();
    assert_eq!(claims.workspace, "workspace1");

    // Rotate tokens
    let new_pair = auth_service.refresh(&token_pair.refresh_token).unwrap();
    assert_ne!(new_pair.access_token, token_pair.access_token);

    // Logout
    auth_service.logout(&new_pair.access_token).await.unwrap();

    // Token should be revoked
    let validation_result = auth_service.validate(&new_pair.access_token);
    assert!(validation_result.is_err());
}

#[test]
fn test_invalid_signature_detection() {
    let (private_key1, public_key1) = generate_test_keys();
    let (private_key2, _) = generate_test_keys(); // Different key pair

    // Service 1 generates token
    let jwt_service1 = JwtService::new(
        &private_key1,
        &public_key1,
        "test-issuer",
        vec!["test-audience".to_string()],
    )
    .unwrap();

    let token_pair = jwt_service1
        .generate_token_pair("user123", "workspace1", "perm_hash", None)
        .unwrap();

    // Service 2 with different keys tries to validate
    let jwt_service2 = JwtService::new(
        &private_key2,
        &public_key1,
        "test-issuer",
        vec!["test-audience".to_string()],
    )
    .unwrap();

    // Should fail signature verification
    let validation_result = jwt_service2.validate_token(&token_pair.access_token);
    assert!(validation_result.is_err());
}

#[test]
fn test_blacklist_statistics() {
    let jwt_service = create_jwt_service();

    // Generate and revoke multiple tokens
    for i in 0..5 {
        let token_pair = jwt_service
            .generate_token_pair(&format!("user{}", i), "workspace1", "perm_hash", None)
            .unwrap();

        let claims = jwt_service
            .validate_token(&token_pair.access_token)
            .unwrap();
        jwt_service.revoke_token(&claims.jti, claims.exp).unwrap();
    }

    let stats = jwt_service.blacklist_stats().unwrap();
    assert_eq!(stats.total_revoked, 5);
    assert_eq!(stats.active_revocations, 5);
    assert_eq!(stats.expired_tokens, 0);
}

#[test]
fn test_token_cannot_rotate_with_access_token() {
    let jwt_service = create_jwt_service();

    let token_pair = jwt_service
        .generate_token_pair("user123", "workspace1", "perm_hash_abc", None)
        .unwrap();

    // Attempting to rotate with access token should fail
    let rotation_result = jwt_service.rotate_token(&token_pair.access_token);
    assert!(rotation_result.is_err());
}
