//! Audit event types and structures
//!
//! Defines the schema for audit logging events with full context tracking.

use std::collections::HashMap;

use chrono::{DateTime, Duration, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

/// Complete audit event structure
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditEvent {
    /// Unique event identifier
    pub event_id: Uuid,

    /// Event timestamp (RFC3339)
    pub timestamp: DateTime<Utc>,

    /// User information
    pub user: UserInfo,

    /// Action details
    pub action: ActionInfo,

    /// Authorization context
    pub authorization: AuthorizationInfo,

    /// KMS access records
    #[serde(default)]
    pub kms_access: Vec<KmsAccess>,

    /// Result information
    pub result: AuditResult,

    /// Additional metadata (extensible)
    #[serde(default)]
    pub metadata: HashMap<String, String>,
}

impl AuditEvent {
    /// Create a new audit event
    pub fn new(user: UserInfo, action: ActionInfo, authorization: AuthorizationInfo) -> Self {
        Self {
            event_id: Uuid::new_v4(),
            timestamp: Utc::now(),
            user,
            action,
            authorization,
            kms_access: Vec::new(),
            result: AuditResult::default(),
            metadata: HashMap::new(),
        }
    }

    /// Mark event as completed successfully
    pub fn complete(mut self, duration_ms: u64) -> Self {
        self.result.status = AuditStatus::Success;
        self.result.duration_ms = duration_ms;
        self
    }

    /// Mark event as failed
    pub fn fail(mut self, duration_ms: u64, error: String) -> Self {
        self.result.status = AuditStatus::Failure;
        self.result.duration_ms = duration_ms;
        self.result.error = Some(error);
        self
    }

    /// Mark event as error
    pub fn error(mut self, duration_ms: u64, error: String) -> Self {
        self.result.status = AuditStatus::Error;
        self.result.duration_ms = duration_ms;
        self.result.error = Some(error);
        self
    }

    /// Add KMS access record
    pub fn add_kms_access(mut self, key: String) -> Self {
        self.kms_access.push(KmsAccess {
            key,
            accessed_at: Utc::now(),
        });
        self
    }

    /// Add metadata entry
    pub fn add_metadata(mut self, key: String, value: String) -> Self {
        self.metadata.insert(key, value);
        self
    }

    /// Check if event contains PII (for GDPR compliance)
    pub fn contains_pii(&self) -> bool {
        // Check for common PII patterns
        self.user.email.is_some()
            || !self.user.name.is_empty()
            || self.action.parameters.to_string().contains("email")
            || self.action.parameters.to_string().contains("phone")
    }

    /// Anonymize PII for GDPR compliance
    pub fn anonymize_pii(mut self) -> Self {
        use sha2::{Digest, Sha256};

        // Hash user ID
        let mut hasher = Sha256::new();
        hasher.update(self.user.id.as_bytes());
        self.user.id = format!("hashed_{:x}", hasher.finalize());

        // Remove email
        self.user.email = None;

        // Hash name
        let mut hasher = Sha256::new();
        hasher.update(self.user.name.as_bytes());
        self.user.name = format!("user_{:x}", hasher.finalize())[..16].to_string();

        self
    }
}

/// User information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserInfo {
    /// User ID (UUID or hashed ID for anonymization)
    pub id: String,

    /// User name (may be hashed for GDPR compliance)
    pub name: String,

    /// IP address
    pub ip: String,

    /// User agent string
    pub user_agent: String,

    /// Email address (optional, removed for GDPR)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub email: Option<String>,

    /// Session ID
    #[serde(skip_serializing_if = "Option::is_none")]
    pub session_id: Option<String>,
}

/// Action information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ActionInfo {
    /// Action type
    #[serde(rename = "type")]
    pub action_type: ActionType,

    /// Resource identifier
    pub resource: String,

    /// Workspace identifier
    pub workspace: String,

    /// Action parameters (sanitized, no secrets)
    pub parameters: serde_json::Value,

    /// Resource tags
    #[serde(default)]
    pub tags: HashMap<String, String>,
}

/// Request parameters for audit logging operations
#[derive(Debug, Clone)]
pub struct AuditLogRequest {
    /// User information
    pub user: UserInfo,
    /// Action type
    pub action_type: ActionType,
    /// Resource identifier
    pub resource: String,
    /// Workspace identifier
    pub workspace: String,
    /// Action parameters
    pub parameters: serde_json::Value,
    /// Authorization context
    pub authorization: AuthorizationInfo,
    /// Operation duration in milliseconds
    pub duration_ms: u64,
}

