//! Database configuration for multi-backend support
//!
//! Provides configuration structures for different database backends.
//! Configuration is typically loaded from TOML files and can be overridden
//! by environment variables.
//!
//! # Examples
//!
//! ```ignore
//! let config = DatabaseConfig::load("configs/database.toml")?;
//! let db = config.create_database().await?;
//! ```

use serde::{Deserialize, Serialize};
use std::path::Path;

/// Main database configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DatabaseConfig {
    /// Backend engine: "sqlite" or "surrealdb"
    pub engine: String,

    /// SQLite configuration (required if engine is "sqlite")
    pub sqlite: Option<SqliteConfig>,

    /// SurrealDB configuration (required if engine is "surrealdb")
    pub surrealdb: Option<SurrealDbConfig>,

    /// Optional PostgreSQL configuration for future support
    pub postgresql: Option<PostgresConfig>,
}

/// SQLite-specific configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SqliteConfig {
    /// Path to SQLite database file
    pub path: String,

    /// Maximum number of connections in pool (default: 5)
    #[serde(default = "default_sqlite_pool_size")]
    pub max_connections: u32,

    /// Connection timeout in seconds (default: 30)
    #[serde(default = "default_timeout_secs")]
    pub timeout_secs: u64,

    /// Enable write-ahead logging (default: true)
    #[serde(default = "default_true")]
    pub wal_mode: bool,

    /// PRAGMA synchronous setting: OFF, NORMAL, FULL (default: NORMAL)
    #[serde(default = "default_pragma_synchronous")]
    pub pragma_synchronous: String,

    /// PRAGMA cache_size in pages (default: 2000)
    #[serde(default = "default_cache_size")]
    pub pragma_cache_size: i32,
}

/// SurrealDB-specific configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SurrealDbConfig {
    /// SurrealDB server URL (e.g., "ws://localhost:8000")
    pub url: String,

    /// Namespace for isolation (default: "syntaxis")
    #[serde(default = "default_namespace")]
    pub namespace: String,

    /// Database name (default: "projects")
    #[serde(default = "default_database")]
    pub database: String,

    /// Username for authentication
    pub username: String,

    /// Password for authentication
    pub password: String,

    /// Maximum connections (default: 10)
    #[serde(default = "default_surrealdb_pool_size")]
    pub max_connections: u32,

    /// Connection timeout in seconds (default: 30)
    #[serde(default = "default_timeout_secs")]
    pub timeout_secs: u64,

    /// Enable TLS (default: false)
    #[serde(default)]
    pub tls_enabled: bool,

    /// TLS certificate path (optional)
    pub tls_cert_path: Option<String>,
}

/// PostgreSQL configuration (for future support)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PostgresConfig {
    /// Connection string
    pub url: String,

    /// Maximum connections (default: 10)
    #[serde(default = "default_postgres_pool_size")]
    pub max_connections: u32,

    /// Connection timeout in seconds (default: 30)
    #[serde(default = "default_timeout_secs")]
    pub timeout_secs: u64,
}

// ============================================================================
// DEFAULT IMPLEMENTATIONS
// ============================================================================

fn default_sqlite_pool_size() -> u32 {
    5
}

fn default_surrealdb_pool_size() -> u32 {
    10
}

fn default_postgres_pool_size() -> u32 {
    10
}

fn default_timeout_secs() -> u64 {
    30
}

fn default_true() -> bool {
    true
}

fn default_pragma_synchronous() -> String {
    "NORMAL".to_string()
}

fn default_cache_size() -> i32 {
    2000
}

fn default_namespace() -> String {
    "syntaxis".to_string()
}

fn default_database() -> String {
    "projects".to_string()
}

// ============================================================================
// IMPLEMENTATIONS
// ============================================================================

impl DatabaseConfig {
    /// Create a default SQLite configuration
    pub fn sqlite_default(path: &str) -> Self {
        Self {
            engine: "sqlite".to_string(),
            sqlite: Some(SqliteConfig {
                path: path.to_string(),
                max_connections: default_sqlite_pool_size(),
                timeout_secs: default_timeout_secs(),
                wal_mode: true,
                pragma_synchronous: "NORMAL".to_string(),
                pragma_cache_size: 2000,
            }),
            surrealdb: None,
            postgresql: None,
        }
    }

    /// Create a default SurrealDB configuration
    pub fn surrealdb_default(url: &str, username: &str, password: &str) -> Self {
        Self {
            engine: "surrealdb".to_string(),
            sqlite: None,
            surrealdb: Some(SurrealDbConfig {
                url: url.to_string(),
                namespace: default_namespace(),
                database: default_database(),
                username: username.to_string(),
                password: password.to_string(),
                max_connections: default_surrealdb_pool_size(),
                timeout_secs: default_timeout_secs(),
                tls_enabled: false,
                tls_cert_path: None,
            }),
            postgresql: None,
        }
    }

