// Copyright (c) 2026 Kirky.X
//
// Licensed under the MIT License
// See LICENSE file in the project root for full license information.

//! 配置类型定义
//!
//! 纯数据结构，配置加载通过 serde 直接反序列化

use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::time::Duration;
use thiserror::Error;

// ============================================================================
// 错误类型
// ============================================================================

/// 配置错误
#[derive(Debug, Error)]
pub enum ConfigError {
    /// 缺少必填字段
    #[error("Missing required configuration: {0}")]
    MissingField(String),

    /// 缺少 URL
    #[error("Missing required configuration: dbnexus.url")]
    MissingUrl,

    /// 无效缓存容量
    #[error("Invalid cache capacity: {0}")]
    InvalidCacheCapacity(String),

    /// 无效值
    #[error("Invalid configuration value for '{key}': {message}")]
    InvalidValue {
        /// 配置键名
        key: String,
        /// 错误消息
        message: String,
    },

    /// 无效格式
    #[error("Invalid configuration format: {0}")]
    InvalidFormat(String),

    /// 文件未找到
    #[error("Configuration file not found: {0}")]
    FileNotFound(String),

    /// IO 错误
    #[error("IO error: {0}")]
    IoError(String),

    /// 无效 URL
    #[error("Invalid URL: {0}")]
    InvalidUrl(String),

    /// 不支持的协议
    #[error("Unsupported database protocol: {0}")]
    UnsupportedProtocol(String),

    /// 解析错误
    #[error("Parse error: {0}")]
    ParseError(String),

    /// 验证错误
    #[error("Validation error: {0}")]
    ValidationError(String),
}

// ============================================================================
// 缓存配置
// ============================================================================

/// 缓存配置
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CacheConfig {
    /// 权限策略缓存容量
    #[serde(default = "default_policy_cache_capacity")]
    pub policy_cache_capacity: u64,

    /// SQL 解析缓存容量
    #[serde(default = "default_sql_parse_cache_capacity")]
    pub sql_parse_cache_capacity: u64,

    /// 查询结果缓存容量
    #[serde(default = "default_query_cache_capacity")]
    pub query_cache_capacity: u64,

    /// 默认 TTL（秒）
    #[serde(default = "default_cache_ttl")]
    pub default_ttl: u64,
}

fn default_policy_cache_capacity() -> u64 {
    4096
}
fn default_sql_parse_cache_capacity() -> u64 {
    1000
}
fn default_query_cache_capacity() -> u64 {
    10000
}
fn default_cache_ttl() -> u64 {
    300
}

impl Default for CacheConfig {
    fn default() -> Self {
        Self {
            policy_cache_capacity: default_policy_cache_capacity(),
            sql_parse_cache_capacity: default_sql_parse_cache_capacity(),
            query_cache_capacity: default_query_cache_capacity(),
            default_ttl: default_cache_ttl(),
        }
    }
}

impl CacheConfig {
    /// 获取默认 TTL Duration
    pub fn default_ttl_duration(&self) -> Duration {
        Duration::from_secs(self.default_ttl)
    }

    /// 从 YAML 字符串加载配置
    ///
    /// 使用 `serde_yaml_ng` 直接反序列化，缺失字段使用 serde 默认值。
    ///
    /// # Errors
    ///
    /// 如果 YAML 格式无效或字段类型不匹配，返回解析错误
    #[cfg(feature = "yaml")]
    pub fn from_yaml_str(yaml: &str) -> Result<Self, serde_yaml_ng::Error> {
        serde_yaml_ng::from_str(yaml)
    }

    /// 从 `serde_json::Value` 加载配置
    ///
    /// # Errors
    ///
    /// 如果 JSON 结构无法反序列化为 `CacheConfig`，返回解析错误
    pub fn from_json_value(v: serde_json::Value) -> Result<Self, serde_json::Error> {
        serde_json::from_value(v)
    }
}

