use std::time::Duration;

use prost::Message;
use proto::PlayerStatusRedisData;
use redis::{aio::MultiplexedConnection, AsyncCommands, Client, RedisError};
use tokio::sync::Mutex;

pub struct RedisMgr(Mutex<MultiplexedConnection>);

impl RedisMgr {
    pub async fn new(redis_conn_str: &str) -> Result<Self, RedisError> {
        let client = Client::open(redis_conn_str)?;
        let connection = client
            .get_multiplexed_tokio_connection_with_response_timeouts(
                Duration::from_secs(1),
                Duration::from_secs(1),
            )
            .await?;

        Ok(Self(Mutex::new(connection)))
    }

    pub async fn get_player_status(&self, uid: u32) -> Option<PlayerStatusRedisData> {
        match self
            .0
            .lock()
            .await
            .get::<String, Option<String>>(format!("player:status:{uid}"))
            .await
        {
            Ok(Some(proto_str)) => {
                let data = rbase64::decode(&proto_str).ok()?;
                Some(PlayerStatusRedisData::decode(&*data).ok()?)
            }
            Ok(None) => Some(PlayerStatusRedisData::default()),
            Err(_) => None,
        }
    }

    pub async fn compare_and_modify_player_status(
        &self,
        uid: u32,
        _last_login_rand: u64,
        new_data: PlayerStatusRedisData,
    ) {
        let data = new_data.encode_to_vec();

        let _ = self
            .0
            .lock()
            .await
            .set::<String, Option<String>, Option<String>>(
                format!("player:status:{uid}"),
                Some(rbase64::encode(&data)),
            )
            .await;
    }
}