    /// Load configuration from TOML file
    pub fn load_from_file<P: AsRef<Path>>(path: P) -> crate::error::Result<Self> {
        let contents = std::fs::read_to_string(path)?;
        Self::load_from_str(&contents)
    }

    /// Load configuration from TOML string
    pub fn load_from_str(toml: &str) -> crate::error::Result<Self> {
        toml::from_str(toml).map_err(|e| {
            crate::error::LifecycleError::Config(format!("Failed to parse config: {}", e))
        })
    }

    /// Validate configuration
    pub fn validate(&self) -> crate::error::Result<()> {
        match self.engine.as_str() {
            "sqlite" => {
                self.sqlite.as_ref().ok_or_else(|| {
                    crate::error::LifecycleError::Config(
                        "SQLite engine selected but no sqlite config provided".to_string(),
                    )
                })?;
                Ok(())
            }
            "surrealdb" => {
                self.surrealdb.as_ref().ok_or_else(|| {
                    crate::error::LifecycleError::Config(
                        "SurrealDB engine selected but no surrealdb config provided".to_string(),
                    )
                })?;
                Ok(())
            }
            "postgresql" => {
                self.postgresql.as_ref().ok_or_else(|| {
                    crate::error::LifecycleError::Config(
                        "PostgreSQL engine selected but no postgresql config provided".to_string(),
                    )
                })?;
                Ok(())
            }
            other => Err(crate::error::LifecycleError::Config(format!(
                "Unknown database engine: {}",
                other
            ))),
        }
    }

    /// Get the engine type
    pub fn get_engine(&self) -> &str {
        &self.engine
    }

    /// Check if SQLite backend is configured
    pub fn is_sqlite(&self) -> bool {
        self.engine == "sqlite"
    }

    /// Check if SurrealDB backend is configured
    pub fn is_surrealdb(&self) -> bool {
        self.engine == "surrealdb"
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_sqlite_default_config() {
        let config = DatabaseConfig::sqlite_default("test.db");
        assert!(config.is_sqlite());
        assert!(config.sqlite.is_some());
        assert_eq!(config.sqlite.unwrap().path, "test.db");
    }

    #[test]
    fn test_surrealdb_default_config() {
        let config = DatabaseConfig::surrealdb_default("ws://localhost:8000", "user", "pass");
        assert!(config.is_surrealdb());
        assert!(config.surrealdb.is_some());

        let sdb = config.surrealdb.unwrap();
        assert_eq!(sdb.url, "ws://localhost:8000");
        assert_eq!(sdb.username, "user");
        assert_eq!(sdb.password, "pass");
    }

    #[test]
    fn test_validate_sqlite() {
        let config = DatabaseConfig::sqlite_default("test.db");
        assert!(config.validate().is_ok());
    }

    #[test]
    fn test_validate_invalid_engine() {
        let config = DatabaseConfig {
            engine: "mongodb".to_string(),
            sqlite: None,
            surrealdb: None,
            postgresql: None,
        };
        assert!(config.validate().is_err());
    }

    #[test]
    fn test_load_from_str() {
        let toml = r#"
engine = "sqlite"

[sqlite]
path = "test.db"
max_connections = 10
"#;
        let config = DatabaseConfig::load_from_str(toml).unwrap();
        assert_eq!(config.engine, "sqlite");
        assert_eq!(config.sqlite.unwrap().max_connections, 10);
    }

    #[test]
    fn test_sqlite_config_defaults() {
        let config = SqliteConfig {
            path: "test.db".to_string(),
            max_connections: default_sqlite_pool_size(),
            timeout_secs: default_timeout_secs(),
            wal_mode: true,
            pragma_synchronous: "NORMAL".to_string(),
            pragma_cache_size: 2000,
        };

        assert_eq!(config.max_connections, 5);
        assert_eq!(config.timeout_secs, 30);
        assert!(config.wal_mode);
    }

    #[test]
    fn test_surrealdb_config_defaults() {
        let config = SurrealDbConfig {
            url: "ws://localhost:8000".to_string(),
            namespace: default_namespace(),
            database: default_database(),
            username: "root".to_string(),
            password: "root".to_string(),
            max_connections: default_surrealdb_pool_size(),
            timeout_secs: default_timeout_secs(),
            tls_enabled: false,
            tls_cert_path: None,
        };

        assert_eq!(config.namespace, "syntaxis");
        assert_eq!(config.database, "projects");
        assert_eq!(config.max_connections, 10);
    }
}
