use crate::cache_redis::{RedisCacheError, RedisCacheResult, RedisLuaScript};

use super::RedisDistributedLock;

impl RedisDistributedLock {
    pub(crate) async fn invoke_release_script<C>(
        &self,
        connection: &mut C,
        keys: &[String],
        args: &[String],
    ) -> RedisCacheResult<i64>
    where
        C: redis::aio::ConnectionLike + Send,
    {
        match self.eval_release_script(connection, keys, args).await {
            Ok(value) => Ok(value),
            Err(error) if RedisLuaScript::is_noscript_error(&error) => {
                self.load_release_script(connection).await?;
                self.eval_release_script(connection, keys, args).await
            }
            Err(error) => Err(error),
        }
    }

    async fn load_release_script<C>(&self, connection: &mut C) -> RedisCacheResult<String>
    where
        C: redis::aio::ConnectionLike + Send,
    {
        tokio::time::timeout(self.config.command_timeout, async {
            redis::cmd("SCRIPT")
                .arg("LOAD")
                .arg(self.release_script.text().as_bytes())
                .query_async::<String>(connection)
                .await
        })
        .await
        .map_err(|_| RedisCacheError::Timeout("SCRIPT LOAD".to_string()))?
        .map_err(crate::cache_redis::redis_error)
    }

    async fn eval_release_script<C>(
        &self,
        connection: &mut C,
        keys: &[String],
        args: &[String],
    ) -> RedisCacheResult<i64>
    where
        C: redis::aio::ConnectionLike + Send,
    {
        let mut cmd = redis::cmd("EVALSHA");
        cmd.arg(self.release_script.sha1()).arg(keys.len());
        for key in keys {
            cmd.arg(key);
        }
        for arg in args {
            cmd.arg(arg);
        }
        tokio::time::timeout(self.config.command_timeout, async {
            cmd.query_async::<i64>(connection).await
        })
        .await
        .map_err(|_| RedisCacheError::Timeout("EVALSHA".to_string()))?
        .map_err(crate::cache_redis::redis_error)
    }
}
