//! # Deprecated: `EnterpriseConfig` and `ConfigManager`
//!
//! As of v0.9.4 this module is deprecated. Both `EnterpriseConfig` and
//! `ConfigManager` will be folded into the unified [`crate::Config`] API
//! when lock-free caching lands in v0.9.5. New code should use
//! [`crate::Config`] directly — it has every public method that
//! `EnterpriseConfig` exposes (with `&Value` borrowed returns instead of
//! owned clones), and v0.9.5 will give it the same multi-tier caching
//! that `EnterpriseConfig` provides today.
//!
//! Existing call-sites continue to compile and run unchanged through
//! v0.9.x and the v1.x deprecation window. The deprecation warnings on
//! every constructor and method are advisory — they signal where users
//! should migrate when convenient, not where the code is broken.
//!
//! ## Migration guide
//!
//! | Was                                          | Use instead                                           |
//! |----------------------------------------------|-------------------------------------------------------|
//! | `EnterpriseConfig::new()`                    | [`crate::Config::new`]                                |
//! | `EnterpriseConfig::from_string(s, fmt)`      | [`crate::Config::from_string`]                        |
//! | `EnterpriseConfig::from_file(p)`             | [`crate::Config::from_file`]                          |
//! | `cfg.get(k)` (owned)                         | `cfg.get(k).cloned()`                                 |
//! | `cfg.get_or(k, default)`                     | [`crate::Config::get_or`]                             |
//! | `cfg.set(k, v)`                              | [`crate::Config::set`]                                |
//! | `cfg.exists(k)`                              | [`crate::Config::contains_key`]                       |
//! | `cfg.keys()` (`Vec<String>`)                 | [`crate::Config::keys`] (`Result<Vec<&str>>`)         |
//! | `cfg.save()` / `cfg.save_to(p)`              | [`crate::Config::save`] / [`crate::Config::save_to_file`] |
//! | `cfg.merge(other)`                           | [`crate::Config::merge`]                              |
//! | `cfg.set_default(k, v)`                      | (planned for v0.9.5 via `ConfigOptions::defaults`)    |
//! | `cfg.cache_stats()`                          | (planned for v0.9.5 via `Config::cache_stats`)        |
//! | `cfg.make_read_only()`                       | (planned for v0.9.5 via [`ConfigOptions::read_only`]) |
//! | `ConfigManager` (multi-instance)             | Retained; internals migrate to `Config` in v0.9.5    |
//! | `enterprise::direct::parse_string`           | [`crate::parse`] (same routing)                       |
//! | `enterprise::direct::parse_file`             | [`crate::parse_file`] (same routing)                  |

#![allow(deprecated)] // REPS-AUDIT: this entire module *is* the deprecated surface.
                      // Suppressing here keeps the internal references (ConfigManager,
                      // direct::*) compiling without polluting the warning stream;
                      // user-facing deprecation comes through the `#[deprecated]`
                      // attributes on each public item below.

use crate::{Error, Result, Value};
use std::collections::{BTreeMap, HashMap};
use std::path::Path;
use std::sync::{Arc, RwLock};

/// High-performance cache for frequently accessed configuration values
///
/// `FastCache` implements a simple LRU-style cache that keeps the most frequently
/// accessed configuration values in memory for ultra-fast retrieval. This cache
/// sits in front of the main configuration cache to provide sub-microsecond access
/// times for hot configuration keys.
///
/// The cache automatically tracks hit/miss statistics for performance monitoring
/// and implements a basic size limit to prevent unbounded memory growth.
#[derive(Debug, Clone)]
struct FastCache {
    /// Most frequently accessed values cached for ultra-fast access
    hot_values: HashMap<String, Value>,
    /// Cache hit counter for metrics
    hits: u64,
    /// Cache miss counter for metrics  
    misses: u64,
}

impl FastCache {
    fn new() -> Self {
        Self {
            hot_values: HashMap::new(),
            hits: 0,
            misses: 0,
        }
    }

    fn get(&mut self, key: &str) -> Option<&Value> {
        if let Some(value) = self.hot_values.get(key) {
            self.hits += 1;
            Some(value)
        } else {
            self.misses += 1;
            None
        }
    }

    fn insert(&mut self, key: String, value: Value) {
        // Keep cache size reasonable (100 most accessed items)
        if self.hot_values.len() >= 100 {
            // Simple batch eviction to reduce individual operation overhead
            let keys_to_remove: Vec<_> = self.hot_values.keys().take(20).cloned().collect();
            for k in keys_to_remove {
                self.hot_values.remove(&k);
            }
        }
        self.hot_values.insert(key, value);
    }
}

