//! Audit logging for rename operations.
//! 
//! This module provides immutable audit logging for all rename operations,
//! suitable for server administration and compliance requirements.
//! All operations are async and non-blocking.

use serde::{Serialize, Deserialize};
use tokio::fs;
use tokio::io::AsyncWriteExt;
use std::path::PathBuf;
use chrono::{DateTime, Local};
use crate::fs_ops::RenameExecutionResult;
use crate::history::RenameAction;

/// Represents a single audit log entry.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AuditEntry {
    /// Timestamp when the operation occurred
    pub timestamp: DateTime<Local>,
    /// User who performed the operation (if available)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub user: Option<String>,
    /// Working directory where the operation was performed
    pub working_directory: PathBuf,
    /// Command that was executed
    pub command: String,
    /// Pattern used for transformation
    #[serde(skip_serializing_if = "Option::is_none")]
    pub pattern: Option<String>,
    /// Number of files successfully renamed
    pub successful_count: usize,
    /// Number of files skipped
    pub skipped_count: usize,
    /// Number of files that had errors
    pub error_count: usize,
    /// List of successful renames (old_path -> new_path)
    pub successful: Vec<(PathBuf, PathBuf)>,
    /// List of skipped files with reasons
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub skipped: Vec<(PathBuf, String)>,
    /// List of errors with details
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub errors: Vec<(PathBuf, String)>,
}

const AUDIT_FILE: &str = ".fren_audit.log";

/// Gets the current user name if available.
fn get_current_user() -> Option<String> {
    #[cfg(unix)]
    {
        std::env::var("USER").ok()
            .or_else(|| std::env::var("USERNAME").ok())
    }
    #[cfg(windows)]
    {
        std::env::var("USERNAME").ok()
    }
    #[cfg(not(any(unix, windows)))]
    {
        None
    }
}

/// Logs a rename operation to the audit log asynchronously.
/// 
/// The audit log is append-only and uses JSON Lines format (one JSON object per line).
/// This makes it easy to parse and query while maintaining an immutable audit trail.
/// 
/// # Arguments
/// 
/// * `command` - The full command that was executed
/// * `pattern` - The rename pattern used (if any)
/// * `working_directory` - The directory where the operation was performed
/// * `successful` - List of successful renames (old_path, new_path)
/// * `skipped` - List of skipped files with reasons
/// * `errors` - List of errors with details
/// 
/// # Returns
/// 
/// * `Ok(())` - Audit entry logged successfully
/// * `Err(Box<dyn Error>)` - If file I/O fails
/// 
/// # Examples
/// 
/// ```
/// # tokio_test::block_on(async {
/// use freneng::audit::log_audit_entry;
/// use std::path::PathBuf;
/// 
/// let successful = vec![
///     (PathBuf::from("old.txt"), PathBuf::from("new.txt"))
/// ];
/// log_audit_entry(
///     "fren list *.txt transform \"%N_backup.%E\" rename --yes",
///     Some("%N_backup.%E".to_string()),
///     PathBuf::from("."),
///     successful,
///     vec![],
///     vec![],
/// ).await.unwrap();
/// # })
/// ```
pub async fn log_audit_entry(
    command: &str,
    pattern: Option<String>,
    working_directory: PathBuf,
    successful: Vec<(PathBuf, PathBuf)>,
    skipped: Vec<(PathBuf, String)>,
    errors: Vec<(PathBuf, String)>,
) -> Result<(), Box<dyn std::error::Error>> {
    let entry = AuditEntry {
        timestamp: Local::now(),
        user: get_current_user(),
        working_directory,
        command: command.to_string(),
        pattern,
        successful_count: successful.len(),
        skipped_count: skipped.len(),
        error_count: errors.len(),
        successful,
        skipped,
        errors,
    };

    // Serialize to JSON (compact, one line)
    let json = serde_json::to_string(&entry)?;
    
    // Append to audit log file (JSON Lines format)
    let mut file = fs::OpenOptions::new()
        .create(true)
        .append(true)
        .open(AUDIT_FILE)
        .await?;
    
    // Write JSON line with newline
    file.write_all(json.as_bytes()).await?;
    file.write_all(b"\n").await?;
    file.sync_all().await?;
    
    Ok(())
}

