//! SecretumVault KMS Integration Tests
//! Tests cover embedded library mode and service mode operations

use std::str::FromStr;

use vault_service::types::*;
use vault_service::KmsService;

/// Test configuration for embedded mode (filesystem)
#[allow(dead_code)]
fn embedded_config() -> KmsBackendConfig {
    KmsBackendConfig::Secretumvault {
        server_url: "http://localhost:8200".to_string(),
        storage_backend: "filesystem".to_string(),
        auth_token: None,
        mount_point: "transit".to_string(),
        key_name: "test-key".to_string(),
        tls_verify: false,
    }
}

/// Test configuration for service mode
fn service_config() -> KmsBackendConfig {
    KmsBackendConfig::Secretumvault {
        server_url: std::env::var("SECRETUMVAULT_URL")
            .unwrap_or_else(|_| "http://localhost:8200".to_string()),
        storage_backend: "surrealdb".to_string(),
        auth_token: std::env::var("SECRETUMVAULT_TOKEN").ok(),
        mount_point: "transit".to_string(),
        key_name: "test-key".to_string(),
        tls_verify: false,
    }
}

#[tokio::test]
#[ignore] // Requires SecretumVault service running
async fn test_secretumvault_service_health_check() {
    let config = service_config();
    let service = KmsService::new(config)
        .await
        .expect("Failed to initialize KMS service");

    let health = service.health_check().await.expect("Health check failed");
    assert!(health, "SecretumVault service should be healthy");
}

#[tokio::test]
#[ignore] // Requires SecretumVault service running
async fn test_secretumvault_get_version() {
    let config = service_config();
    let service = KmsService::new(config)
        .await
        .expect("Failed to initialize KMS service");

    let version = service.get_version().await.expect("Failed to get version");
    assert!(!version.is_empty(), "Version should not be empty");
    println!("SecretumVault version: {}", version);
}

#[tokio::test]
#[ignore] // Requires SecretumVault service running
async fn test_secretumvault_encrypt_decrypt() {
    let config = service_config();
    let service = KmsService::new(config)
        .await
        .expect("Failed to initialize KMS service");

    let plaintext = b"secret data to encrypt";
    let context = EncryptionContext::new();

    // Encrypt
    let ciphertext = service
        .encrypt(plaintext, &context)
        .await
        .expect("Encryption failed");

    assert!(!ciphertext.is_empty(), "Ciphertext should not be empty");
    assert_ne!(
        plaintext,
        &ciphertext[..],
        "Ciphertext should differ from plaintext"
    );

    // Decrypt
    let decrypted = service
        .decrypt(&ciphertext, &context)
        .await
        .expect("Decryption failed");

    assert_eq!(
        plaintext,
        &decrypted[..],
        "Decrypted data should match original plaintext"
    );
}

#[tokio::test]
#[ignore] // Requires SecretumVault service running
async fn test_secretumvault_encrypt_decrypt_large_data() {
    let config = service_config();
    let service = KmsService::new(config)
        .await
        .expect("Failed to initialize KMS service");

    // Large data (1 MB)
    let plaintext = vec![0x42u8; 1024 * 1024];
    let context = EncryptionContext::new();

    // Encrypt
    let ciphertext = service
        .encrypt(&plaintext, &context)
        .await
        .expect("Encryption failed");

    // Decrypt
    let decrypted = service
        .decrypt(&ciphertext, &context)
        .await
        .expect("Decryption failed");

    assert_eq!(
        plaintext, decrypted,
        "Decrypted large data should match original"
    );
}

#[tokio::test]
#[ignore] // Requires SecretumVault service running
async fn test_secretumvault_encrypt_with_context() {
    let config = service_config();
    let service = KmsService::new(config)
        .await
        .expect("Failed to initialize KMS service");

    let plaintext = b"sensitive data";
    let mut context = EncryptionContext::new();
    context.add("environment", "production");
    context.add("service", "api-gateway");

    // Encrypt with context
    let ciphertext = service
        .encrypt(plaintext, &context)
        .await
        .expect("Encryption with context failed");

    // Decrypt with same context
    let decrypted = service
        .decrypt(&ciphertext, &context)
        .await
        .expect("Decryption with context failed");

    assert_eq!(
        plaintext,
        &decrypted[..],
        "Decrypted data should match original"
    );
}

