#![allow(dead_code)]
#![allow(unused_imports)]
use anyhow::Result;
use futures::stream::TryStreamExt;
use mongodb::{
    bson::{doc, oid::ObjectId},
    Collection,
};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AuditEventType {
    #[serde(rename = "login_success")]
    LoginSuccess,
    #[serde(rename = "login_failed")]
    LoginFailed,
    #[serde(rename = "logout")]
    Logout,
    #[serde(rename = "password_recovery")]
    PasswordRecovery,
    #[serde(rename = "password_changed")]
    PasswordChanged,
    #[serde(rename = "account_created")]
    AccountCreated,
    #[serde(rename = "account_deleted")]
    AccountDeleted,
    #[serde(rename = "data_accessed")]
    DataAccessed,
    #[serde(rename = "data_modified")]
    DataModified,
    #[serde(rename = "data_shared")]
    DataShared,
    #[serde(rename = "session_created")]
    SessionCreated,
    #[serde(rename = "session_revoked")]
    SessionRevoked,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditLog {
    #[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
    pub id: Option<ObjectId>,
    pub event_type: AuditEventType,
    pub user_id: Option<ObjectId>,
    pub email: Option<String>,
    pub ip_address: String,
    pub resource_type: Option<String>,
    pub resource_id: Option<String>,
    pub timestamp: mongodb::bson::DateTime,
}

#[derive(Clone)]
pub struct AuditLogRepository {
    collection: Collection<AuditLog>,
}

impl AuditLogRepository {
    pub fn new(db: &mongodb::Database) -> Self {
        let collection = db.collection("audit_logs");
        Self { collection }
    }

    pub async fn log(
        &self,
        event_type: AuditEventType,
        user_id: Option<ObjectId>,
        email: Option<String>,
        ip_address: String,
        resource_type: Option<String>,
        resource_id: Option<String>,
    ) -> Result<ObjectId> {
        let audit_log = AuditLog {
            id: None,
            event_type,
            user_id,
            email,
            ip_address,
            resource_type,
            resource_id,
            timestamp: mongodb::bson::DateTime::now(),
        };

        self.collection
            .insert_one(audit_log, None)
            .await?
            .inserted_id
            .as_object_id()
            .ok_or_else(|| anyhow::anyhow!("Failed to get inserted id"))
    }

    pub async fn find_by_user(&self, user_id: &ObjectId) -> Result<Vec<AuditLog>> {
        let cursor = self
            .collection
            .find(
                doc! {
                    "user_id": user_id
                },
                None,
            )
            .await?;

        let logs: Vec<AuditLog> = cursor.try_collect().await?;
        Ok(logs)
    }

    pub async fn find_recent(&self, limit: u64) -> Result<Vec<AuditLog>> {
        use mongodb::options::FindOptions;

        let opts = FindOptions::builder()
            .sort(doc! { "timestamp": -1 })
            .limit(limit as i64)
            .build();

        let cursor = self.collection.find(doc! {}, opts).await?;

        let logs: Vec<AuditLog> = cursor.try_collect().await?;
        Ok(logs)
    }
}
