//! Database access. All SQL lives in this crate.

pub mod audit;
pub mod authority;
pub mod catalog;
pub mod fields;
pub mod seed;
pub mod users;
pub mod vocab;

use sqlx::postgres::{PgPool, PgPoolOptions};

/// Result of a delete that catalogue-object references may block.
#[derive(Debug, PartialEq, Eq)]
pub enum DeleteOutcome {
    /// The row was deleted.
    Deleted,
    /// Refused: `count` catalogue objects still reference it.
    InUse { count: i64 },
    /// The row did not exist.
    NotFound,
}

/// A handle to the organization's PostgreSQL database.
#[derive(Clone)]
pub struct Db {
    pool: PgPool,
}

impl Db {
    /// Connect to the database at `database_url`, opening a connection pool with at most
    /// `max_connections` connections.
    pub async fn connect(database_url: &str, max_connections: u32) -> Result<Self, sqlx::Error> {
        let pool = PgPoolOptions::new()
            .max_connections(max_connections)
            .connect(database_url)
            .await?;

        Ok(Self { pool })
    }

    /// Build a handle from an existing pool (used in tests).
    pub fn from_pool(pool: PgPool) -> Self {
        Self { pool }
    }

    /// Borrow the underlying pool for repository modules.
    pub fn pool(&self) -> &PgPool {
        &self.pool
    }

    /// Readiness check: run a trivial query to confirm the database answers.
    pub async fn ping(&self) -> Result<(), sqlx::Error> {
        sqlx::query_scalar::<_, i32>("SELECT 1")
            .fetch_one(&self.pool)
            .await?;

        Ok(())
    }

    /// Apply all pending schema migrations (embedded at compile time).
    ///
    /// Pre-1.0 the migration files are rewritten freely and dev databases are
    /// recreated; this is the schema-bootstrap mechanism, not forward-migration
    /// discipline.
    pub async fn migrate(&self) -> Result<(), sqlx::migrate::MigrateError> {
        sqlx::migrate!().run(&self.pool).await
    }
}
