//! Audit logger implementation
//!
//! Provides async, non-blocking audit logging with structured events.

use std::sync::Arc;

use anyhow::{Context, Result};
use tokio::sync::RwLock;
use tracing::{debug, error, info, warn};

use super::storage::AuditStorage;
use super::types::{
    ActionInfo, ActionType, AuditEvent, AuditFailureLogRequest, AuditFilter, AuditLogRequest,
    AuditQuery, AuditStatus, AuthorizationInfo, RetentionPolicy, SiemFormat, UserInfo,
};

/// Audit logger configuration
#[derive(Debug, Clone)]
pub struct AuditLoggerConfig {
    /// Enable audit logging
    pub enabled: bool,

    /// Anonymize PII for GDPR compliance
    pub anonymize_pii: bool,

    /// Buffer size for async logging
    pub buffer_size: usize,

    /// Flush interval in seconds
    pub flush_interval_secs: u64,

    /// Retention policy
    pub retention_policy: RetentionPolicy,

    /// Enable debug logging
    pub debug: bool,
}

impl Default for AuditLoggerConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            anonymize_pii: false,
            buffer_size: 1000,
            flush_interval_secs: 5,
            retention_policy: RetentionPolicy::default(),
            debug: false,
        }
    }
}

/// Audit logger
pub struct AuditLogger {
    config: AuditLoggerConfig,
    storage: Arc<dyn AuditStorage>,
    buffer: Arc<RwLock<Vec<AuditEvent>>>,
    stats: Arc<RwLock<AuditStats>>,
}

#[derive(Debug, Default)]
struct AuditStats {
    total_events: u64,
    success_events: u64,
    failure_events: u64,
    error_events: u64,
    buffered_events: u64,
    flushed_events: u64,
}

impl AuditLogger {
    /// Create a new audit logger
    pub async fn new(config: AuditLoggerConfig, storage: Arc<dyn AuditStorage>) -> Result<Self> {
        info!("Initializing audit logger");

        let logger = Self {
            config: config.clone(),
            storage,
            buffer: Arc::new(RwLock::new(Vec::with_capacity(config.buffer_size))),
            stats: Arc::new(RwLock::new(AuditStats::default())),
        };

        // Start background flush task
        if config.enabled {
            logger.start_flush_task();
        }

        info!("Audit logger initialized successfully");
        Ok(logger)
    }

    /// Log an audit event
    pub async fn log(&self, mut event: AuditEvent) -> Result<()> {
        if !self.config.enabled {
            return Ok(());
        }

        // Apply PII anonymization if configured
        if self.config.anonymize_pii && event.contains_pii() {
            event = event.anonymize_pii();
        }

        // Update statistics
        {
            let mut stats = self.stats.write().await;
            stats.total_events += 1;
            match event.result.status {
                AuditStatus::Success => stats.success_events += 1,
                AuditStatus::Failure => stats.failure_events += 1,
                AuditStatus::Error => stats.error_events += 1,
                AuditStatus::Pending => {}
            }
        }

        // Add to buffer
        {
            let mut buffer = self.buffer.write().await;
            buffer.push(event.clone());

            let mut stats = self.stats.write().await;
            stats.buffered_events += 1;

            // Flush if buffer is full
            if buffer.len() >= self.config.buffer_size {
                drop(buffer); // Release lock before flush
                drop(stats);
                self.flush().await?;
            }
        }

        if self.config.debug {
            debug!(
                event_id = %event.event_id,
                user = %event.user.id,
                action = ?event.action.action_type,
                status = ?event.result.status,
                "Audit event logged"
            );
        }

        Ok(())
    }

    /// Log a user action (convenience method)
    pub async fn log_action(
        &self,
        user: UserInfo,
        action_type: ActionType,
        resource: String,
        workspace: String,
        parameters: serde_json::Value,
        authorization: AuthorizationInfo,
    ) -> Result<()> {
        let action = ActionInfo {
            action_type,
            resource,
            workspace,
            parameters,
            tags: Default::default(),
        };

        let event = AuditEvent::new(user, action, authorization);
        self.log(event).await
    }

    /// Log a successful action (convenience method)
    pub async fn log_success(&self, req: AuditLogRequest) -> Result<()> {
        let action = ActionInfo {
            action_type: req.action_type,
            resource: req.resource,
            workspace: req.workspace,
            parameters: req.parameters,
            tags: Default::default(),
        };

        let event = AuditEvent::new(req.user, action, req.authorization).complete(req.duration_ms);
        self.log(event).await
    }

