use super::redis_key::{namespaced_key, RedisKey};
use redis::aio::MultiplexedConnection;
use redis::{AsyncCommands, Client};
use serde::Serialize;
use std::sync::Arc;
use tokio::sync::Mutex;

/// Redis 客户端（使用 MultiplexedConnection）
#[derive(Clone)]
pub struct RedisClient {
    conn: Arc<Mutex<MultiplexedConnection>>,
}

impl RedisClient {
    /// 创建新的 Redis 客户端
    pub async fn new(url: &str) -> redis::RedisResult<Self> {
        let client = Client::open(url)?;
        let conn = client.get_multiplexed_async_connection().await?;
        Ok(Self {
            conn: Arc::new(Mutex::new(conn)),
        })
    }

    /// 验证当前连接仍可用，供运行健康检查使用。
    pub async fn ping(&self) -> redis::RedisResult<()> {
        let mut connection = self.conn.lock().await;
        redis::cmd("PING")
            .query_async::<String>(&mut *connection)
            .await
            .map(|_| ())
    }

    /// 设置字符串值
    pub async fn set(&self, k: &str, v: &str) -> redis::RedisResult<()> {
        let key = namespaced_key(k);
        let mut c = self.conn.lock().await;
        c.set(key, v).await
    }

    /// 获取字符串值
    pub async fn get(&self, k: &str) -> redis::RedisResult<Option<String>> {
        let key = namespaced_key(k);
        let mut c = self.conn.lock().await;
        c.get(key).await
    }

    /// 设置字符串值并指定过期时间（秒）
    pub async fn set_ex(&self, k: &str, v: &str, seconds: u64) -> redis::RedisResult<()> {
        let key = namespaced_key(k);
        let mut c = self.conn.lock().await;
        c.set_ex(key, v, seconds).await
    }

    /// 删除键
    pub async fn del(&self, k: &str) -> redis::RedisResult<()> {
        let key = namespaced_key(k);
        let mut c = self.conn.lock().await;
        c.del(key).await
    }

    /// 设置键的过期时间（秒）
    pub async fn expire(&self, k: &str, seconds: u64) -> redis::RedisResult<()> {
        let key = namespaced_key(k);
        let mut c = self.conn.lock().await;
        c.expire(key, seconds as i64).await
    }

    /// 使用 RedisKey 设置 JSON 值
    pub async fn set_key<T: Serialize>(&self, key: &RedisKey, value: &T) -> redis::RedisResult<()> {
        let json = serde_json::to_string(value).map_err(|e| {
            redis::RedisError::from((
                redis::ErrorKind::TypeError,
                "JSON serialization failed",
                e.to_string(),
            ))
        })?;
        let mut c = self.conn.lock().await;
        c.set(key.build(), json).await
    }

    /// 使用 RedisKey 设置 JSON 值并指定过期时间（秒）
    pub async fn set_key_ex<T: Serialize>(
        &self,
        key: &RedisKey,
        value: &T,
        expiration_seconds: u64,
    ) -> redis::RedisResult<()> {
        let json = serde_json::to_string(value).map_err(|e| {
            redis::RedisError::from((
                redis::ErrorKind::TypeError,
                "JSON serialization failed",
                e.to_string(),
            ))
        })?;
        let mut c = self.conn.lock().await;
        c.set_ex(key.build(), json, expiration_seconds).await
    }

    /// 使用 RedisKey 获取字符串值
    pub async fn get_key(&self, key: &RedisKey) -> redis::RedisResult<Option<String>> {
        let mut c = self.conn.lock().await;
        let json: Option<String> = c.get(key.build()).await?;
        Ok(json)
    }

    /// 使用 RedisKey 获取并反序列化 JSON 值
    pub async fn get_key_json<T: for<'de> serde::Deserialize<'de>>(
        &self,
        key: &RedisKey,
    ) -> redis::RedisResult<Option<T>> {
        let mut c = self.conn.lock().await;
        let json: Option<String> = c.get(key.build()).await?;
        match json {
            Some(data) => {
                let value = serde_json::from_str(&data).map_err(|e| {
                    redis::RedisError::from((
                        redis::ErrorKind::TypeError,
                        "JSON deserialization failed",
                        e.to_string(),
                    ))
                })?;
                Ok(Some(value))
            }
            None => Ok(None),
        }
    }

    /// 使用 RedisKey 删除键
    pub async fn delete_key(&self, key: &RedisKey) -> redis::RedisResult<()> {
        let mut c = self.conn.lock().await;
        c.del(key.build()).await
    }