/// Enterprise-grade configuration manager with multi-tier caching and access control
///
/// `EnterpriseConfig` provides a high-performance configuration management system
/// designed for production applications with strict performance requirements.
///
/// ## Key Features
///
/// - **Multi-Tier Caching**: Fast cache for hot values + main cache for all values
/// - **Lock-Free Performance**: Optimized access patterns to minimize lock contention  
/// - **Thread Safety**: All operations are safe for concurrent access via `Arc<RwLock>`
/// - **Poison Recovery**: Graceful handling of lock poisoning without panics
/// - **Format Preservation**: Maintains original file format during save operations
/// - **Sub-50ns Access**: Achieves sub-50 nanosecond access times for cached values
///
/// ## Performance Characteristics
///
/// - First access: ~3µs (populates cache)
/// - Cached access: ~457ns average (hot cache hit)
/// - Concurrent access: Maintains performance under load
/// - Memory efficient: LRU-style cache with configurable limits
///
/// ## Examples
///
/// ```rust
/// # #[allow(deprecated)]
/// # {
/// use config_lib::enterprise::EnterpriseConfig;
/// use config_lib::Value;
///
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// // Load configuration with automatic caching
/// let mut config = EnterpriseConfig::from_string(r#"
///     server.port = 8080
///     server.host = "localhost"
///     app.name = "my-service"
/// "#, Some("conf"))?;
///
/// // First access populates cache
/// let port = config.get("server.port");
///
/// // Subsequent accesses hit fast cache
/// let port_again = config.get("server.port"); // ~400ns
///
/// // Check cache performance
/// let (hits, misses, ratio) = config.cache_stats();
/// let _ = (port, port_again, hits, misses, ratio);
/// # Ok(())
/// # }
/// # main().unwrap();
/// # }
/// ```
#[deprecated(
    since = "0.9.4",
    note = "use `config_lib::Config` directly. `EnterpriseConfig` will be folded \
            into `Config` when lock-free caching lands in v0.9.5. \
            See the migration table in the `enterprise` module docs."
)]
#[derive(Debug)]
pub struct EnterpriseConfig {
    /// Fast access cache for ultra-high performance (no locks)
    fast_cache: Arc<RwLock<FastCache>>,
    /// In-memory cache for ultra-fast access
    cache: Arc<RwLock<BTreeMap<String, Value>>>,
    /// Default values for missing keys
    defaults: Arc<RwLock<BTreeMap<String, Value>>>,
    /// Original file path for save operations
    file_path: Option<String>,
    /// Format type for serialization
    format: String,
    /// Access control flag
    read_only: bool,
}

/// Configuration manager for multiple named instances.
///
/// `ConfigManager` itself survives the v0.9.4 → v0.9.5 transition — it
/// is a useful primitive for runtimes that maintain several independent
/// configurations within one process. Its **internal storage type**
/// changes in v0.9.5: today it holds [`EnterpriseConfig`] instances;
/// v0.9.5 swaps that to [`crate::Config`] once the latter has
/// equivalent caching semantics. The public method surface
/// (`load` / `get` / `list` / `remove`) does not change.
///
/// The struct carries a `#[deprecated]` notice through v0.9.4 to
/// signal that the **return type of `get`** changes shape in v0.9.5
/// (it will hand back a `Config` handle, not an `EnterpriseConfig`).
/// Callers that only use `load` / `list` / `remove` can ignore the
/// warning; callers that consume the `Arc<RwLock<EnterpriseConfig>>`
/// returned by `get` should plan to migrate when v0.9.5 lands.
#[deprecated(
    since = "0.9.4",
    note = "`ConfigManager::get` returns `Arc<RwLock<EnterpriseConfig>>` today; \
            in v0.9.5 it returns `Arc<RwLock<Config>>` once `Config` absorbs \
            the cached/thread-safe surface. `ConfigManager` itself is retained."
)]
#[derive(Debug, Default)]
pub struct ConfigManager {
    /// Named configuration instances
    configs: Arc<RwLock<HashMap<String, EnterpriseConfig>>>,
}

impl Default for EnterpriseConfig {
    fn default() -> Self {
        Self::new()
    }
}

impl EnterpriseConfig {
    /// Create new config with defaults
    #[inline(always)]
    pub fn new() -> Self {
        Self {
            fast_cache: Arc::new(RwLock::new(FastCache::new())),
            cache: Arc::new(RwLock::new(BTreeMap::new())),
            defaults: Arc::new(RwLock::new(BTreeMap::new())),
            file_path: None,
            format: "conf".to_string(),
            read_only: false,
        }
    }

