use sqlx::types::chrono::{self, NaiveDateTime};
use sqlx::{FromRow, MySqlPool};

#[derive(Debug, Clone, FromRow)]
pub struct Mutexes {
    pub id: String,
    pub tenant: String,
    pub collection: String,
    pub action: String,
    pub created_at: NaiveDateTime,
    pub expires_at: Option<NaiveDateTime>,
}

impl Mutexes {
    pub fn new(
        tenant: String,
        collection: String,
        action: String,
        duration: std::time::Duration,
    ) -> Self {
        let now: NaiveDateTime = chrono::Utc::now().naive_utc();
        Self {
            id: uuid::Uuid::new_v4().to_string(),
            tenant,
            collection,
            action,
            created_at: now,
            expires_at: Some(now + duration),
        }
    }

    pub async fn save(&self, pool: &MySqlPool) -> sqlx::Result<String> {
        let query = r#"
            INSERT INTO `mutexes` (`id`, `tenant`, `collection`, `action`, `created_at`, `expires_at`)
            VALUES (?, ?, ?, ?, ?, ?)
            "#;
        sqlx::query(query)
            .bind(&self.id)
            .bind(&self.tenant)
            .bind(&self.collection)
            .bind(&self.action)
            .bind(self.created_at)
            .bind(self.expires_at)
            .execute(pool)
            .await?;
        Ok(self.id.clone())
    }

    pub async fn delete(pool: &MySqlPool, id: &str) -> sqlx::Result<()> {
        let query = r#"
            DELETE FROM `mutexes` WHERE `id` = ?
            "#;
        sqlx::query(query).bind(id).execute(pool).await?;
        Ok(())
    }

    pub async fn query(
        pool: &MySqlPool,
        tenant: &str,
        collection: &str,
        action: &str,
    ) -> sqlx::Result<Option<Self>> {
        let query = r#"
            SELECT `id`, `tenant`, `collection`, `action`, `created_at`, `expires_at` FROM `mutexes` WHERE `tenant` = ? AND `collection` = ? AND `action` = ?
            "#;
        let mutex = sqlx::query_as(query)
            .bind(tenant)
            .bind(collection)
            .bind(action)
            .fetch_optional(pool)
            .await?;
        Ok(mutex)
    }
}
