use sqlx::{Executor, Pool, Postgres};
use crate::auth::dal::model::AuthSession;
use crate::auth::token::checks::UserRole;
use crate::background_tasks::dal::model::QueuedTask;
use crate::scheduled_tasks::dal::model::ScheduledTask;

/// Runs all framework migrations against the provided connection pool.
///
/// This executes the SQL generated by `AuthSession::generate_migration_sql()`,
/// which creates the `saps` schema, the `saps.auth_sessions` table, indexes,
/// and the `saps.ping` function if they don't already exist.
pub async fn run_migrations(pool: &Pool<Postgres>) {
    // We need a concrete type for the generic, but generate_migration_sql is the
    // same regardless of the type parameter — it's a static string.
    // Use a dummy type to call it.
    let sql = AuthSession::<DummyRole>::generate_migration_sql();
    pool.execute(sql).await.expect("failed to run saps migrations");
    
    let sql = QueuedTask::generate_migration_sql();
    pool.execute(sql).await.expect("failed to run saps migrations");

    let sql = ScheduledTask::generate_migration_sql();
    pool.execute(sql).await.expect("failed to run saps migrations");
}

/// Internal dummy role type used only to satisfy the generic bound on `AuthSession`
/// when calling `generate_migration_sql()` (which doesn't depend on the type parameter).
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
struct DummyRole;

impl std::fmt::Display for DummyRole {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "dummy")
    }
}

impl TryFrom<String> for DummyRole {
    type Error = crate::errors::saps::SapsError;
    fn try_from(_value: String) -> Result<Self, Self::Error> {
        Ok(DummyRole)
    }
}

impl UserRole for DummyRole {}
