use std::collections::HashMap;
pub use bb8_redis::bb8::{Pool, PooledConnection};
pub use bb8_redis::RedisConnectionManager;
use redis::{AsyncCommands, Commands, RedisResult};
use std::env;

pub type BB8Pool = Pool<RedisConnectionManager>;

#[derive(Debug)]
pub struct CacheError {
    pub msg: String,
}

impl CacheError {
    pub fn new_string(s: String) -> CacheError {
        CacheError { msg: s }
    }
}

fn get_redis_host() -> String {
    let name = "CAL_REDIS_HOST";
    match env::var(name) {
        Ok(v) => v,
        Err(e) => panic!("${} is not set ({})", name, e),
    }
}

pub async fn create_pool() -> BB8Pool {
    let host = get_redis_host();
    let manager = RedisConnectionManager::new(host).unwrap();
    Pool::builder().build(manager).await.unwrap()
}

pub async fn get_connection(
    pool: &BB8Pool,
) -> Result<PooledConnection<RedisConnectionManager>, CacheError> {
    let con = pool.get().await.unwrap();
    Ok(con)
}

pub async fn get_str(pool: &BB8Pool, key: &str) -> Result<Option<String>, CacheError> {
    let mut con = get_connection(pool).await?;

    let res: Result<Option<String>, CacheError> = con
        .get(key)
        .await
        .map_err(|e| CacheError::new_string(e.to_string()));

    match res {
        Ok(r) => match r {
            None => Ok(None),
            Some(value) => Ok(Some(value)),
        },
        Err(e) => Err(e),
    }
}

pub async fn get_hash(
    pool: &BB8Pool,
    key: &str,
    field: &str,
) -> Result<Option<String>, CacheError> {
    let mut con = get_connection(pool).await?;
    let _field = format!("\"{}\"", field);
    let res: Result<Option<String>, CacheError> = con
        .hget(key, _field)
        .await
        .map_err(|e| CacheError::new_string(e.to_string()));

    match res {
        Ok(r) => match r {
            None => Ok(None),
            Some(value) => Ok(Some(value)),
        },
        Err(e) => Err(e),
    }
}

pub async fn get_hash_all_values(pool: &BB8Pool, key: &str) -> Result<HashMap<String, String>, CacheError> {
    let mut con = get_connection(pool).await?;
    
    let res:  RedisResult<HashMap<String, String>> = con
        .hvals(key)
        .await;
    
    match res {
        Ok(r) => Ok(r),
        Err(e) => Err(CacheError::new_string(e.to_string())),
    }
}
