//! Tests for the audit logging module.
//! 
//! These tests verify audit log creation, reading, and management functionality.
//! All tests are async to match the async API of the audit module.
//! 
//! Note: These tests change the current directory, so they must run sequentially
//! to avoid race conditions. A static mutex ensures tests don't run in parallel.

use freneng::audit::{log_audit_entry, read_audit_log, clear_audit_log};
use std::path::PathBuf;
use std::sync::Mutex;
use tempfile::TempDir;

// Mutex to ensure tests run sequentially (they change the current directory)
static TEST_MUTEX: Mutex<()> = Mutex::new(());

#[tokio::test]
async fn test_log_audit_entry_creates_file() {
    let _guard = TEST_MUTEX.lock().unwrap();
    let temp_dir = TempDir::new().unwrap();
    let old_dir = std::env::current_dir().unwrap();
    std::env::set_current_dir(temp_dir.path()).unwrap();
    let _keep_alive = &temp_dir;
    
    let successful = vec![
        (PathBuf::from("old1.txt"), PathBuf::from("new1.txt")),
        (PathBuf::from("old2.txt"), PathBuf::from("new2.txt")),
    ];
    
    let result = log_audit_entry(
        "fren list *.txt transform \"%N_backup.%E\" rename --yes",
        Some("%N_backup.%E".to_string()),
        PathBuf::from("."),
        successful,
        vec![],
        vec![],
    ).await;
    
    assert!(result.is_ok());
    
    // Check file exists in current directory (temp_dir)
    let audit_file = std::env::current_dir().unwrap().join(".fren_audit.log");
    assert!(audit_file.exists(), "Audit file should exist at: {}", audit_file.display());
    
    std::env::set_current_dir(&old_dir).unwrap();
}

#[tokio::test]
async fn test_log_audit_entry_with_skipped_and_errors() {
    let _guard = TEST_MUTEX.lock().unwrap();
    let temp_dir = TempDir::new().unwrap();
    let old_dir = std::env::current_dir().unwrap();
    std::env::set_current_dir(temp_dir.path()).unwrap();
    let _keep_alive = &temp_dir;
    
    let successful = vec![
        (PathBuf::from("old1.txt"), PathBuf::from("new1.txt")),
    ];
    let skipped = vec![
        (PathBuf::from("old2.txt"), "Target exists".to_string()),
    ];
    let errors = vec![
        (PathBuf::from("old3.txt"), "Permission denied".to_string()),
    ];
    
    let result = log_audit_entry(
        "fren list *.txt rename \"%N_backup.%E\" --yes",
        Some("%N_backup.%E".to_string()),
        PathBuf::from("."),
        successful,
        skipped,
        errors,
    ).await;
    
    assert!(result.is_ok());
    
    // Check file exists in current directory
    let audit_file = std::env::current_dir().unwrap().join(".fren_audit.log");
    assert!(audit_file.exists());
    
    std::env::set_current_dir(&old_dir).unwrap();
}

#[tokio::test]
async fn test_read_audit_log_empty() {
    let _guard = TEST_MUTEX.lock().unwrap();
    let temp_dir = TempDir::new().unwrap();
    let old_dir = std::env::current_dir().unwrap();
    std::env::set_current_dir(temp_dir.path()).unwrap();
    let _keep_alive = &temp_dir;
    
    // No file should exist yet
    let audit_file = std::env::current_dir().unwrap().join(".fren_audit.log");
    assert!(!audit_file.exists());
    
    let entries = read_audit_log().await.unwrap();
    assert_eq!(entries.len(), 0);
    
    std::env::set_current_dir(&old_dir).unwrap();
}

#[tokio::test]
async fn test_read_audit_log_multiple_entries() {
    let _guard = TEST_MUTEX.lock().unwrap();
    let temp_dir = TempDir::new().unwrap();
    let old_dir = std::env::current_dir().unwrap();
    std::env::set_current_dir(temp_dir.path()).unwrap();
    let _keep_alive = &temp_dir;
    
    // Log multiple entries
    for i in 0..3 {
        let result = log_audit_entry(
            &format!("fren list file{}.txt rename \"%N_backup.%E\" --yes", i),
            Some("%N_backup.%E".to_string()),
            PathBuf::from("."),
            vec![(PathBuf::from(format!("file{}.txt", i)), PathBuf::from(format!("file{}_backup.txt", i)))],
            vec![],
            vec![],
        ).await;
        assert!(result.is_ok(), "Failed to log entry {}: {:?}", i, result);
    }
    
    // Verify file exists before reading
    let audit_file = std::env::current_dir().unwrap().join(".fren_audit.log");
    assert!(audit_file.exists(), "Audit file should exist at: {}", audit_file.display());
    
    let entries = read_audit_log().await.unwrap();
    assert_eq!(entries.len(), 3);
    
    // Should be most recent first
    assert!(entries[0].timestamp >= entries[1].timestamp);
    assert!(entries[1].timestamp >= entries[2].timestamp);
    
    std::env::set_current_dir(&old_dir).unwrap();
}