    /// Log a failed action (convenience method)
    pub async fn log_failure(&self, req: AuditFailureLogRequest) -> Result<()> {
        let action = ActionInfo {
            action_type: req.action_type,
            resource: req.resource,
            workspace: req.workspace,
            parameters: req.parameters,
            tags: Default::default(),
        };

        let event =
            AuditEvent::new(req.user, action, req.authorization).fail(req.duration_ms, req.error);
        self.log(event).await
    }

    /// Flush buffered events to storage
    pub async fn flush(&self) -> Result<()> {
        let events = {
            let mut buffer = self.buffer.write().await;
            if buffer.is_empty() {
                return Ok(());
            }
            let events = buffer.drain(..).collect::<Vec<_>>();
            events
        };

        let count = events.len();
        debug!("Flushing {} buffered audit events", count);

        // Write to storage
        self.storage
            .write_batch(&events)
            .await
            .context("Failed to write audit events to storage")?;

        // Update statistics
        {
            let mut stats = self.stats.write().await;
            stats.flushed_events += count as u64;
            stats.buffered_events = stats.buffered_events.saturating_sub(count as u64);
        }

        info!("Flushed {} audit events to storage", count);
        Ok(())
    }

    /// Query audit events
    pub async fn query(&self, query: AuditQuery) -> Result<Vec<AuditEvent>> {
        self.storage
            .query(query)
            .await
            .context("Failed to query audit events")
    }

    /// Export audit events
    pub async fn export(&self, format: SiemFormat, filter: Option<AuditFilter>) -> Result<Vec<u8>> {
        self.storage
            .export(format, filter)
            .await
            .context("Failed to export audit events")
    }

    /// Apply retention policy
    pub async fn apply_retention(&self) -> Result<usize> {
        if !self.config.retention_policy.auto_apply {
            warn!("Retention policy auto-apply is disabled");
            return Ok(0);
        }

        info!(
            "Applying retention policy: {} days",
            self.config.retention_policy.max_age_days
        );

        let deleted = self
            .storage
            .apply_retention(self.config.retention_policy.clone())
            .await
            .context("Failed to apply retention policy")?;

        info!("Retention policy applied: {} events processed", deleted);
        Ok(deleted)
    }

    /// Get audit statistics
    pub async fn stats(&self) -> AuditStatistics {
        let stats = self.stats.read().await;
        AuditStatistics {
            total_events: stats.total_events,
            success_events: stats.success_events,
            failure_events: stats.failure_events,
            error_events: stats.error_events,
            buffered_events: stats.buffered_events,
            flushed_events: stats.flushed_events,
        }
    }

    /// Start background flush task
    fn start_flush_task(&self) {
        let buffer = self.buffer.clone();
        let storage = self.storage.clone();
        let stats = self.stats.clone();
        let interval = self.config.flush_interval_secs;

        tokio::spawn(async move {
            let mut ticker = tokio::time::interval(tokio::time::Duration::from_secs(interval));

            loop {
                ticker.tick().await;

                // Flush buffered events
                let mut buf = buffer.write().await;
                if buf.is_empty() {
                    drop(buf);
                    continue;
                }
                let events = buf.drain(..).collect::<Vec<_>>();
                drop(buf);

                let count = events.len();
                debug!("Background flush: {} events", count);

                match storage.write_batch(&events).await {
                    Ok(_) => {
                        let mut s = stats.write().await;
                        s.flushed_events += count as u64;
                        s.buffered_events = s.buffered_events.saturating_sub(count as u64);
                    }
                    Err(e) => {
                        error!("Background flush failed: {}", e);
                    }
                }
            }
        });
    }
}

/// Audit statistics
#[derive(Debug, Clone, serde::Serialize)]
pub struct AuditStatistics {
    pub total_events: u64,
    pub success_events: u64,
    pub failure_events: u64,
    pub error_events: u64,
    pub buffered_events: u64,
    pub flushed_events: u64,
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;

    use tempfile::TempDir;

    use super::super::storage::FileStorage;
    use super::*;

    async fn create_test_logger() -> (AuditLogger, TempDir) {
        let temp_dir = TempDir::new().unwrap();
        let storage = FileStorage::new(temp_dir.path().to_path_buf(), 1024 * 1024)
            .await
            .unwrap();

        let config = AuditLoggerConfig {
            enabled: true,
            anonymize_pii: false,
            buffer_size: 10,
            flush_interval_secs: 60, // Long interval for tests
            retention_policy: RetentionPolicy::default(),
            debug: false,
        };

        let logger = AuditLogger::new(config, Arc::new(storage)).await.unwrap();

        (logger, temp_dir)
    }