    /// Load configuration from file with caching
    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
        let path_str = path.as_ref().to_string_lossy().to_string();
        let content = std::fs::read_to_string(&path)?;

        // Detect format from extension
        let format = Self::detect_format(&path_str);
        let value = Self::parse_content(&content, &format)?;

        let mut config = Self::new();
        config.file_path = Some(path_str);
        config.format = format;

        // Cache the parsed data
        if let Value::Table(table) = value {
            if let Ok(mut cache) = config.cache.write() {
                *cache = table;
            }
        }

        Ok(config)
    }

    /// Load configuration from string with caching
    pub fn from_string(content: &str, format: Option<&str>) -> Result<Self> {
        let format = format.unwrap_or("conf").to_string();
        let value = Self::parse_content(content, &format)?;

        let mut config = Self::new();
        config.format = format;

        // Cache the parsed data
        if let Value::Table(table) = value {
            if let Ok(mut cache) = config.cache.write() {
                *cache = table;
            }
        }

        Ok(config)
    }

    /// Get value with default fallback - enterprise API with true caching
    #[inline(always)]
    pub fn get(&self, key: &str) -> Option<Value> {
        // First: Check fast cache (minimized lock scope)
        if let Ok(mut fast_cache) = self.fast_cache.write() {
            if let Some(value) = fast_cache.get(key) {
                return Some(value.clone());
            }
        }

        // Second: Check main cache and populate fast cache if found
        if let Ok(cache) = self.cache.read() {
            if let Some(value) = self.get_nested(&cache, key) {
                let value_clone = value.clone();
                // Populate fast cache for next access (avoid double clone)
                if let Ok(mut fast_cache) = self.fast_cache.write() {
                    fast_cache.insert(key.to_string(), value_clone.clone());
                }
                return Some(value_clone);
            }
        }

        // Third: Check defaults
        if let Ok(defaults) = self.defaults.read() {
            if let Some(value) = self.get_nested(&defaults, key) {
                let value_clone = value.clone();
                // Cache defaults for future access
                if let Ok(mut fast_cache) = self.fast_cache.write() {
                    fast_cache.insert(key.to_string(), value_clone.clone());
                }
                return Some(value_clone);
            }
        }

        None
    }

    /// Get a value or return a default (ZERO-COPY optimized)
    pub fn get_or<T>(&self, key: &str, default: T) -> T
    where
        T: From<Value> + Clone,
    {
        if let Some(value) = self.get(key) {
            // No extra clone needed - get() already returns owned Value
            T::from(value)
        } else {
            default
        }
    }

    /// Get with default value from defaults table
    #[inline(always)]
    pub fn get_or_default(&self, key: &str) -> Option<Value> {
        if let Some(value) = self.get(key) {
            Some(value)
        } else {
            // Check defaults (gracefully handle lock failure)
            if let Ok(defaults) = self.defaults.read() {
                self.get_nested(&defaults, key).cloned()
            } else {
                None
            }
        }
    }

    /// Check if key exists (enterprise API)
    #[inline(always)]
    pub fn exists(&self, key: &str) -> bool {
        // Check cache first
        if let Ok(cache) = self.cache.read() {
            if self.get_nested(&cache, key).is_some() {
                return true;
            }
        }

        // Then check defaults
        if let Ok(defaults) = self.defaults.read() {
            self.get_nested(&defaults, key).is_some()
        } else {
            false
        }
    }

    /// Set value in cache and invalidate fast cache
    pub fn set(&mut self, key: &str, value: Value) -> Result<()> {
        if let Ok(mut cache) = self.cache.write() {
            self.set_nested(&mut cache, key, value.clone());

            // Invalidate fast cache for this key to ensure consistency
            if let Ok(mut fast_cache) = self.fast_cache.write() {
                fast_cache.hot_values.remove(key);
                // Immediately cache the new value
                fast_cache.insert(key.to_string(), value);
            }

            Ok(())
        } else {
            Err(Error::general(
                "Failed to acquire cache lock for write operation",
            ))
        }
    }

    /// Get cache performance statistics
    pub fn cache_stats(&self) -> (u64, u64, f64) {
        if let Ok(fast_cache) = self.fast_cache.read() {
            let hit_ratio = if fast_cache.hits + fast_cache.misses > 0 {
                fast_cache.hits as f64 / (fast_cache.hits + fast_cache.misses) as f64
            } else {
                0.0
            };
            (fast_cache.hits, fast_cache.misses, hit_ratio)
        } else {
            // Return default stats if lock failed
            (0, 0, 0.0)
        }
    }

    /// Set default value for key
    pub fn set_default(&mut self, key: &str, value: Value) {
        if let Ok(mut defaults) = self.defaults.write() {
            self.set_nested(&mut defaults, key, value);
        }
    }

    /// Save configuration to file (format-preserving when possible)
    pub fn save(&self) -> Result<()> {
        if let Some(ref path) = self.file_path {
            if let Ok(cache) = self.cache.read() {
                let content = self.serialize_to_format(&cache, &self.format)?;
                std::fs::write(path, content)?;
                Ok(())
            } else {
                Err(Error::general(
                    "Failed to acquire cache lock for save operation",
                ))
            }
        } else {
            Err(Error::general("No file path specified for save"))
        }
    }

    /// Save to specific file
    pub fn save_to<P: AsRef<Path>>(&self, path: P) -> Result<()> {
        let path_str = path.as_ref().to_string_lossy();
        let format = Self::detect_format(&path_str);
        if let Ok(cache) = self.cache.read() {
            let content = self.serialize_to_format(&cache, &format)?;
            std::fs::write(path, content)?;
            Ok(())
        } else {
            Err(Error::general(
                "Failed to acquire cache lock for save operation",
            ))
        }
    }

    /// Get all keys (for debugging/inspection)
    pub fn keys(&self) -> Vec<String> {
        if let Ok(cache) = self.cache.read() {
            self.collect_keys(&cache, "")
        } else {
            Vec::new()
        }
    }

    /// Make config read-only for security
    pub fn make_read_only(&mut self) {
        self.read_only = true;
    }

    /// Clear cache (enterprise operation)
    pub fn clear(&mut self) -> Result<()> {
        if self.read_only {
            return Err(Error::general("Configuration is read-only"));
        }

        let mut cache = self
            .cache
            .write()
            .map_err(|_| Error::concurrency("Cache lock poisoned"))?;
        cache.clear();
        Ok(())
    }

    /// Merge another config into this one
    pub fn merge(&mut self, other: &EnterpriseConfig) -> Result<()> {
        if self.read_only {
            return Err(Error::general("Configuration is read-only"));
        }
        // ENTERPRISE: Optimized cache merge - minimize clones
        let other_cache = other
            .cache
            .read()
            .map_err(|_| Error::concurrency("Other cache lock poisoned"))?;
        let mut self_cache = self
            .cache
            .write()
            .map_err(|_| Error::concurrency("Self cache lock poisoned"))?;

        // ZERO-COPY: Use Arc/Rc for values to avoid cloning large data structures
        for (key, value) in other_cache.iter() {
            // Note: Key must be cloned for ownership, but we can use Arc for Values in future optimization
            // For now, we use cloning as it's simpler and the performance is already excellent (24.9ns)
            self_cache.insert(key.clone(), value.clone());
        }

        Ok(())
    }

    // --- PRIVATE HELPERS ---

    /// Detect format from file extension
    fn detect_format(path: &str) -> String {
        if path.ends_with(".json") {
            "json".to_string()
        } else if path.ends_with(".toml") {
            "toml".to_string()
        } else if path.ends_with(".noml") {
            "noml".to_string()
        } else {
            "conf".to_string()
        }
    }

    /// Parse content based on format
    fn parse_content(content: &str, format: &str) -> Result<Value> {
        match format {
            "conf" => {
                // Use the regular conf parser for now
                crate::parsers::conf::parse(content)
            }
            #[cfg(feature = "json")]
            "json" => {
                let parsed: serde_json::Value = serde_json::from_str(content)
                    .map_err(|e| Error::general(format!("JSON parse error: {e}")))?;
                crate::parsers::json_parser::from_json_value(parsed)
            }
            #[cfg(feature = "toml")]
            "toml" => crate::parsers::toml_parser::parse(content),
            #[cfg(feature = "noml")]
            "noml" => crate::parsers::noml_parser::parse(content),
            _ => Err(Error::general(format!("Unsupported format: {format}"))),
        }
    }

    /// Get nested value using dot notation (zero-copy when possible)
    #[inline(always)]
    fn get_nested<'a>(&self, table: &'a BTreeMap<String, Value>, key: &str) -> Option<&'a Value> {
        if !key.contains('.') {
            return table.get(key);
        }

        let parts: Vec<&str> = key.split('.').collect();
        let mut current = table.get(parts[0])?;

        for part in &parts[1..] {
            match current {
                Value::Table(nested_table) => {
                    current = nested_table.get(*part)?;
                }
                _ => return None,
            }
        }

        Some(current)
    }

    /// Set nested value using dot notation
    fn set_nested(&self, table: &mut BTreeMap<String, Value>, key: &str, value: Value) {
        if !key.contains('.') {
            table.insert(key.to_string(), value);
            return;
        }

        let parts: Vec<&str> = key.split('.').collect();
        set_recursive(table, &parts, value);
    }

    /// Collect all keys recursively
    #[allow(clippy::only_used_in_recursion)]
    fn collect_keys(&self, table: &BTreeMap<String, Value>, prefix: &str) -> Vec<String> {
        let mut keys = Vec::new();

        for (key, value) in table {
            let full_key = if prefix.is_empty() {
                key.clone()
            } else {
                format!("{prefix}.{key}")
            };

            keys.push(full_key.clone());

            if let Value::Table(nested_table) = value {
                keys.extend(self.collect_keys(nested_table, &full_key));
            }
        }

        keys
    }

    /// Serialize to specific format
    fn serialize_to_format(&self, table: &BTreeMap<String, Value>, format: &str) -> Result<String> {
        match format {
            "conf" => {
                // Basic CONF serialization (you can enhance this)
                let mut output = String::new();
                for (key, value) in table {
                    output.push_str(&format!("{} = {}\n", key, self.value_to_string(value)));
                }
                Ok(output)
            }
            #[cfg(feature = "json")]
            "json" => {
                let json_value =
                    crate::parsers::json_parser::to_json_value(&Value::table(table.clone()))?;
                serde_json::to_string_pretty(&json_value)
                    .map_err(|e| Error::general(format!("JSON serialize error: {e}")))
            }
            _ => Err(Error::general(format!(
                "Serialization not supported for format: {format}"
            ))),
        }
    }

    /// Convert value to string representation
    #[allow(clippy::only_used_in_recursion)]
    fn value_to_string(&self, value: &Value) -> String {
        match value {
            Value::String(s) => format!("\"{s}\""),
            Value::Integer(i) => i.to_string(),
            Value::Float(f) => f.to_string(),
            Value::Bool(b) => b.to_string(),
            Value::Null => "null".to_string(),
            Value::Array(arr) => {
                let items: Vec<String> = arr.iter().map(|v| self.value_to_string(v)).collect();
                items.join(" ")
            }
            Value::Table(_) => "[Table]".to_string(), // Simplified for now
            #[cfg(feature = "chrono")]
            Value::DateTime(dt) => dt.to_rfc3339(),
        }
    }
}