#[tokio::test]
#[ignore] // Requires SecretumVault service running
async fn test_secretumvault_generate_aes128_key() {
    let config = service_config();
    let service = KmsService::new(config)
        .await
        .expect("Failed to initialize KMS service");

    let data_key = service
        .generate_data_key(&KeySpec::Aes128)
        .await
        .expect("Data key generation failed");

    assert_eq!(
        data_key.plaintext.len(),
        16,
        "AES-128 key should be 16 bytes"
    );
    assert!(
        !data_key.ciphertext.is_empty(),
        "Encrypted key should not be empty"
    );
    assert_eq!(
        data_key.key_id, "test-key",
        "Key ID should match configuration"
    );
}

#[tokio::test]
#[ignore] // Requires SecretumVault service running
async fn test_secretumvault_generate_aes256_key() {
    let config = service_config();
    let service = KmsService::new(config)
        .await
        .expect("Failed to initialize KMS service");

    let data_key = service
        .generate_data_key(&KeySpec::Aes256)
        .await
        .expect("Data key generation failed");

    assert_eq!(
        data_key.plaintext.len(),
        32,
        "AES-256 key should be 32 bytes"
    );
    assert!(
        !data_key.ciphertext.is_empty(),
        "Encrypted key should not be empty"
    );
    assert_eq!(
        data_key.key_id, "test-key",
        "Key ID should match configuration"
    );
}

#[tokio::test]
#[ignore] // Requires SecretumVault service running
async fn test_secretumvault_generate_rsa2048_key() {
    let config = service_config();
    let service = KmsService::new(config)
        .await
        .expect("Failed to initialize KMS service");

    let data_key = service
        .generate_data_key(&KeySpec::Rsa2048)
        .await
        .expect("Data key generation failed");

    assert_eq!(
        data_key.plaintext.len(),
        256,
        "RSA-2048 key should be 256 bytes"
    );
    assert!(
        !data_key.ciphertext.is_empty(),
        "Encrypted key should not be empty"
    );
}

#[tokio::test]
#[ignore] // Requires SecretumVault service running
async fn test_secretumvault_generate_rsa4096_key() {
    let config = service_config();
    let service = KmsService::new(config)
        .await
        .expect("Failed to initialize KMS service");

    let data_key = service
        .generate_data_key(&KeySpec::Rsa4096)
        .await
        .expect("Data key generation failed");

    assert_eq!(
        data_key.plaintext.len(),
        512,
        "RSA-4096 key should be 512 bytes"
    );
    assert!(
        !data_key.ciphertext.is_empty(),
        "Encrypted key should not be empty"
    );
}

#[tokio::test]
#[ignore] // Requires SecretumVault service running
async fn test_secretumvault_multiple_operations() {
    let config = service_config();
    let service = KmsService::new(config)
        .await
        .expect("Failed to initialize KMS service");

    let context = EncryptionContext::new();

    // Perform multiple operations
    for i in 0..5 {
        let plaintext = format!("data-{}", i).into_bytes();
        let ciphertext = service
            .encrypt(&plaintext, &context)
            .await
            .expect("Encryption failed");

        let decrypted = service
            .decrypt(&ciphertext, &context)
            .await
            .expect("Decryption failed");

        assert_eq!(plaintext, decrypted, "Data mismatch on iteration {}", i);
    }

    // Generate multiple keys
    for _ in 0..3 {
        let _key = service
            .generate_data_key(&KeySpec::Aes256)
            .await
            .expect("Key generation failed");
    }

    // Health check after operations
    let health = service.health_check().await.expect("Health check failed");
    assert!(health, "Service should still be healthy");
}

#[tokio::test]
#[ignore] // Requires SecretumVault service running
async fn test_secretumvault_binary_data() {
    let config = service_config();
    let service = KmsService::new(config)
        .await
        .expect("Failed to initialize KMS service");

    // Binary data with null bytes and special chars
    let plaintext: Vec<u8> = vec![
        0x00, 0x01, 0x02, 0x03, 0xFF, 0xFE, 0xFD, 0xFC, 0x7F, 0x80, 0x00, 0xFF, 0xAB, 0xCD, 0xEF,
        0x12,
    ];
    let context = EncryptionContext::new();

    let ciphertext = service
        .encrypt(&plaintext, &context)
        .await
        .expect("Binary encryption failed");

    let decrypted = service
        .decrypt(&ciphertext, &context)
        .await
        .expect("Binary decryption failed");

    assert_eq!(
        plaintext, decrypted,
        "Binary data should match after round-trip"
    );
}