// ============================================================================
// 连接池配置
// ============================================================================

/// 数据库连接池配置
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PoolConfig {
    /// 最大连接数
    #[serde(default = "default_max_connections")]
    pub max_connections: u32,

    /// 最小连接数
    #[serde(default = "default_min_connections")]
    pub min_connections: u32,

    /// 连接空闲超时时间（秒）
    #[serde(default = "default_idle_timeout")]
    pub idle_timeout: u64,

    /// 连接获取超时时间（毫秒）
    #[serde(default = "default_acquire_timeout")]
    pub acquire_timeout: u64,
}

fn default_max_connections() -> u32 {
    20
}
fn default_min_connections() -> u32 {
    5
}
fn default_idle_timeout() -> u64 {
    300
}
fn default_acquire_timeout() -> u64 {
    5000
}

impl Default for PoolConfig {
    fn default() -> Self {
        Self {
            max_connections: default_max_connections(),
            min_connections: default_min_connections(),
            idle_timeout: default_idle_timeout(),
            acquire_timeout: default_acquire_timeout(),
        }
    }
}

impl PoolConfig {
    /// 获取空闲超时 Duration
    pub fn idle_timeout_duration(&self) -> Duration {
        Duration::from_secs(self.idle_timeout)
    }

    /// 获取获取超时 Duration
    pub fn acquire_timeout_duration(&self) -> Duration {
        Duration::from_millis(self.acquire_timeout)
    }
}

// ============================================================================
// 数据库类型
// ============================================================================

/// 数据库类型枚举
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum DatabaseType {
    /// PostgreSQL
    Postgres,
    /// MySQL
    MySql,
    /// SQLite
    #[default]
    Sqlite,
    /// DuckDB（嵌入式分析型数据库，0.3.0 新增）
    DuckDb,
}

impl DatabaseType {
    /// 从 URL 解析数据库类型
    ///
    /// 使用 `url` crate 解析连接串，支持 `sqlite`/`sqlite3`/`postgres`/`postgresql`/`mysql`/`duckdb` scheme。
    /// 未知 scheme 返回 `Err(DbNexusError::UnsupportedDatabaseScheme)`。
    ///
    /// # Errors
    ///
    /// - URL 解析失败（无 scheme）
    /// - 未知数据库协议
    pub fn from_url(url: &str) -> Result<Self, crate::common::error::DbNexusError> {
        // 处理 SQLite 特殊格式 sqlite::memory: / sqlite3::memory:
        let lower = url.to_lowercase();
        if lower == "sqlite::memory:" || lower.starts_with("sqlite://") || lower.starts_with("sqlite3://") {
            return Ok(DatabaseType::Sqlite);
        }
        if lower.starts_with("duckdb:") {
            return Ok(DatabaseType::DuckDb);
        }

        let parsed = url::Url::parse(url).map_err(|_| {
            crate::common::error::DbNexusError::UnsupportedDatabaseScheme(format!("failed to parse URL: {url}"))
        })?;

        match parsed.scheme() {
            "sqlite" | "sqlite3" => Ok(DatabaseType::Sqlite),
            "postgres" | "postgresql" => Ok(DatabaseType::Postgres),
            "mysql" => Ok(DatabaseType::MySql),
            "duckdb" => Ok(DatabaseType::DuckDb),
            other => Err(crate::common::error::DbNexusError::UnsupportedDatabaseScheme(format!(
                "'{other}' is not a supported database scheme"
            ))),
        }
    }

    /// 从 URL 解析数据库类型（别名）
    ///
    /// # Errors
    ///
    /// 同 [`from_url`](Self::from_url)
    pub fn parse_database_type(url: &str) -> Result<Self, crate::common::error::DbNexusError> {
        Self::from_url(url)
    }

