use std::path::Path;
use std::sync::Arc;

use sqlx::postgres::{PgPool, PgPoolOptions};
use tokio::sync::RwLock;

const VAULT_SECRETS_PATH: &str = "/vault/secrets/.env";
const MAX_CONNECTIONS: u32 = 10;

/// Database connection pool that supports credential refresh
#[derive(Clone)]
pub struct Database {
    pool: Arc<RwLock<PgPool>>,
}

impl Database {
    /// Create a new database connection using DATABASE_URL from environment
    pub async fn connect() -> Result<Self, sqlx::Error> {
        let database_url = std::env::var("DATABASE_URL")
            .expect("DATABASE_URL must be set");

        let pool = PgPoolOptions::new()
            .max_connections(MAX_CONNECTIONS)
            .connect(&database_url)
            .await?;

        tracing::info!("Database connection established");

        Ok(Self {
            pool: Arc::new(RwLock::new(pool)),
        })
    }

    /// Get a reference to the current connection pool
    pub async fn pool(&self) -> tokio::sync::RwLockReadGuard<'_, PgPool> {
        self.pool.read().await
    }

    /// Refresh the database connection with new credentials
    /// Called when SIGHUP is received (Vault Agent rotated credentials)
    pub async fn refresh(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        tracing::info!("Refreshing database credentials...");

        // Read the new DATABASE_URL from the Vault-rendered env file
        let new_url = Self::read_database_url_from_file()?;

        // Create a new connection pool with the new credentials
        let new_pool = PgPoolOptions::new()
            .max_connections(MAX_CONNECTIONS)
            .connect(&new_url)
            .await?;

        // Test the new connection
        sqlx::query("SELECT 1")
            .execute(&new_pool)
            .await?;

        // Swap the pools
        let mut pool = self.pool.write().await;
        let old_pool = std::mem::replace(&mut *pool, new_pool);

        // Close old connections gracefully
        old_pool.close().await;

        tracing::info!("Database credentials refreshed successfully");
        Ok(())
    }

    /// Read DATABASE_URL from the Vault-rendered .env file
    fn read_database_url_from_file() -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
        let path = Path::new(VAULT_SECRETS_PATH);

        if !path.exists() {
            return Err("Vault secrets file not found".into());
        }

        let content = std::fs::read_to_string(path)?;

        for line in content.lines() {
            let line = line.trim();
            if line.starts_with("DATABASE_URL=") {
                return Ok(line.trim_start_matches("DATABASE_URL=").to_string());
            }
        }

        Err("DATABASE_URL not found in secrets file".into())
    }
}