/// Request parameters for failed audit logging operations
#[derive(Debug, Clone)]
pub struct AuditFailureLogRequest {
    /// User information
    pub user: UserInfo,
    /// Action type
    pub action_type: ActionType,
    /// Resource identifier
    pub resource: String,
    /// Workspace identifier
    pub workspace: String,
    /// Action parameters
    pub parameters: serde_json::Value,
    /// Authorization context
    pub authorization: AuthorizationInfo,
    /// Operation duration in milliseconds
    pub duration_ms: u64,
    /// Error message
    pub error: String,
}

/// Action type enumeration
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ActionType {
    // Server operations
    ServerCreate,
    ServerDelete,
    ServerUpdate,
    ServerSsh,
    ServerList,

    // Task service operations
    TaskservCreate,
    TaskservDelete,
    TaskservUpdate,
    TaskservGenerate,
    TaskservList,

    // Cluster operations
    ClusterCreate,
    ClusterDelete,
    ClusterUpdate,
    ClusterList,

    // Workflow operations
    WorkflowSubmit,
    WorkflowCancel,
    WorkflowRollback,
    WorkflowList,

    // Batch operations
    BatchSubmit,
    BatchCancel,
    BatchList,

    // Configuration operations
    ConfigRead,
    ConfigWrite,
    ConfigDelete,

    // Secret operations (SOPS/Age)
    SecretRead,
    SecretWrite,
    SecretDelete,

    // Dynamic secret operations
    SecretGeneration,
    SecretRevocation,
    SecretRenewal,
    SecretAccess,

    // KMS operations
    KmsKeyCreate,
    KmsKeyRotate,
    KmsEncrypt,
    KmsDecrypt,

    // Authentication
    AuthLogin,
    AuthLogout,
    AuthTokenRefresh,
    AuthMfaVerify,

    // Authorization
    AuthzPolicyEvaluate,
    AuthzPermissionCheck,

    // System operations
    SystemHealthCheck,
    SystemMetrics,
    SystemBackup,
    SystemRestore,

    // Workspace operations
    WorkspaceCreate,
    WorkspaceDelete,
    WorkspaceUpdate,
    WorkspaceSwitch,
    WorkspaceList,
    WorkspaceSync,

    // Unknown/Custom
    Unknown,
}

impl std::fmt::Display for ActionType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{:?}", self)
    }
}

/// Authorization context
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthorizationInfo {
    /// Token ID (for tracking)
    pub token_id: String,

    /// Cedar policy ID that authorized the action
    pub cedar_policy: String,

    /// MFA verification status
    pub mfa_verified: bool,

    /// Permission granted
    pub permissions: Vec<String>,

    /// Policy evaluation duration (microseconds)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub evaluation_duration_us: Option<u64>,
}

/// KMS key access record
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KmsAccess {
    /// KMS key identifier
    pub key: String,

    /// Access timestamp
    pub accessed_at: DateTime<Utc>,
}

/// Audit result information
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AuditResult {
    /// Result status
    pub status: AuditStatus,

    /// Operation duration in milliseconds
    pub duration_ms: u64,

    /// Error message (if failed)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error: Option<String>,

    /// Response size in bytes (if applicable)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub response_size_bytes: Option<usize>,
}

/// Audit status enumeration
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum AuditStatus {
    /// Operation succeeded
    #[default]
    Success,

    /// Operation failed (expected failure)
    Failure,

    /// Operation error (unexpected failure)
    Error,

    /// Operation pending
    Pending,
}

/// SIEM export format
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum SiemFormat {
    /// JSON format
    Json,

    /// JSON Lines (one event per line)
    JsonLines,

    /// CSV format
    Csv,

    /// Splunk Common Information Model (CIM)
    SplunkCim,

    /// Elastic Common Schema (ECS)
    ElasticEcs,
}

/// Retention policy
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RetentionPolicy {
    /// Maximum age of audit events (days)
    pub max_age_days: u64,

    /// Automatically apply policy
    pub auto_apply: bool,

    /// Archive old events before deletion
    pub archive_before_delete: bool,

    /// Archive destination path
    #[serde(skip_serializing_if = "Option::is_none")]
    pub archive_path: Option<String>,
}

impl Default for RetentionPolicy {
    fn default() -> Self {
        Self {
            max_age_days: 365, // 1 year default
            auto_apply: false,
            archive_before_delete: true,
            archive_path: None,
        }
    }
}

impl RetentionPolicy {
    /// Get cutoff date for retention
    pub fn cutoff_date(&self) -> DateTime<Utc> {
        Utc::now() - Duration::days(self.max_age_days as i64)
    }
}

/// Audit query parameters
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AuditQuery {
    /// Filter criteria
    pub filter: AuditFilter,

    /// Maximum number of results
    pub limit: Option<usize>,

    /// Offset for pagination
    pub offset: Option<usize>,

    /// Sort by field
    pub sort_by: Option<String>,

    /// Sort order (asc/desc)
    pub sort_desc: bool,
}