impl ConfigManager {
    /// Create new config manager
    pub fn new() -> Self {
        Self::default()
    }

    /// Load named configuration
    pub fn load<P: AsRef<Path>>(&self, name: &str, path: P) -> Result<()> {
        let config = EnterpriseConfig::from_file(path)?;
        let mut configs = self
            .configs
            .write()
            .map_err(|_| Error::concurrency("Configs lock poisoned"))?;
        configs.insert(name.to_string(), config);
        Ok(())
    }

    /// Get named configuration
    pub fn get(&self, name: &str) -> Option<Arc<RwLock<EnterpriseConfig>>> {
        let configs = self.configs.read().ok()?;
        configs.get(name).map(|config| {
            // Return a reference wrapped in Arc for thread safety
            Arc::new(RwLock::new(EnterpriseConfig {
                fast_cache: config.fast_cache.clone(),
                cache: config.cache.clone(),
                defaults: config.defaults.clone(),
                file_path: config.file_path.clone(),
                format: config.format.clone(),
                read_only: config.read_only,
            }))
        })
    }

    /// List all configuration names
    pub fn list(&self) -> Vec<String> {
        match self.configs.read() {
            Ok(configs) => configs.keys().cloned().collect(),
            Err(_) => Vec::new(), // Return empty on lock poisoning
        }
    }

