//! Database initialization and connection management
//!
//! This module handles database connection setup, including support for both
//! PostgreSQL and SQLite databases with automatic type detection.

#[cfg(any(feature = "auth", feature = "content-db", feature = "post-engagement"))]
use crate::config::Config;
#[cfg(any(feature = "auth", feature = "content-db", feature = "post-engagement"))]
use crate::database::{Database, DatabaseConfig, DatabasePool};

/// Initialize database connection (if database features are enabled)
#[cfg(any(feature = "auth", feature = "content-db", feature = "post-engagement"))]
pub async fn initialize_database(
    config: &Config,
) -> Result<(Database, sqlx::PgPool, DatabasePool), Box<dyn std::error::Error>> {
    let database_config = DatabaseConfig {
        url: config.database.url.clone(),
        max_connections: config.database.max_connections,
        min_connections: config.database.min_connections,
        connect_timeout: std::time::Duration::from_secs(config.database.connect_timeout),
        idle_timeout: std::time::Duration::from_secs(config.database.idle_timeout),
        max_lifetime: std::time::Duration::from_secs(config.database.max_lifetime),
        create_if_missing: config.database.create_if_missing,
    };

    tracing::info!("Connecting to database: {}", database_config.url);

    let database_pool =
        DatabasePool::new(&database_config)
            .await
            .map_err(|e| -> Box<dyn std::error::Error> {
                tracing::error!("Failed to connect to database: {}", e);
                std::io::Error::new(std::io::ErrorKind::Other, e.to_string()).into()
            })?;

    let db_type = database_pool.database_type();
    tracing::info!("Database type detected: {:?}", db_type);

    let database = Database::new(database_pool.clone());

    // Auth services use the database-agnostic DatabasePool directly.
    // PgPool is only needed when actually connecting to PostgreSQL; for SQLite
    // we return a None-sentinel via a disconnected pool stub rather than exiting.
    #[cfg(feature = "auth")]
    let pool = match &database_pool {
        DatabasePool::PostgreSQL(pg_pool) => pg_pool.clone(),
        DatabasePool::SQLite(_) => {
            tracing::info!("SQLite database: auth services use database-agnostic pool");
            // Return a disconnected stub — callers that receive this pool and try to
            // use it directly will fail fast with a clear error rather than at exit.
            sqlx::postgres::PgPoolOptions::new()
                .max_connections(1)
                .connect_lazy("postgres://127.0.0.1/rustelo_stub")
                .unwrap_or_else(|_| {
                    // connect_lazy rarely fails; if it does return a pool that errors on use
                    sqlx::postgres::PgPoolOptions::new()
                        .max_connections(1)
                        .connect_lazy("postgres://127.0.0.1/stub")
                        .expect("failed to build lazy stub pool")
                })
        }
    };

    #[cfg(not(feature = "auth"))]
    let pool = match &database_pool {
        DatabasePool::PostgreSQL(pg_pool) => pg_pool.clone(),
        DatabasePool::SQLite(_) => sqlx::postgres::PgPoolOptions::new()
            .max_connections(1)
            .connect_lazy("postgres://127.0.0.1/rustelo_stub")
            .unwrap_or_else(|_| {
                sqlx::postgres::PgPoolOptions::new()
                    .max_connections(1)
                    .connect_lazy("postgres://127.0.0.1/stub")
                    .expect("failed to build lazy stub pool")
            }),
    };

    Ok((database, pool, database_pool))
}

#[cfg(feature = "content-db")]
pub fn initialize_content_services(
    database_pool: &DatabasePool,
) -> std::sync::Arc<crate::content::ContentService> {
    use std::sync::Arc;

    use crate::content::{ContentRepository, ContentService, ContentSource};

    let content_repository = Arc::new(ContentRepository::from_pool(database_pool));
    Arc::new(
        ContentService::new(content_repository)
            .with_source(ContentSource::Database)
            .with_cache(true),
    )
}