    /// 获取数据库类型的显示名称
    pub fn as_str(&self) -> &'static str {
        match self {
            DatabaseType::Postgres => "postgres",
            DatabaseType::MySql => "mysql",
            DatabaseType::Sqlite => "sqlite",
            DatabaseType::DuckDb => "duckdb",
        }
    }

    /// 检查是否为嵌入式数据库（SQLite / DuckDB）
    ///
    /// 0.3.0 新增：取代 `is_real_database()` 的二分法，更清晰地表达数据库部署模式。
    /// 嵌入式数据库运行在进程内，无需独立服务器；服务器端数据库（Postgres/MySQL）需要独立进程。
    pub fn is_embedded(&self) -> bool {
        matches!(self, DatabaseType::Sqlite | DatabaseType::DuckDb)
    }

    /// 检查是否为服务器端数据库（PostgreSQL / MySQL）
    ///
    /// 0.3.0 新增：与 `is_embedded()` 互补。
    pub fn is_server_side(&self) -> bool {
        matches!(self, DatabaseType::Postgres | DatabaseType::MySql)
    }

    /// 检查是否为真实数据库（非 SQLite 内存数据库）
    ///
    /// # Deprecated
    ///
    /// 此方法语义模糊（"真实"数据库 vs "内存"数据库 vs "嵌入式"数据库），
    /// 0.3.0 推荐使用 [`is_embedded()`](Self::is_embedded) 或 [`is_server_side()`](Self::is_server_side) 替代。
    pub fn is_real_database(&self) -> bool {
        matches!(self, DatabaseType::Postgres | DatabaseType::MySql)
    }
}

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

// ============================================================================
// 数据库配置
// ============================================================================

/// 数据库配置
///
/// 纯数据结构，通过 `from_yaml_str()` / `from_json_str()` 直接反序列化加载
///
/// # 配置字段
///
/// | 字段 | 默认值 |
/// |------|--------|
/// | `url` | **必填** |
/// | `max_connections` | 20 |
/// | `min_connections` | 5 |
/// | `idle_timeout` | 300 |
/// | `acquire_timeout` | 5000 |
/// | `admin_role` | "admin" |
/// | `permissions_path` | None |
/// | `migrations_dir` | None |
/// | `auto_migrate` | false |
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DbConfig {
    /// 数据库连接 URL
    pub url: String,

    /// 最大连接数
    #[serde(default = "default_max_connections")]
    pub max_connections: u32,

    /// 最小连接数
    #[serde(default = "default_min_connections")]
    pub min_connections: u32,

    /// 空闲连接超时（秒）
    #[serde(default = "default_idle_timeout")]
    pub idle_timeout: u64,

    /// 连接获取超时（毫秒）
    #[serde(default = "default_acquire_timeout")]
    pub acquire_timeout: u64,

    /// 权限配置文件路径
    #[serde(default)]
    pub permissions_path: Option<String>,

    /// 迁移文件目录
    #[serde(default)]
    pub migrations_dir: Option<PathBuf>,

    /// 是否启用自动迁移
    #[serde(default)]
    pub auto_migrate: bool,

    /// 迁移超时时间（秒）
    #[serde(default = "default_migration_timeout")]
    pub migration_timeout: u64,

    /// 管理员角色名称（用于 DDL 操作）
    #[serde(default = "default_admin_role")]
    pub admin_role: String,

    /// 预热超时时间（秒）
    #[serde(default = "default_warmup_timeout")]
    pub warmup_timeout: u64,

    /// 预热重试次数
    #[serde(default = "default_warmup_retries")]
    pub warmup_retries: u32,

    /// 缓存配置
    #[serde(default)]
    pub cache_config: CacheConfig,
}

impl Default for DbConfig {
    fn default() -> Self {
        Self {
            url: String::new(),
            max_connections: default_max_connections(),
            min_connections: default_min_connections(),
            idle_timeout: default_idle_timeout(),
            acquire_timeout: default_acquire_timeout(),
            permissions_path: None,
            migrations_dir: None,
            auto_migrate: false,
            migration_timeout: default_migration_timeout(),
            admin_role: default_admin_role(),
            warmup_timeout: default_warmup_timeout(),
            warmup_retries: default_warmup_retries(),
            cache_config: CacheConfig::default(),
        }
    }
}