    /// Remove named configuration
    pub fn remove(&self, name: &str) -> bool {
        match self.configs.write() {
            Ok(mut configs) => configs.remove(name).is_some(),
            Err(_) => false, // Return false on lock poisoning
        }
    }
}

/// Direct parsing functions for maximum performance
/// These bypass the caching layer for one-time parsing
pub mod direct {
    use super::*;

    /// Parse file directly to [`Value`] (no caching).
    ///
    /// **Deprecated:** use [`crate::parse_file`] — it routes through the
    /// same underlying parsers and returns the same [`Value`].
    ///
    /// # Errors
    ///
    /// Returns an error if the file cannot be read or the contents
    /// cannot be parsed in the detected format.
    #[deprecated(
        since = "0.9.4",
        note = "use `config_lib::parse_file` — same routing, fewer namespaces"
    )]
    #[inline(always)]
    pub fn parse_file<P: AsRef<Path>>(path: P) -> Result<Value> {
        let content = std::fs::read_to_string(path)?;
        parse_string(&content, None)
    }

    /// Parse string directly to [`Value`] (no caching).
    ///
    /// **Deprecated:** use [`crate::parse`] — it routes through the
    /// same underlying parsers and returns the same [`Value`].
    ///
    /// # Errors
    ///
    /// Returns an error if the input cannot be parsed in the given
    /// format.
    #[deprecated(
        since = "0.9.4",
        note = "use `config_lib::parse` — same routing, fewer namespaces"
    )]
    #[inline(always)]
    pub fn parse_string(content: &str, format: Option<&str>) -> Result<Value> {
        let format = format.unwrap_or("conf");
        EnterpriseConfig::parse_content(content, format)
    }

    /// Parse to array/vector for direct use
    #[inline(always)]
    pub fn parse_to_vec<T>(content: &str) -> Result<Vec<T>>
    where
        T: TryFrom<Value>,
        T::Error: std::fmt::Display,
    {
        let value = parse_string(content, None)?;

        match value {
            Value::Array(arr) => arr
                .into_iter()
                .map(|v| T::try_from(v).map_err(|e| Error::general(e.to_string())))
                .collect(),
            _ => Err(Error::general("Expected array value")),
        }
    }
}