/// Audit filter criteria
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AuditFilter {
    /// Filter by user ID
    #[serde(skip_serializing_if = "Option::is_none")]
    pub user_id: Option<String>,

    /// Filter by action type
    #[serde(skip_serializing_if = "Option::is_none")]
    pub action_type: Option<ActionType>,

    /// Filter by resource
    #[serde(skip_serializing_if = "Option::is_none")]
    pub resource: Option<String>,

    /// Filter by workspace
    #[serde(skip_serializing_if = "Option::is_none")]
    pub workspace: Option<String>,

    /// Filter by status
    #[serde(skip_serializing_if = "Option::is_none")]
    pub status: Option<AuditStatus>,

    /// Filter by date range (start)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub from: Option<DateTime<Utc>>,

    /// Filter by date range (end)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub to: Option<DateTime<Utc>>,

    /// Filter by IP address
    #[serde(skip_serializing_if = "Option::is_none")]
    pub ip: Option<String>,

    /// Filter by metadata key-value pairs
    #[serde(default)]
    pub metadata: HashMap<String, String>,
}

impl AuditFilter {
    /// Check if event matches filter
    pub fn matches(&self, event: &AuditEvent) -> bool {
        if let Some(ref user_id) = self.user_id {
            if &event.user.id != user_id {
                return false;
            }
        }

        if let Some(action_type) = self.action_type {
            if event.action.action_type != action_type {
                return false;
            }
        }

        if let Some(ref resource) = self.resource {
            if &event.action.resource != resource {
                return false;
            }
        }

        if let Some(ref workspace) = self.workspace {
            if &event.action.workspace != workspace {
                return false;
            }
        }

        if let Some(status) = self.status {
            if event.result.status != status {
                return false;
            }
        }

        if let Some(from) = self.from {
            if event.timestamp < from {
                return false;
            }
        }

        if let Some(to) = self.to {
            if event.timestamp > to {
                return false;
            }
        }

        if let Some(ref ip) = self.ip {
            if &event.user.ip != ip {
                return false;
            }
        }

        // Check metadata filters
        for (key, value) in &self.metadata {
            if event.metadata.get(key) != Some(value) {
                return false;
            }
        }

        true
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_audit_event_creation() {
        let user = UserInfo {
            id: "user123".to_string(),
            name: "Test User".to_string(),
            ip: "192.168.1.1".to_string(),
            user_agent: "Mozilla/5.0".to_string(),
            email: Some("test@example.com".to_string()),
            session_id: Some("session123".to_string()),
        };

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

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

        let event = AuditEvent::new(user, action, authz);

        assert!(!event.event_id.to_string().is_empty());
        assert_eq!(event.user.id, "user123");
        assert_eq!(event.action.action_type, ActionType::ServerCreate);
        assert!(event.authorization.mfa_verified);
    }

    #[test]
    fn test_audit_event_anonymization() {
        let user = UserInfo {
            id: "user123".to_string(),
            name: "John Doe".to_string(),
            ip: "192.168.1.1".to_string(),
            user_agent: "Mozilla/5.0".to_string(),
            email: Some("john@example.com".to_string()),
            session_id: None,
        };

        let action = ActionInfo {
            action_type: ActionType::ServerList,
            resource: "*".to_string(),
            workspace: "test".to_string(),
            parameters: serde_json::json!({}),
            tags: HashMap::new(),
        };

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

        let event = AuditEvent::new(user, action, authz);
        assert!(event.contains_pii());

        let anonymized = event.anonymize_pii();
        assert!(anonymized.user.id.starts_with("hashed_"));
        assert_eq!(anonymized.user.email, None);
        assert_ne!(anonymized.user.name, "John Doe");
    }

    #[test]
    fn test_audit_filter_matches() {
        let user = UserInfo {
            id: "user123".to_string(),
            name: "Test User".to_string(),
            ip: "192.168.1.1".to_string(),
            user_agent: "Mozilla/5.0".to_string(),
            email: None,
            session_id: None,
        };

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

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

        let event = AuditEvent::new(user, action, authz);

        // Test matching filter
        let filter = AuditFilter {
            user_id: Some("user123".to_string()),
            action_type: Some(ActionType::ServerCreate),
            ..Default::default()
        };
        assert!(filter.matches(&event));

        // Test non-matching filter
        let filter = AuditFilter {
            user_id: Some("user456".to_string()),
            ..Default::default()
        };
        assert!(!filter.matches(&event));
    }

    #[test]
    fn test_retention_policy_cutoff() {
        let policy = RetentionPolicy {
            max_age_days: 30,
            auto_apply: true,
            archive_before_delete: false,
            archive_path: None,
        };

        let cutoff = policy.cutoff_date();
        let now = Utc::now();
        let expected = now - Duration::days(30);

        // Allow 1 second tolerance for test execution time
        assert!((cutoff - expected).num_seconds().abs() < 2);
    }
}
