//! Core configuration trait.
//!
//! This module defines the synchronous `Config` trait that all
//! configuration types should implement.

use crate::Result;

/// Core configuration trait.
///
/// This trait defines the basic operations that all configuration types
/// should support. Implementations should be:
///
/// - **Thread-safe**: `Send + Sync` for concurrent access
/// - **Cloneable**: Cheap to clone for use across the application
/// - **Serializable**: Work with `serde` for persistence
///
/// # Example
///
/// ```
/// use cfgmatic_core::Config;
/// use serde::{Deserialize, Serialize};
///
/// #[derive(Debug, Clone, Deserialize, Serialize)]
/// struct ServerConfig {
///     host: String,
///     port: u16,
/// }
///
/// impl Config for ServerConfig {
///     fn merge(&mut self, other: Self) -> cfgmatic_core::Result<()> {
///         // Simple override merge
///         self.host = other.host;
///         self.port = other.port;
///         Ok(())
///     }
/// }
/// ```
pub trait Config: Send + Sync + Clone + serde::Serialize + serde::de::DeserializeOwned {
    /// Merge another configuration into this one.
    ///
    /// This method should combine `other` into `self`, with `other`
    /// taking precedence for conflicting values.
    ///
    /// # Errors
    ///
    /// Returns an error if the merge cannot be performed, such as
    /// incompatible types or validation failures.
    ///
    /// # Example
    ///
    /// ```
    /// use cfgmatic_core::Config;
    ///
    /// # #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
    /// # struct MyConfig;
    /// # impl Config for MyConfig {
    /// #     fn merge(&mut self, other: Self) -> cfgmatic_core::Result<()> { Ok(()) }
    /// # }
    /// let mut config = MyConfig;
    /// let other = MyConfig;
    /// config.merge(other)?;
    /// # Ok::<(), cfgmatic_core::Error>(())
    /// ```
    fn merge(&mut self, other: Self) -> Result<()>;

    /// Validate this configuration.
    ///
    /// Implementations can override this to provide custom validation logic.
    /// By default, this always returns `Ok(())`.
    ///
    /// # Errors
    ///
    /// Returns an error if the configuration is invalid.
    ///
    /// # Example
    ///
    /// ```
    /// use cfgmatic_core::Config;
    ///
    /// # #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
    /// # struct ServerConfig { port: u16 }
    /// impl Config for ServerConfig {
    /// #     fn merge(&mut self, other: Self) -> cfgmatic_core::Result<()> { Ok(()) }
    ///     fn validate(&self) -> cfgmatic_core::Result<()> {
    ///         if self.port < 1024 {
    ///             return Err(cfgmatic_core::Error::validation(
    ///                 "port must be >= 1024".to_string()
    ///             ));
    ///         }
    ///         Ok(())
    ///     }
    /// }
    /// ```
    fn validate(&self) -> Result<()> {
        let _ = self;
        Ok(())
    }

    /// Convert configuration to a JSON value.
    ///
    /// This is a convenience method for serialization and diff operations.
    ///
    /// # Errors
    ///
    /// Returns an error if serialization fails.
    fn to_json(&self) -> Result<serde_json::Value> {
        serde_json::to_value(self).map_err(|e| crate::Error::Serialization(e.to_string()))
    }

    /// Get configuration as a JSON string.
    ///
    /// # Errors
    ///
    /// Returns an error if serialization fails.
    fn to_json_string(&self) -> Result<String> {
        serde_json::to_string(self).map_err(|e| crate::Error::Serialization(e.to_string()))
    }

    /// Get configuration as a pretty-printed JSON string.
    ///
    /// # Errors
    ///
    /// Returns an error if serialization fails.
    fn to_json_string_pretty(&self) -> Result<String> {
        serde_json::to_string_pretty(self).map_err(|e| crate::Error::Serialization(e.to_string()))
    }
}

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

    #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
    struct TestConfig {
        value: String,
        count: u32,
    }

    impl Config for TestConfig {
        fn merge(&mut self, other: Self) -> Result<()> {
            self.value = other.value;
            self.count = other.count;
            Ok(())
        }

        fn validate(&self) -> Result<()> {
            if self.count > 100 {
                return Err(crate::Error::validation("count must be <= 100".to_string()));
            }
            Ok(())
        }
    }

    #[test]
    fn test_merge() {
        let mut config = TestConfig {
            value: "original".to_string(),
            count: 5,
        };
        let other = TestConfig {
            value: "updated".to_string(),
            count: 10,
        };

        config.merge(other).unwrap();
        assert_eq!(config.value, "updated");
        assert_eq!(config.count, 10);
    }

    #[test]
    fn test_validate_ok() {
        let config = TestConfig {
            value: "test".to_string(),
            count: 50,
        };
        assert!(config.validate().is_ok());
    }

    #[test]
    fn test_validate_error() {
        let config = TestConfig {
            value: "test".to_string(),
            count: 150,
        };
        assert!(config.validate().is_err());
    }

    #[test]
    fn test_to_json() {
        let config = TestConfig {
            value: "test".to_string(),
            count: 42,
        };
        let json = config.to_json().unwrap();
        assert_eq!(json["value"], "test");
        assert_eq!(json["count"], 42);
    }

    #[test]
    fn test_to_json_string() {
        let config = TestConfig {
            value: "test".to_string(),
            count: 42,
        };
        let json_str = config.to_json_string().unwrap();
        assert!(json_str.contains("\"value\""));
        assert!(json_str.contains("\"count\""));
    }
}
