use diesel::{r2d2::ConnectionManager as DieselConnectionManager, PgConnection};
// use redis::{aio::{Connection as RedisConnection, ConnectionManager as RedisConnectionManager}, AsyncCommands};
use redis::aio::Connection as RedisConnection;
use siren::ServiceError;
use crate::diesel_migrations::MigrationHarness;
use lazy_static::lazy_static;
use log::{error, info};
use r2d2;
use std::env;

pub mod backgrounds;
pub mod bestiary;
pub mod classes;
pub mod conditions;
pub mod feats;
pub mod guilds;
pub mod items;
pub mod messages;
pub mod options;
pub mod races;
pub mod spells;
pub mod schema;

type DbPool = r2d2::Pool<DieselConnectionManager<PgConnection>>;
pub type DbConnection = r2d2::PooledConnection<DieselConnectionManager<PgConnection>>;
// type RedisPool = r2d2::Pool<redis::ConnectionManager>;

pub const MIGRATIONS: diesel_migrations::EmbeddedMigrations = embed_migrations!();

lazy_static! {
  static ref POOL: DbPool = {
    let username = env::var("DATABASE_USER").expect("DATABASE_USERNAME is not set");
    let password = env::var("DATABASE_PASSWORD").expect("DATABASE_PASSWORD is not set");
    let host = env::var("DATABASE_HOST").unwrap_or("localhost".to_string());
    let name = env::var("DATABASE_NAME").expect("DATABASE_NAME is not set");
    let port = env::var("DATABASE_PORT").unwrap_or("5432".to_string());
    let url = format!("postgres://{}:{}@{}:{}/{}", username, password, host, port, name);
    let manager = DieselConnectionManager::<PgConnection>::new(url);
    DbPool::builder().test_on_check_out(true).build(manager).expect("Failed to create db pool")
  };
  // static ref REDIS_POOL: RedisPool = {
  //   let host = env::var("REDIS_HOST").unwrap_or("localhost".to_string());
  //   let port = env::var("REDIS_PORT").unwrap_or("6379".to_string());
  //   let url = format!("redis://{}:{}", host, port);
  //   let client = redis::Client::open(url).expect("Failed to create redis client");
  //   let manager = RedisConnectionManager::new(client);
  //   "".to_string()
  // };
}

pub fn init() {
  lazy_static::initialize(&POOL);
  let mut pool: DbConnection = connection().expect("Failed to get db connection");
  match pool.run_pending_migrations(MIGRATIONS) {
    Ok(_) => info!("Database initialized"),
    Err(err) => error!("Failed to initialize database; {}", err)
  };
}

pub fn connection() -> Result<DbConnection, ServiceError> {
  POOL.get()
    .map_err(|e| ServiceError::new(500, format!("Failed getting db connection: {}", e)))
}

pub fn redis_client() -> Result<redis::Client, ServiceError> {
  let host = env::var("REDIS_HOST").unwrap_or("localhost".to_string());
  let port = env::var("REDIS_PORT").unwrap_or("6379".to_string());
  let url = format!("redis://{}:{}", host, port);
  let client = redis::Client::open(url)?;
  Ok(client)
}

pub fn redis_connection() -> Result<redis::Connection, ServiceError> {
  let client = redis_client()?;
  let conn = client.get_connection()?;
  Ok(conn)
}

pub async fn redis_async_connection() -> Result<RedisConnection, ServiceError> {
  let client = redis_client()?;
  let conn = client.get_async_connection().await?;
  Ok(conn)
}

pub fn load_data(data_dir_path: &str) {
  spells::load_data(data_dir_path);
}
