pub mod models;
pub mod schema;
pub mod util;

use diesel::{
    PgConnection,
    r2d2::{ConnectionManager, Pool, PooledConnection},
};
use diesel_migrations::{EmbeddedMigrations, MigrationHarness, embed_migrations};
use once_cell::sync::OnceCell;
use std::error::Error;
pub use util::*;

pub type PgPool = Pool<ConnectionManager<PgConnection>>;
pub type PgPooledConnection = PooledConnection<ConnectionManager<PgConnection>>;

const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");

pub static DB_POOL: OnceCell<DbConnectionPool> = OnceCell::new();

#[derive(Debug)]
pub struct DbConnectionPool {
    pool: PgPool,
}

impl DbConnectionPool {
    pub fn new(database_url: &str, max_connections: u32) -> Self {
        let manager = ConnectionManager::<PgConnection>::new(database_url);
        let pool = Pool::builder()
            .max_size(max_connections)
            .build(manager)
            .expect("Failed to create connection pool");

        DbConnectionPool { pool }
    }

    pub fn run_migrations(&self) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
        // This will run the necessary migrations.
        //
        // See the documentation for `MigrationHarness` for
        // all available methods.
        self.get_connection()?.run_pending_migrations(MIGRATIONS)?;

        Ok(())
    }

    pub fn get_connection(&self) -> Result<PgPooledConnection, diesel::r2d2::PoolError> {
        self.pool.get()
    }
}
