//! Async configuration trait.
//!
//! This module defines the async `AsyncConfig` trait for configurations
//! that support asynchronous operations.
//!
//! # Feature
//!
//! This module is only available when the `async` feature is enabled.
//!
//! # Example
//!
//! ```rust,no_run
//! use cfgmatic_core::{AsyncConfig, 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<()> {
//!         self.host = other.host;
//!         self.port = other.port;
//!         Ok(())
//!     }
//! }
//!
//! impl AsyncConfig for ServerConfig {
//!     async fn merge_async(&mut self, other: Self) -> cfgmatic_core::Result<()> {
//!         // Async merge logic
//!         self.host = other.host;
//!         self.port = other.port;
//!         Ok(())
//!     }
//!
//!     async fn collect_state(&self) -> cfgmatic_core::Result<serde_json::Value> {
//!         // Async state collection (e.g., from remote API)
//!         Ok(self.to_json()?)
//!     }
//! }
//! ```

use crate::{Config, Result};
use serde_json::Value;

/// Async configuration trait.
///
/// Extends the sync `Config` trait with async operations for scenarios
/// where operations may block (e.g., network calls, file I/O).
///
/// # Example
///
/// ```rust,no_run
/// use cfgmatic_core::{AsyncConfig, Config, Result};
///
/// #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
/// struct RemoteConfig {
///     url: String,
/// }
///
/// impl Config for RemoteConfig {
///     fn merge(&mut self, other: Self) -> Result<()> {
///         self.url = other.url;
///         Ok(())
///     }
/// }
///
/// impl AsyncConfig for RemoteConfig {
///     async fn merge_async(&mut self, other: Self) -> Result<()> {
///         // Maybe validate against remote service
///         self.url = other.url;
///         Ok(())
///     }
///
///     async fn collect_state(&self) -> Result<serde_json::Value> {
///         // Fetch actual state from a custom async backend
///         Ok(serde_json::json!({ "url": self.url }))
///     }
/// }
/// ```
pub trait AsyncConfig: Config {
    /// Async merge of another configuration into this one.
    ///
    /// Like `Config::merge`, but async for operations that may block.
    ///
    /// # Errors
    ///
    /// Returns an error if the merge cannot be performed.
    async fn merge_async(&mut self, other: Self) -> Result<()> {
        // Default to sync merge
        self.merge(other)
    }

    /// Async validation of this configuration.
    ///
    /// Override this for async validation logic (e.g., checking against
    /// a remote service).
    ///
    /// # Errors
    ///
    /// Returns an error if the configuration is invalid.
    async fn validate_async(&self) -> Result<()> {
        // Default to sync validation
        self.validate()
    }

    /// Collect the actual runtime state for this configuration.
    ///
    /// This is used in Terraform-like workflows where the user needs to
    /// provide the current/actual state of the system.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// # use cfgmatic_core::{AsyncConfig, Config, Result};
    /// # #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
    /// # struct RemoteConfig {
    /// #     url: String,
    /// # }
    /// # impl Config for RemoteConfig {
    /// #     fn merge(&mut self, other: Self) -> Result<()> {
    /// #         self.url = other.url;
    /// #         Ok(())
    /// #     }
    /// # }
    /// # impl AsyncConfig for RemoteConfig {
    /// async fn collect_state(&self) -> Result<serde_json::Value> {
    ///     // Query the running application for its state
    ///     let payload = serde_json::json!({ "url": self.url });
    ///     Ok(payload)
    /// }
    /// # }
    /// ```
    ///
    /// # Errors
    ///
    /// Returns an error if state collection fails.
    async fn collect_state(&self) -> Result<Value> {
        // Default: serialize to JSON
        self.to_json()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde::{Deserialize, Serialize};

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

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

    impl AsyncConfig for TestConfig {}

    #[tokio::test]
    async fn test_async_merge() {
        let mut config = TestConfig {
            value: "original".to_string(),
        };
        let other = TestConfig {
            value: "updated".to_string(),
        };

        config.merge_async(other).await.unwrap();
        assert_eq!(config.value, "updated");
    }

    #[tokio::test]
    async fn test_collect_state() {
        let config = TestConfig {
            value: "test".to_string(),
        };

        let state = config.collect_state().await.unwrap();
        assert_eq!(state["value"], "test");
    }
}