#[tokio::test]
#[ignore] // Requires SecretumVault service running
async fn test_secretumvault_empty_data() {
    let config = service_config();
    let service = KmsService::new(config)
        .await
        .expect("Failed to initialize KMS service");

    let plaintext: Vec<u8> = vec![];
    let context = EncryptionContext::new();

    // Empty data encryption might be rejected, which is acceptable
    let result = service.encrypt(&plaintext, &context).await;

    // Either it succeeds and empty data encrypts, or it fails gracefully
    if let Ok(ciphertext) = result {
        let decrypted = service
            .decrypt(&ciphertext, &context)
            .await
            .expect("Empty data decryption failed");
        assert!(decrypted.is_empty(), "Decrypted empty data should be empty");
    }
}

#[tokio::test]
#[ignore] // Requires SecretumVault service running
async fn test_secretumvault_backend_name() {
    let config = service_config();
    let service = KmsService::new(config)
        .await
        .expect("Failed to initialize KMS service");

    let backend_name = service.get_backend_name();
    assert_eq!(
        backend_name, "secretumvault",
        "Backend name should be 'secretumvault'"
    );
}

#[tokio::test]
#[ignore] // Requires SecretumVault service running
async fn test_secretumvault_operations_counter() {
    let config = service_config();
    let service = KmsService::new(config)
        .await
        .expect("Failed to initialize KMS service");

    let initial_count = service.get_operations_count().await;

    let context = EncryptionContext::new();
    let plaintext = b"test";

    // Perform operations
    let _ = service.encrypt(plaintext, &context).await;
    let _ = service.health_check().await;
    let _ = service.get_version().await;

    let final_count = service.get_operations_count().await;
    assert!(
        final_count > initial_count,
        "Operations counter should increment"
    );
}

#[tokio::test]
#[ignore] // Requires SecretumVault service running
async fn test_secretumvault_uptime() {
    let config = service_config();
    let service = KmsService::new(config)
        .await
        .expect("Failed to initialize KMS service");

    // get_uptime() returns u64, always >= 0 by type
    let _uptime = service.get_uptime();
}

#[tokio::test]
async fn test_secretumvault_config_validation() {
    // Invalid storage backend
    let config = KmsBackendConfig::Secretumvault {
        server_url: "http://localhost:8200".to_string(),
        storage_backend: "invalid_backend".to_string(),
        auth_token: None,
        mount_point: "transit".to_string(),
        key_name: "test-key".to_string(),
        tls_verify: false,
    };

    let result = KmsService::new(config).await;
    assert!(
        result.is_err(),
        "Invalid storage backend should fail validation"
    );
}

#[tokio::test]
async fn test_secretumvault_encryption_context_add() {
    let mut context = EncryptionContext::new();

    context.add("key1", "value1");
    context.add("key2", "value2");

    assert_eq!(context.context.len(), 2, "Context should have 2 entries");
    assert_eq!(context.context.get("key1").unwrap(), "value1");
    assert_eq!(context.context.get("key2").unwrap(), "value2");
}

#[tokio::test]
async fn test_secretumvault_encryption_context_from_str() {
    let context_str = "environment=prod,service=api";
    let context = EncryptionContext::from_str(context_str).expect("Failed to parse context");

    assert_eq!(context.context.len(), 2);
    assert_eq!(context.context.get("environment").unwrap(), "prod");
    assert_eq!(context.context.get("service").unwrap(), "api");
}

#[tokio::test]
async fn test_secretumvault_encryption_context_to_string() {
    let mut context = EncryptionContext::new();
    context.add("environment", "prod");
    context.add("service", "api");

    let context_str = context.to_string();
    assert!(
        !context_str.is_empty(),
        "Context string should not be empty"
    );
    assert!(
        context_str.contains("environment=prod"),
        "Context should include environment"
    );
    assert!(
        context_str.contains("service=api"),
        "Context should include service"
    );
}

#[tokio::test]
async fn test_key_spec_sizes() {
    assert_eq!(KeySpec::Aes128.key_size(), 16);
    assert_eq!(KeySpec::Aes256.key_size(), 32);
    assert_eq!(KeySpec::Rsa2048.key_size(), 256);
    assert_eq!(KeySpec::Rsa4096.key_size(), 512);
}

// Integration test runner helper - run with:
// cargo test --package vault-service --test secretumvault_integration --
// --ignored --test-threads=1
//
// Before running tests, start SecretumVault:
// secretumvault server --storage-backend filesystem
//
// Optional: set environment variables for service mode tests:
// export SECRETUMVAULT_URL=http://localhost:8200
// export SECRETUMVAULT_TOKEN=your-token