fn default_admin_role() -> String {
    "admin".to_string()
}
fn default_migration_timeout() -> u64 {
    60
}
fn default_warmup_timeout() -> u64 {
    30
}
fn default_warmup_retries() -> u32 {
    3
}

impl DbConfig {
    /// 从环境变量加载配置
    ///
    /// 支持的环境变量：
    /// - `DATABASE_URL`: 数据库连接 URL（必需）
    /// - `DB_MAX_CONNECTIONS`: 最大连接数（默认 20）
    /// - `DB_MIN_CONNECTIONS`: 最小连接数（默认 5）
    /// - `DB_IDLE_TIMEOUT`: 空闲超时秒数（默认 300）
    /// - `DB_ACQUIRE_TIMEOUT`: 获取连接超时毫秒数（默认 5000）
    /// - `DB_ADMIN_ROLE`: 管理员角色名（默认 "admin"）
    /// - `DB_PERMISSIONS_PATH`: 权限配置文件路径
    /// - `DB_MIGRATIONS_DIR`: 迁移文件目录
    /// - `DB_AUTO_MIGRATE`: 是否启用自动迁移（默认 false）
    /// - `DB_MIGRATION_TIMEOUT`: 迁移超时秒数（默认 60）
    #[cfg(feature = "config-env")]
    pub fn from_env() -> Result<Self, ConfigError> {
        let url = std::env::var("DATABASE_URL").map_err(|_| ConfigError::MissingUrl)?;

        Ok(Self {
            url,
            max_connections: std::env::var("DB_MAX_CONNECTIONS")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(20),
            min_connections: std::env::var("DB_MIN_CONNECTIONS")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(5),
            idle_timeout: std::env::var("DB_IDLE_TIMEOUT")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(300),
            acquire_timeout: std::env::var("DB_ACQUIRE_TIMEOUT")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(5000),
            admin_role: std::env::var("DB_ADMIN_ROLE").unwrap_or_else(|_| "admin".to_string()),
            permissions_path: std::env::var("DB_PERMISSIONS_PATH").ok(),
            migrations_dir: std::env::var("DB_MIGRATIONS_DIR").ok().map(PathBuf::from),
            auto_migrate: std::env::var("DB_AUTO_MIGRATE")
                .ok()
                .map(|s| s.to_lowercase() == "true")
                .unwrap_or(false),
            migration_timeout: std::env::var("DB_MIGRATION_TIMEOUT")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(60),
            warmup_timeout: std::env::var("DB_WARMUP_TIMEOUT")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(30),
            warmup_retries: std::env::var("DB_WARMUP_RETRIES")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(3),
            cache_config: CacheConfig::default(),
        })
    }

    /// 从 YAML 字符串加载配置
    ///
    /// 使用 `serde_yaml_ng` 直接反序列化，缺失字段使用 serde 默认值。
    /// `cache_config` 子结构通过 `#[serde(default)]` 嵌入，serde 会自动反序列化
    /// `cache_config:` 子节点（缺失时回退到 `CacheConfig::default()`）。
    ///
    /// # Example
    ///
    /// ```rust,ignore
    /// use dbnexus::config::DbConfig;
    ///
    /// let yaml = r#"
    /// url: "sqlite::memory:"
    /// max_connections: 20
    /// "#;
    /// let config = DbConfig::from_yaml_str(yaml)?;
    /// ```
    ///
    /// # Errors
    ///
    /// 如果 YAML 格式无效或字段类型不匹配，返回解析错误
    #[cfg(feature = "yaml")]
    pub fn from_yaml_str(yaml: &str) -> Result<Self, serde_yaml_ng::Error> {
        serde_yaml_ng::from_str(yaml)
    }