    /// 使用 RedisKey 检查键是否存在
    pub async fn exists_key(&self, key: &RedisKey) -> redis::RedisResult<bool> {
        let mut c = self.conn.lock().await;
        c.exists(key.build()).await
    }

    /// 使用 RedisKey 设置键的过期时间（秒）
    pub async fn expire_key(&self, key: &RedisKey, seconds: u64) -> redis::RedisResult<()> {
        let mut c = self.conn.lock().await;
        c.expire(key.build(), seconds as i64).await
    }

    pub async fn queue_push<T: Serialize>(&self, key: &str, value: &T) -> redis::RedisResult<()> {
        let json = serde_json::to_string(value).map_err(|e| {
            redis::RedisError::from((
                redis::ErrorKind::TypeError,
                "JSON serialization failed",
                e.to_string(),
            ))
        })?;
        let key = namespaced_key(key);
        let mut connection = self.conn.lock().await;
        connection.lpush(key, json).await
    }

    pub async fn queue_pop<T: for<'de> serde::Deserialize<'de>>(
        &self,
        key: &str,
    ) -> redis::RedisResult<Option<T>> {
        let key = namespaced_key(key);
        let mut connection = self.conn.lock().await;
        let value: Option<String> = connection.rpop(key, None).await?;
        value
            .map(|json| {
                serde_json::from_str(&json).map_err(|e| {
                    redis::RedisError::from((
                        redis::ErrorKind::TypeError,
                        "JSON deserialization failed",
                        e.to_string(),
                    ))
                })
            })
            .transpose()
    }

    pub async fn queue_len(&self, key: &str) -> redis::RedisResult<usize> {
        let key = namespaced_key(key);
        let mut connection = self.conn.lock().await;
        connection.llen(key).await
    }

    /// 滑动窗口限流（Lua 原子脚本）。
    /// 返回 `true` 表示允许通过，`false` 表示窗口内已达上限。
    ///
    /// - `key`: 限流键
    /// - `max`: 窗口内允许的最大次数
    /// - `window_ms`: 窗口大小（毫秒）
    /// - `ttl_s`: 键过期时间（秒，应 >= window_ms/1000）
    pub async fn sliding_window_allow(
        &self,
        key: &str,
        max: u32,
        window_ms: u64,
        ttl_s: u64,
    ) -> redis::RedisResult<bool> {
        const SCRIPT: &str = r#"
local now = tonumber(ARGV[1])
local win = tonumber(ARGV[2])
local maxc = tonumber(ARGV[3])
redis.call('ZREMRANGEBYSCORE', KEYS[1], 0, now - win)
local c = redis.call('ZCARD', KEYS[1])
if c >= maxc then return 0 end
redis.call('ZADD', KEYS[1], now, ARGV[5])
redis.call('EXPIRE', KEYS[1], ARGV[4])
return 1
"#;
        let now_ms = chrono::Utc::now().timestamp_millis();
        let unique = uuid::Uuid::new_v4().to_string();
        let key = namespaced_key(key);
        let mut c = self.conn.lock().await;
        let res: i64 = redis::cmd("EVAL")
            .arg(SCRIPT)
            .arg(1i64)
            .arg(key)
            .arg(now_ms)
            .arg(window_ms as i64)
            .arg(max as i64)
            .arg(ttl_s as i64)
            .arg(unique)
            .query_async(&mut *c)
            .await?;
        Ok(res == 1)
    }

    /// 向 SET 添加成员（可选 TTL）
    pub async fn sadd(
        &self,
        key: &str,
        member: &str,
        ttl_s: Option<u64>,
    ) -> redis::RedisResult<()> {
        let key = namespaced_key(key);
        let mut c = self.conn.lock().await;
        let _: () = c.sadd(&key, member).await?;
        if let Some(ttl) = ttl_s {
            let _: () = c.expire(key, ttl as i64).await?;
        }
        Ok(())
    }

    /// 判断成员是否在 SET 中
    pub async fn sismember(&self, key: &str, member: &str) -> redis::RedisResult<bool> {
        let key = namespaced_key(key);
        let mut c = self.conn.lock().await;
        let n: i64 = c.sismember(key, member).await?;
        Ok(n == 1)
    }

    /// 自增计数器（首次创建时设置 TTL），用于滥用计数
    pub async fn incr_with_ttl(&self, key: &str, ttl_s: u64) -> redis::RedisResult<i64> {
        let key = namespaced_key(key);
        let mut c = self.conn.lock().await;
        let n: i64 = c.incr(&key, 1i64).await?;
        if n == 1 {
            let _: () = c.expire(key, ttl_s as i64).await?;
        }
        Ok(n)
    }
}