    #[tokio::test]
    async fn test_log_event() {
        let (logger, _temp) = create_test_logger().await;

        let user = UserInfo {
            id: "user123".to_string(),
            name: "Test User".to_string(),
            ip: "127.0.0.1".to_string(),
            user_agent: "test".to_string(),
            email: None,
            session_id: None,
        };

        let action = ActionInfo {
            action_type: ActionType::ServerCreate,
            resource: "test-server".to_string(),
            workspace: "test".to_string(),
            parameters: serde_json::json!({"count": 1}),
            tags: HashMap::new(),
        };

        let authz = AuthorizationInfo {
            token_id: "token123".to_string(),
            cedar_policy: "allow_all".to_string(),
            mfa_verified: false,
            permissions: vec![],
            evaluation_duration_us: None,
        };

        let event = AuditEvent::new(user, action, authz);
        logger.log(event).await.unwrap();

        let stats = logger.stats().await;
        assert_eq!(stats.total_events, 1);
        assert_eq!(stats.buffered_events, 1);
    }

    #[tokio::test]
    async fn test_log_success() {
        let (logger, _temp) = create_test_logger().await;

        let user = UserInfo {
            id: "user123".to_string(),
            name: "Test User".to_string(),
            ip: "127.0.0.1".to_string(),
            user_agent: "test".to_string(),
            email: None,
            session_id: None,
        };

        let authz = AuthorizationInfo {
            token_id: "token123".to_string(),
            cedar_policy: "allow_all".to_string(),
            mfa_verified: true,
            permissions: vec!["server:create".to_string()],
            evaluation_duration_us: Some(500),
        };

        logger
            .log_success(AuditLogRequest {
                user,
                action_type: ActionType::ServerCreate,
                resource: "server-01".to_string(),
                workspace: "production".to_string(),
                parameters: serde_json::json!({"size": "large"}),
                authorization: authz,
                duration_ms: 1500,
            })
            .await
            .unwrap();

        let stats = logger.stats().await;
        assert_eq!(stats.success_events, 1);
    }

    #[tokio::test]
    async fn test_flush() {
        let (logger, _temp) = create_test_logger().await;

        // Log multiple events
        for i in 0..5 {
            let user = UserInfo {
                id: format!("user{}", i),
                name: format!("User {}", i),
                ip: "127.0.0.1".to_string(),
                user_agent: "test".to_string(),
                email: None,
                session_id: None,
            };

            let authz = AuthorizationInfo {
                token_id: format!("token{}", i),
                cedar_policy: "allow_all".to_string(),
                mfa_verified: false,
                permissions: vec![],
                evaluation_duration_us: None,
            };

            logger
                .log_success(AuditLogRequest {
                    user,
                    action_type: ActionType::ServerList,
                    resource: "*".to_string(),
                    workspace: "test".to_string(),
                    parameters: serde_json::json!({}),
                    authorization: authz,
                    duration_ms: 100,
                })
                .await
                .unwrap();
        }

        let stats_before = logger.stats().await;
        assert_eq!(stats_before.buffered_events, 5);

        // Flush
        logger.flush().await.unwrap();

        let stats_after = logger.stats().await;
        assert_eq!(stats_after.buffered_events, 0);
        assert_eq!(stats_after.flushed_events, 5);
    }

    #[tokio::test]
    async fn test_pii_anonymization() {
        let temp_dir = TempDir::new().unwrap();
        let storage = FileStorage::new(temp_dir.path().to_path_buf(), 1024 * 1024)
            .await
            .unwrap();

        let config = AuditLoggerConfig {
            enabled: true,
            anonymize_pii: true, // Enable anonymization
            buffer_size: 10,
            flush_interval_secs: 60,
            retention_policy: RetentionPolicy::default(),
            debug: false,
        };

        let logger = AuditLogger::new(config, Arc::new(storage)).await.unwrap();

        let user = UserInfo {
            id: "user123".to_string(),
            name: "John Doe".to_string(),
            ip: "192.168.1.1".to_string(),
            user_agent: "Mozilla".to_string(),
            email: Some("john@example.com".to_string()),
            session_id: None,
        };

        let authz = AuthorizationInfo {
            token_id: "token123".to_string(),
            cedar_policy: "allow".to_string(),
            mfa_verified: true,
            permissions: vec![],
            evaluation_duration_us: None,
        };

        logger
            .log_success(AuditLogRequest {
                user,
                action_type: ActionType::ConfigRead,
                resource: "config.toml".to_string(),
                workspace: "test".to_string(),
                parameters: serde_json::json!({}),
                authorization: authz,
                duration_ms: 50,
            })
            .await
            .unwrap();

        logger.flush().await.unwrap();

        // Query and verify anonymization
        let query = AuditQuery::default();
        let events = logger.query(query).await.unwrap();

        assert_eq!(events.len(), 1);
        assert!(events[0].user.id.starts_with("hashed_"));
        assert_eq!(events[0].user.email, None);
    }
}