    /// 从 JSON 字符串加载配置
    ///
    /// # Example
    ///
    /// ```rust,ignore
    /// use dbnexus::config::DbConfig;
    ///
    /// let json = r#"{"url":"sqlite::memory:","max_connections":20}"#;
    /// let config = DbConfig::from_json_str(json)?;
    /// ```
    ///
    /// # Errors
    ///
    /// 如果 JSON 格式无效或字段类型不匹配，返回解析错误
    pub fn from_json_str(json: &str) -> Result<Self, serde_json::Error> {
        serde_json::from_str(json)
    }

    /// 获取数据库类型
    ///
    /// # Errors
    ///
    /// URL 解析失败或未知协议时返回 `DbNexusError::UnsupportedDatabaseScheme`
    pub fn database_type(&self) -> Result<DatabaseType, crate::common::error::DbNexusError> {
        DatabaseType::from_url(&self.url)
    }

    /// 获取空闲超时 Duration
    pub fn idle_timeout_duration(&self) -> Duration {
        Duration::from_secs(self.idle_timeout)
    }

    /// 获取获取超时 Duration
    pub fn acquire_timeout_duration(&self) -> Duration {
        Duration::from_millis(self.acquire_timeout)
    }

    /// 获取迁移超时 Duration
    pub fn migration_timeout_duration(&self) -> Duration {
        Duration::from_secs(self.migration_timeout)
    }

