use crate::GitError;
use crate::hook::sync::HookMetaDataSync;

impl HookMetaDataSync {
    const LOCK_TTL_SECS: u64 = 60;

    pub async fn acquire_lock(&self) -> Result<String, GitError> {
        let lock_key = format!("git:repo:lock:{}", self.repo.id);
        let lock_value = format!("{}:{}", uuid::Uuid::new_v4(), std::process::id());

        let mut conn = self
            .cache
            .conn()
            .await
            .map_err(|e| GitError::IoError(format!("failed to get redis connection: {}", e)))?;

        let result: bool = redis::cmd("SET")
            .arg(&lock_key)
            .arg(&lock_value)
            .arg("NX")
            .arg("EX")
            .arg(Self::LOCK_TTL_SECS)
            .query_async(&mut conn)
            .await
            .map_err(|e| GitError::IoError(format!("failed to acquire lock: {}", e)))?;

        if result {
            Ok(lock_value)
        } else {
            Err(GitError::Locked(format!(
                "repository {} is locked by another process",
                self.repo.id
            )))
        }
    }

    pub async fn release_lock(&self, lock_value: &str) -> Result<(), GitError> {
        let lock_key = format!("git:repo:lock:{}", self.repo.id);

        let mut conn = self
            .cache
            .conn()
            .await
            .map_err(|e| GitError::IoError(format!("failed to get redis connection: {}", e)))?;

        let script = r#"
            if redis.call("get", KEYS[1]) == ARGV[1] then
                return redis.call("del", KEYS[1])
            else
                return 0
            end
        "#;

        let _: i32 = redis::Script::new(script)
            .key(&lock_key)
            .arg(lock_value)
            .invoke_async(&mut conn)
            .await
            .map_err(|e| GitError::IoError(format!("failed to release lock: {}", e)))?;

        Ok(())
    }
}