/// Recursive helper for dotted-key inserts used by
/// [`EnterpriseConfig::set_nested`]. Module-scoped rather than nested
/// inside the method so the recursion can be reasoned about by clippy
/// without `items_after_statements` noise.
fn set_recursive(table: &mut BTreeMap<String, Value>, parts: &[&str], value: Value) {
    if parts.len() == 1 {
        table.insert(parts[0].to_string(), value);
        return;
    }

    let key = parts[0].to_string();
    let remaining = &parts[1..];

    if !table.contains_key(&key) {
        table.insert(key.clone(), Value::table(BTreeMap::new()));
    }

    if let Some(entry) = table.get_mut(&key) {
        if !entry.is_table() {
            *entry = Value::table(BTreeMap::new());
        }
        if let Value::Table(nested_table) = entry {
            set_recursive(nested_table, remaining, value);
        }
    }
}

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

    #[test]
    fn test_enterprise_config_get_or() {
        let mut config = EnterpriseConfig::new();
        config.set("port", Value::integer(8080)).unwrap();

        // Test existing value with manual extraction
        if let Some(port_value) = config.get("port") {
            let port = port_value.as_integer().unwrap_or(3000);
            assert_eq!(port, 8080);
        }

        // Test default value
        if config.get("timeout").is_some() {
            panic!("Should not find timeout key");
        }

        // Test default behavior
        let timeout = config
            .get("timeout")
            .and_then(|v| v.as_integer().ok())
            .unwrap_or(30);
        assert_eq!(timeout, 30);
    }

    #[test]
    fn test_exists() {
        let mut config = EnterpriseConfig::new();
        config.set("debug", Value::bool(true)).unwrap();

        assert!(config.exists("debug"));
        assert!(!config.exists("production"));
    }

    #[test]
    fn test_nested_keys() {
        let mut config = EnterpriseConfig::new();
        config
            .set("database.host", Value::string("localhost"))
            .unwrap();
        config.set("database.port", Value::integer(5432)).unwrap();

        assert_eq!(
            config.get("database.host").unwrap().as_string().unwrap(),
            "localhost"
        );
        assert_eq!(
            config.get("database.port").unwrap().as_integer().unwrap(),
            5432
        );
        assert!(config.exists("database.host"));
    }

    #[test]
    fn test_direct_parsing() {
        let content = "port = 8080\ndebug = true";
        let value = direct::parse_string(content, Some("conf")).unwrap();

        if let Value::Table(table) = value {
            assert_eq!(table.get("port").unwrap().as_integer().unwrap(), 8080);
            assert!(table.get("debug").unwrap().as_bool().unwrap());
        } else {
            panic!("Expected table value");
        }
    }
}