    /// 获取缓存配置
    pub fn cache_config(&self) -> &CacheConfig {
        &self.cache_config
    }
}

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

    // ===== ConfigError Display 测试 =====

    #[test]
    fn test_config_error_display_variants() {
        assert_eq!(
            ConfigError::MissingField("url".into()).to_string(),
            "Missing required configuration: url"
        );
        assert_eq!(
            ConfigError::MissingUrl.to_string(),
            "Missing required configuration: dbnexus.url"
        );
        assert_eq!(
            ConfigError::InvalidCacheCapacity("negative".into()).to_string(),
            "Invalid cache capacity: negative"
        );
        assert_eq!(
            ConfigError::InvalidValue {
                key: "max".into(),
                message: "too large".into(),
            }
            .to_string(),
            "Invalid configuration value for 'max': too large"
        );
        assert_eq!(
            ConfigError::InvalidFormat("yaml".into()).to_string(),
            "Invalid configuration format: yaml"
        );
        assert_eq!(
            ConfigError::FileNotFound("/tmp/cfg".into()).to_string(),
            "Configuration file not found: /tmp/cfg"
        );
        assert_eq!(
            ConfigError::IoError("read fail".into()).to_string(),
            "IO error: read fail"
        );
        assert_eq!(ConfigError::InvalidUrl("bad".into()).to_string(), "Invalid URL: bad");
        assert_eq!(
            ConfigError::UnsupportedProtocol("ftp".into()).to_string(),
            "Unsupported database protocol: ftp"
        );
        assert_eq!(
            ConfigError::ParseError("syntax".into()).to_string(),
            "Parse error: syntax"
        );
        assert_eq!(
            ConfigError::ValidationError("bad value".into()).to_string(),
            "Validation error: bad value"
        );
    }

    // ===== CacheConfig 测试 =====

    #[test]
    fn test_cache_config_default() {
        let cfg = CacheConfig::default();
        assert_eq!(cfg.policy_cache_capacity, 4096);
        assert_eq!(cfg.sql_parse_cache_capacity, 1000);
        assert_eq!(cfg.query_cache_capacity, 10000);
        assert_eq!(cfg.default_ttl, 300);
    }

    #[test]
    fn test_cache_config_default_ttl_duration() {
        let cfg = CacheConfig::default();
        assert_eq!(cfg.default_ttl_duration(), Duration::from_secs(300));
    }

    #[test]
    fn test_cache_config_serde_roundtrip() {
        let cfg = CacheConfig {
            policy_cache_capacity: 100,
            sql_parse_cache_capacity: 200,
            query_cache_capacity: 300,
            default_ttl: 60,
        };
        let json = serde_json::to_string(&cfg).unwrap();
        let deserialized: CacheConfig = serde_json::from_str(&json).unwrap();
        assert_eq!(deserialized.policy_cache_capacity, 100);
        assert_eq!(deserialized.sql_parse_cache_capacity, 200);
        assert_eq!(deserialized.query_cache_capacity, 300);
        assert_eq!(deserialized.default_ttl, 60);
    }

    #[test]
    fn test_cache_config_serde_defaults_applied() {
        // 空 JSON 应使用 serde default 函数
        let json = r#"{}"#;
        let cfg: CacheConfig = serde_json::from_str(json).unwrap();
        assert_eq!(cfg.policy_cache_capacity, 4096);
        assert_eq!(cfg.sql_parse_cache_capacity, 1000);
        assert_eq!(cfg.query_cache_capacity, 10000);
        assert_eq!(cfg.default_ttl, 300);
    }

    // ===== PoolConfig 测试 =====

    #[test]
    fn test_pool_config_default() {
        let cfg = PoolConfig::default();
        assert_eq!(cfg.max_connections, 20);
        assert_eq!(cfg.min_connections, 5);
        assert_eq!(cfg.idle_timeout, 300);
        assert_eq!(cfg.acquire_timeout, 5000);
    }

    #[test]
    fn test_pool_config_duration_methods() {
        let cfg = PoolConfig::default();
        assert_eq!(cfg.idle_timeout_duration(), Duration::from_secs(300));
        assert_eq!(cfg.acquire_timeout_duration(), Duration::from_millis(5000));
    }

    #[test]
    fn test_pool_config_serde_defaults_applied() {
        let json = r#"{}"#;
        let cfg: PoolConfig = serde_json::from_str(json).unwrap();
        assert_eq!(cfg.max_connections, 20);
        assert_eq!(cfg.min_connections, 5);
        assert_eq!(cfg.idle_timeout, 300);
        assert_eq!(cfg.acquire_timeout, 5000);
    }

    // ===== DatabaseType 测试 =====

    #[test]
    fn test_database_type_from_url_postgres() {
        assert_eq!(
            DatabaseType::from_url("postgres://localhost/db").unwrap(),
            DatabaseType::Postgres
        );
        assert_eq!(
            DatabaseType::from_url("postgresql://localhost/db").unwrap(),
            DatabaseType::Postgres
        );
        // 大小写不敏感（url crate 自动处理 scheme 小写化）
        assert_eq!(
            DatabaseType::from_url("POSTGRES://localhost/db").unwrap(),
            DatabaseType::Postgres
        );
    }

    #[test]
    fn test_database_type_from_url_mysql() {
        assert_eq!(
            DatabaseType::from_url("mysql://localhost/db").unwrap(),
            DatabaseType::MySql
        );
        assert_eq!(
            DatabaseType::from_url("MYSQL://localhost/db").unwrap(),
            DatabaseType::MySql
        );
    }

    #[test]
    fn test_database_type_from_url_sqlite() {
        assert_eq!(DatabaseType::from_url("sqlite::memory:").unwrap(), DatabaseType::Sqlite);
        assert_eq!(
            DatabaseType::from_url("sqlite://test.db").unwrap(),
            DatabaseType::Sqlite
        );
    }

    #[test]
    fn test_database_type_from_url_duckdb() {
        assert_eq!(DatabaseType::from_url("duckdb::memory:").unwrap(), DatabaseType::DuckDb);
        assert_eq!(
            DatabaseType::from_url("duckdb://test.db").unwrap(),
            DatabaseType::DuckDb
        );
        assert_eq!(DatabaseType::from_url("duckdb:test.ddb").unwrap(), DatabaseType::DuckDb);
    }

    #[test]
    fn test_database_type_from_url_unknown_scheme_returns_error() {
        // 未知 scheme 现在返回错误（不再默认 SQLite）
        assert!(DatabaseType::from_url("unknown://foo").is_err());
        // 空字符串无法解析为 URL，返回错误
        assert!(DatabaseType::from_url("").is_err());
        // 无 scheme 的相对路径返回错误
        assert!(DatabaseType::from_url("/path/to/db.db").is_err());
    }

    #[test]
    fn test_database_type_parse_database_type_alias() {
        assert_eq!(
            DatabaseType::parse_database_type("mysql://x").unwrap(),
            DatabaseType::MySql
        );
    }

    #[test]
    fn test_database_type_as_str() {
        assert_eq!(DatabaseType::Postgres.as_str(), "postgres");
        assert_eq!(DatabaseType::MySql.as_str(), "mysql");
        assert_eq!(DatabaseType::Sqlite.as_str(), "sqlite");
        assert_eq!(DatabaseType::DuckDb.as_str(), "duckdb");
    }

    #[test]
    fn test_database_type_is_embedded() {
        assert!(DatabaseType::Sqlite.is_embedded());
        assert!(DatabaseType::DuckDb.is_embedded());
        assert!(!DatabaseType::Postgres.is_embedded());
        assert!(!DatabaseType::MySql.is_embedded());
    }

    #[test]
    fn test_database_type_is_server_side() {
        assert!(DatabaseType::Postgres.is_server_side());
        assert!(DatabaseType::MySql.is_server_side());
        assert!(!DatabaseType::Sqlite.is_server_side());
        assert!(!DatabaseType::DuckDb.is_server_side());
    }

    #[test]
    fn test_database_type_is_real_database() {
        assert!(DatabaseType::Postgres.is_real_database());
        assert!(DatabaseType::MySql.is_real_database());
        assert!(!DatabaseType::Sqlite.is_real_database());
        assert!(!DatabaseType::DuckDb.is_real_database());
    }

    #[test]
    fn test_database_type_display() {
        assert_eq!(DatabaseType::Postgres.to_string(), "postgres");
        assert_eq!(DatabaseType::MySql.to_string(), "mysql");
        assert_eq!(DatabaseType::Sqlite.to_string(), "sqlite");
        assert_eq!(DatabaseType::DuckDb.to_string(), "duckdb");
    }

    #[test]
    fn test_database_type_default_is_sqlite() {
        let db_type = DatabaseType::default();
        assert!(matches!(db_type, DatabaseType::Sqlite));
    }

    #[test]
    fn test_database_type_serde_round_trip() {
        let cases = [
            DatabaseType::Sqlite,
            DatabaseType::Postgres,
            DatabaseType::MySql,
            DatabaseType::DuckDb,
        ];
        for original in cases {
            let json = serde_json::to_string(&original).expect("serialize should succeed");
            let restored: DatabaseType = serde_json::from_str(&json).expect("deserialize should succeed");
            assert_eq!(original, restored, "round-trip failed for {:?}", original);
        }
    }

    // ===== DbConfig 测试 =====

    #[test]
    fn test_db_config_default() {
        let cfg = DbConfig::default();
        assert_eq!(cfg.url, String::new());
        assert_eq!(cfg.max_connections, 20);
        assert_eq!(cfg.min_connections, 5);
        assert_eq!(cfg.idle_timeout, 300);
        assert_eq!(cfg.acquire_timeout, 5000);
        assert_eq!(cfg.admin_role, "admin");
        assert_eq!(cfg.migration_timeout, 60);
        assert_eq!(cfg.warmup_timeout, 30);
        assert_eq!(cfg.warmup_retries, 3);
        assert!(!cfg.auto_migrate);
        assert!(cfg.permissions_path.is_none());
        assert!(cfg.migrations_dir.is_none());
        assert_eq!(cfg.cache_config.policy_cache_capacity, 4096);
    }

    #[test]
    fn test_db_config_database_type() {
        let cfg = DbConfig {
            url: "postgres://localhost/db".into(),
            ..Default::default()
        };
        assert_eq!(cfg.database_type().unwrap(), DatabaseType::Postgres);

        let cfg = DbConfig {
            url: "mysql://localhost/db".into(),
            ..Default::default()
        };
        assert_eq!(cfg.database_type().unwrap(), DatabaseType::MySql);

        let cfg = DbConfig {
            url: "sqlite::memory:".into(),
            ..Default::default()
        };
        assert_eq!(cfg.database_type().unwrap(), DatabaseType::Sqlite);
    }

    #[test]
    fn test_db_config_duration_methods() {
        let cfg = DbConfig::default();
        assert_eq!(cfg.idle_timeout_duration(), Duration::from_secs(300));
        assert_eq!(cfg.acquire_timeout_duration(), Duration::from_millis(5000));
        assert_eq!(cfg.migration_timeout_duration(), Duration::from_secs(60));
    }

    #[test]
    fn test_db_config_cache_config_ref() {
        let cfg = DbConfig::default();
        let cache = cfg.cache_config();
        assert_eq!(cache.default_ttl, 300);
    }

    #[test]
    fn test_db_config_serde_roundtrip() {
        let cfg = DbConfig {
            url: "sqlite::memory:".to_string(),
            max_connections: 10,
            min_connections: 2,
            idle_timeout: 100,
            acquire_timeout: 3000,
            permissions_path: Some("/tmp/perms.yaml".into()),
            migrations_dir: Some(PathBuf::from("/tmp/migrations")),
            auto_migrate: true,
            migration_timeout: 120,
            admin_role: "root".to_string(),
            warmup_timeout: 15,
            warmup_retries: 5,
            cache_config: CacheConfig {
                policy_cache_capacity: 512,
                sql_parse_cache_capacity: 256,
                query_cache_capacity: 1024,
                default_ttl: 60,
            },
        };
        let json = serde_json::to_string(&cfg).unwrap();
        let deserialized: DbConfig = serde_json::from_str(&json).unwrap();
        assert_eq!(deserialized.url, "sqlite::memory:");
        assert_eq!(deserialized.max_connections, 10);
        assert_eq!(deserialized.min_connections, 2);
        assert_eq!(deserialized.idle_timeout, 100);
        assert_eq!(deserialized.acquire_timeout, 3000);
        assert_eq!(deserialized.permissions_path, Some("/tmp/perms.yaml".to_string()));
        assert_eq!(deserialized.migrations_dir, Some(PathBuf::from("/tmp/migrations")));
        assert!(deserialized.auto_migrate);
        assert_eq!(deserialized.migration_timeout, 120);
        assert_eq!(deserialized.admin_role, "root");
        assert_eq!(deserialized.warmup_timeout, 15);
        assert_eq!(deserialized.warmup_retries, 5);
        assert_eq!(deserialized.cache_config.policy_cache_capacity, 512);
    }

    #[test]
    fn test_db_config_serde_partial_uses_defaults() {
        // 只提供 url，其他字段应使用 serde default
        let json = r#"{"url":"sqlite::memory:"}"#;
        let cfg: DbConfig = serde_json::from_str(json).unwrap();
        assert_eq!(cfg.url, "sqlite::memory:");
        assert_eq!(cfg.max_connections, 20);
        assert_eq!(cfg.min_connections, 5);
        assert_eq!(cfg.admin_role, "admin");
        assert!(!cfg.auto_migrate);
    }

    // 注意：from_env() 测试需要修改环境变量，在 Rust 2024 edition 中
    // set_var/remove_var 为 unsafe，但 lib crate 有 #![forbid(unsafe_code)]。
    // from_env 的覆盖率为外部测试目录（tests/）中独立 crate 的测试覆盖。
}