/// Reads audit log entries from the audit log file asynchronously.
/// 
/// # Returns
/// 
/// * `Ok(Vec<AuditEntry>)` - List of audit entries (most recent first)
/// * `Err(Box<dyn Error>)` - If file I/O or parsing fails
/// 
/// # Examples
/// 
/// ```
/// # tokio_test::block_on(async {
/// use freneng::audit::read_audit_log;
/// 
/// let entries = read_audit_log().await.unwrap();
/// println!("Found {} audit entries", entries.len());
/// # })
/// ```
pub async fn read_audit_log() -> Result<Vec<AuditEntry>, Box<dyn std::error::Error>> {
    if fs::metadata(AUDIT_FILE).await.is_err() {
        return Ok(Vec::new());
    }

    let content = fs::read_to_string(AUDIT_FILE).await?;
    let mut entries = Vec::new();
    
    // Parse JSON Lines format (one JSON object per line)
    for line in content.lines() {
        if line.trim().is_empty() {
            continue;
        }
        let entry: AuditEntry = serde_json::from_str(line)?;
        entries.push(entry);
    }
    
    // Return most recent first
    entries.reverse();
    Ok(entries)
}

/// Logs a rename operation to the audit log from a `RenameExecutionResult`.
/// 
/// This is a convenience function that converts a `RenameExecutionResult` into
/// the format needed for audit logging. It's the recommended way to log audit
/// entries after performing rename operations.
/// 
/// # Arguments
/// 
/// * `command` - The full command that was executed
/// * `pattern` - The rename pattern used (if any)
/// * `working_directory` - The directory where the operation was performed
/// * `result` - The result from `perform_renames()`
/// 
/// # Returns
/// 
/// * `Ok(())` - Audit entry logged successfully
/// * `Err(Box<dyn Error>)` - If file I/O fails
/// 
/// # Examples
/// 
/// ```
/// # tokio_test::block_on(async {
/// use freneng::audit::log_audit_from_result;
/// use freneng::perform_renames;
/// use freneng::FileRename;
/// use std::path::PathBuf;
/// 
/// let renames = vec![FileRename {
///     old_path: PathBuf::from("old.txt"),
///     new_path: PathBuf::from("new.txt"),
///     new_name: "new.txt".to_string(),
/// }];
/// 
/// let result = perform_renames(&renames, false).await.unwrap();
/// log_audit_from_result(
///     "fren list *.txt transform \"%N_backup.%E\" rename --yes",
///     Some("%N_backup.%E".to_string()),
///     PathBuf::from("."),
///     &result,
/// ).await.unwrap();
/// # })
/// ```
pub async fn log_audit_from_result(
    command: &str,
    pattern: Option<String>,
    working_directory: PathBuf,
    result: &RenameExecutionResult,
) -> Result<(), Box<dyn std::error::Error>> {
    // Convert RenameAction to (PathBuf, PathBuf) tuples
    let successful: Vec<(PathBuf, PathBuf)> = result.successful.iter()
        .map(|action: &RenameAction| (action.old_path.clone(), action.new_path.clone()))
        .collect();
    
    log_audit_entry(
        command,
        pattern,
        working_directory,
        successful,
        result.skipped.clone(),
        result.errors.clone(),
    ).await
}

/// Clears the audit log by deleting the audit file asynchronously.
/// 
/// **Warning**: This should be used with caution as it removes the audit trail.
/// Consider archiving the log instead of deleting it.
/// 
/// # Returns
/// 
/// * `Ok(())` - Audit log cleared (or didn't exist)
/// * `Err(Box<dyn Error>)` - If file deletion fails
pub async fn clear_audit_log() -> Result<(), Box<dyn std::error::Error>> {
    if fs::metadata(AUDIT_FILE).await.is_ok() {
        fs::remove_file(AUDIT_FILE).await?;
    }
    Ok(())
}

