use anyhow::Result;
use sqlx::PgPool;
use std::sync::atomic::{AtomicU32, Ordering};

/// Schema context for database operations
/// Ensures all queries use the correct schema prefix
#[derive(Debug, Clone)]
pub struct SchemaContext {
    pub prefix: String,
}

static SCHEMA_INSTANCE: std::sync::OnceLock<SchemaContext> = std::sync::OnceLock::new();
static SCHEMA_VERSION: AtomicU32 = AtomicU32::new(0);

impl SchemaContext {
    /// Initialize schema context from environment
    pub fn init() -> Self {
        let schema = std::env::var("DATABASE_SCHEMA").unwrap_or_else(|_| "dev".to_string());
        let prefix = if schema == "public" {
            String::new()
        } else {
            format!("{}.", schema)
        };
        Self { prefix }
    }

    /// Get the global schema context
    pub fn global() -> &'static Self {
        SCHEMA_INSTANCE.get_or_init(|| Self::init())
    }

    /// Get table name with schema prefix
    pub fn table(&self, name: &str) -> String {
        format!("{}{}", self.prefix, name)
    }

    /// Reload schema context (for testing)
    pub fn reload() {
        SCHEMA_VERSION.fetch_add(1, Ordering::SeqCst);
        // Note: OnceLock can't be reset, so we use a different approach
        // In production, schema doesn't change at runtime
    }
}

/// Quick helper to get table name with current schema prefix
pub fn t(name: &str) -> String {
    SchemaContext::global().table(name)
}

/// Check if a table exists in the current schema
pub async fn table_exists(pool: &PgPool, table_name: &str) -> Result<bool> {
    let schema = SchemaContext::global();
    let schema_name = if schema.prefix.is_empty() {
        "public".to_string()
    } else {
        schema.prefix.trim_end_matches('.').to_string()
    };

    let query = format!(
        "SELECT EXISTS(SELECT 1 FROM information_schema.tables WHERE table_schema = $1 AND table_name = $2)"
    );
    let exists: bool = sqlx::query_scalar(&query)
        .bind(&schema_name)
        .bind(table_name)
        .fetch_one(pool)
        .await?;
    Ok(exists)
}