#[tokio::test]
async fn test_audit_entry_contains_user() {
    let _guard = TEST_MUTEX.lock().unwrap();
    let temp_dir = TempDir::new().unwrap();
    let old_dir = std::env::current_dir().unwrap();
    std::env::set_current_dir(temp_dir.path()).unwrap();
    let _keep_alive = &temp_dir;
    
    let result = log_audit_entry(
        "fren list *.txt rename \"%N.%E\" --yes",
        Some("%N.%E".to_string()),
        PathBuf::from("."),
        vec![(PathBuf::from("test.txt"), PathBuf::from("new.txt"))],
        vec![],
        vec![],
    ).await;
    assert!(result.is_ok());
    
    // Verify file exists before reading
    let audit_file = std::env::current_dir().unwrap().join(".fren_audit.log");
    assert!(audit_file.exists(), "Audit file should exist at: {}", audit_file.display());
    
    let entries = read_audit_log().await.unwrap();
    assert_eq!(entries.len(), 1);
    // User may or may not be set depending on environment
    // Just verify the entry exists and has the expected structure
    
    std::env::set_current_dir(&old_dir).unwrap();
}

#[tokio::test]
async fn test_audit_entry_contains_command() {
    let _guard = TEST_MUTEX.lock().unwrap();
    let temp_dir = TempDir::new().unwrap();
    let old_dir = std::env::current_dir().unwrap();
    std::env::set_current_dir(temp_dir.path()).unwrap();
    let _keep_alive = &temp_dir;
    
    let command = "fren list *.txt transform \"%N_backup.%E\" rename --yes";
    let result = log_audit_entry(
        command,
        Some("%N_backup.%E".to_string()),
        PathBuf::from("."),
        vec![],
        vec![],
        vec![],
    ).await;
    assert!(result.is_ok());
    
    // Verify file exists before reading
    let audit_file = std::env::current_dir().unwrap().join(".fren_audit.log");
    assert!(audit_file.exists());
    
    let entries = read_audit_log().await.unwrap();
    assert_eq!(entries.len(), 1);
    assert_eq!(entries[0].command, command);
    assert_eq!(entries[0].pattern, Some("%N_backup.%E".to_string()));
    
    std::env::set_current_dir(&old_dir).unwrap();
}

#[tokio::test]
async fn test_clear_audit_log() {
    let _guard = TEST_MUTEX.lock().unwrap();
    let temp_dir = TempDir::new().unwrap();
    let old_dir = std::env::current_dir().unwrap();
    std::env::set_current_dir(temp_dir.path()).unwrap();
    let _keep_alive = &temp_dir;
    
    // Create an audit entry
    let result = log_audit_entry(
        "fren list *.txt rename \"%N.%E\" --yes",
        Some("%N.%E".to_string()),
        PathBuf::from("."),
        vec![],
        vec![],
        vec![],
    ).await;
    assert!(result.is_ok());
    
    // Verify file exists before clearing
    let audit_file = std::env::current_dir().unwrap().join(".fren_audit.log");
    assert!(audit_file.exists(), "Audit file should exist before clearing at: {}", audit_file.display());
    
    // Clear it
    let clear_result = clear_audit_log().await;
    assert!(clear_result.is_ok(), "Failed to clear audit log: {:?}", clear_result);
    
    assert!(!audit_file.exists(), "Audit file should not exist after clearing");
    
    std::env::set_current_dir(&old_dir).unwrap();
}

#[tokio::test]
async fn test_audit_log_append_only() {
    let _guard = TEST_MUTEX.lock().unwrap();
    let temp_dir = TempDir::new().unwrap();
    let old_dir = std::env::current_dir().unwrap();
    std::env::set_current_dir(temp_dir.path()).unwrap();
    let _keep_alive = &temp_dir;
    
    // Log multiple entries
    for i in 0..5 {
        let result = log_audit_entry(
            &format!("fren rename \"%N{}.%E\" --yes", i),
            Some(format!("%N{}.%E", i)),
            PathBuf::from("."),
            vec![],
            vec![],
            vec![],
        ).await;
        assert!(result.is_ok(), "Failed to log entry {}: {:?}", i, result);
    }
    
    // Verify file exists before reading
    let audit_file = std::env::current_dir().unwrap().join(".fren_audit.log");
    assert!(audit_file.exists(), "Audit file should exist at: {}", audit_file.display());
    
    let entries = read_audit_log().await.unwrap();
    assert_eq!(entries.len(), 5);
    
    // Verify all entries are present
    for (i, entry) in entries.iter().rev().enumerate() {
        assert!(entry.command.contains(&i.to_string()));
    }
    
    std::env::set_current_dir(&old_dir).unwrap();
}

