//! Integration examples for resilience patterns
//!
//! This module provides example code showing how to integrate circuit breakers,
//! retry logic, and bounded concurrency into gRPC services.
//!
//! # Service Integration Pattern
//!
//! The recommended pattern for service integration is:
//!
//! 1. Create a circuit breaker for each external dependency
//! 2. Wrap operations with retry logic
//! 3. Use bounded executor to limit concurrent operations
//!
//! # Example: Complete Service Integration
//!
//! ```rust,ignore
//! use common::resilience::{
//!     CircuitBreaker, CircuitBreakerConfig, RetryConfig,
//!     retry_with_backoff, BoundedExecutor,
//! };
//! use std::sync::Arc;
//! use std::time::Duration;
//! use tokio::sync::RwLock;
//!
//! pub struct ResilientService {
//!     // Circuit breakers for different dependencies
//!     database_cb: Arc<CircuitBreaker>,
//!     external_api_cb: Arc<CircuitBreaker>,
//!
//!     // Bounded executor for limiting concurrent operations
//!     executor: BoundedExecutor,
//! }
//!
//! impl ResilientService {
//!     pub fn new() -> Self {
//!         // Configure circuit breaker for database
//!         let db_config = CircuitBreakerConfig {
//!             failure_threshold: 5,
//!             timeout: Duration::from_secs(30),
//!             success_threshold: 2,
//!         };
//!
//!         // Configure circuit breaker for external API
//!         let api_config = CircuitBreakerConfig {
//!             failure_threshold: 3,
//!             timeout: Duration::from_secs(60),
//!             success_threshold: 2,
//!         };
//!
//!         Self {
//!             database_cb: Arc::new(CircuitBreaker::new("database", db_config)),
//!             external_api_cb: Arc::new(CircuitBreaker::new("external_api", api_config)),
//!             executor: BoundedExecutor::new(10), // Max 10 concurrent operations
//!         }
//!     }
//!
//!     /// Execute database operation with circuit breaker and retry
//!     pub async fn execute_db_operation<T>(
//!         &self,
//!         operation: impl Fn() -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<T, CommonError>> + Send>> + Send,
//!     ) -> Result<T, CommonError> {
//!         // Check circuit breaker
//!         if !self.database_cb.can_execute().await {
//!             return Err(CommonError::service(
//!                 ErrorCategory::Database,
//!                 "Circuit breaker open - database unavailable",
//!             ));
//!         }
//!
//!         // Execute with retry and bounded concurrency
//!         let db_cb = Arc::clone(&self.database_cb);
//!
//!         let result = self.executor.execute(|| async {
//!             retry_with_backoff(
//!                 || operation(),
//!                 RetryConfig::default(),
//!             ).await
//!         }).await;
//!
//!         match &result {
//!             Ok(_) => self.database_cb.record_success().await,
//!             Err(_) => self.database_cb.record_failure().await,
//!         }
//!
//!         result.map_err(|e| match e {
//!             RetryError::MaxRetriesExceeded { last_error, .. } => last_error,
//!             RetryError::NonRetryableError(err) => err,
//!         })
//!     }
//! }
//! ```
//!
//! # Example: gRPC Handler with Full Resilience
//!
//! ```rust,ignore
//! use tonic::{Request, Response, Status};
//! use common::resilience::{
//!     CircuitBreaker, retry_with_backoff, RetryConfig, BoundedExecutor,
//! };
//!
//! pub struct TradingService {
//!     circuit_breaker: Arc<CircuitBreaker>,
//!     executor: BoundedExecutor,
//! }
//!
//! #[tonic::async_trait]
//! impl TradingServiceGrpc for TradingService {
//!     async fn submit_order(
//!         &self,
//!         request: Request<OrderRequest>,
//!     ) -> Result<Response<OrderResponse>, Status> {
//!         // Check circuit breaker
//!         if !self.circuit_breaker.can_execute().await {
//!             return Err(Status::unavailable("Service circuit breaker open"));
//!         }
//!
//!         // Execute with bounded concurrency
//!         let result = self.executor.execute(|| async {
//!             // Retry logic for transient failures
//!             retry_with_backoff(
//!                 || async {
//!                     // Your business logic here
//!                     self.process_order(request.get_ref()).await
//!                 },
//!                 RetryConfig {
//!                     max_retries: 3,
//!                     base_delay: Duration::from_millis(100),
//!                     max_delay: Duration::from_secs(10),
//!                 },
//!             ).await
//!         }).await;
//!
//!         // Update circuit breaker state
//!         match &result {
//!             Ok(_) => self.circuit_breaker.record_success().await,
//!             Err(_) => self.circuit_breaker.record_failure().await,
//!         }
//!
//!         // Convert to gRPC response
//!         result
//!             .map(|r| Response::new(r))
//!             .map_err(|e| match e {
//!                 RetryError::MaxRetriesExceeded { last_error, .. } => {
//!                     Status::internal(format!("Failed after retries: {}", last_error))
//!                 }
//!                 RetryError::NonRetryableError(err) => {
//!                     Status::invalid_argument(err.to_string())
//!                 }
//!             })
//!     }
//! }
//! ```
//!
//! # Example: Asset Parser with Validation and Bounded Concurrency
//!
//! ```rust,ignore
//! use regex::Regex;
//! use common::resilience::BoundedExecutor;
//!
//! pub struct AssetParser {
//!     executor: BoundedExecutor,
//!     asset_regex: Regex,
//! }
//!
//! impl AssetParser {
//!     pub fn new() -> Self {
//!         Self {
//!             executor: BoundedExecutor::new(10),
//!             // Match ES.FUT, NQ.FUT, etc.
//!             asset_regex: Regex::new(r"^[A-Z]{1,4}\.FUT$").unwrap(),
//!         }
//!     }
//!
//!     /// Validate asset format before processing
//!     pub fn validate_asset(&self, asset: &str) -> Result<(), CommonError> {
//!         if !self.asset_regex.is_match(asset) {
//!             return Err(CommonError::validation(
//!                 format!("Invalid asset format: {}. Expected format: XX.FUT", asset)
//!             ));
//!         }
//!         Ok(())
//!     }
//!
//!     /// Pre-flight checks before processing
//!     pub async fn preflight_check(&self, file_path: &str) -> Result<(), CommonError> {
//!         // Check file exists
//!         if !tokio::fs::try_exists(file_path).await.unwrap_or(false) {
//!             return Err(CommonError::validation(
//!                 format!("File not found: {}", file_path)
//!             ));
//!         }
//!
//!         // Check file permissions
//!         let metadata = tokio::fs::metadata(file_path)
//!             .await
//!             .map_err(|e| CommonError::validation(
//!                 format!("Cannot access file {}: {}", file_path, e)
//!             ))?;
//!
//!         if !metadata.is_file() {
//!             return Err(CommonError::validation(
//!                 format!("Path is not a file: {}", file_path)
//!             ));
//!         }
//!
//!         Ok(())
//!     }
//!
//!     /// Process asset with bounded concurrency
//!     pub async fn process_asset(
//!         &self,
//!         asset: &str,
//!         file_path: &str,
//!     ) -> Result<(), CommonError> {
//!         // Validate asset format
//!         self.validate_asset(asset)?;
//!
//!         // Pre-flight checks
//!         self.preflight_check(file_path).await?;
//!
//!         // Execute with bounded concurrency
//!         self.executor.execute(|| async {
//!             // Your processing logic here
//!             Ok::<_, CommonError>(())
//!         }).await
//!     }
//! }
//! ```

use crate::error::{CommonError, ErrorCategory};
use crate::resilience::{
    retry_with_backoff, BoundedExecutor, CircuitBreaker, CircuitBreakerConfig, RetryConfig,
    RetryError,
};
use std::time::Duration;

/// Helper function to execute an operation with full resilience patterns
///
/// This combines circuit breaker, retry logic, and bounded concurrency in a single call.
///
/// # Arguments
///
/// * `circuit_breaker` - Circuit breaker to use
/// * `executor` - Bounded executor for concurrency control
/// * `retry_config` - Retry configuration
/// * `operation` - The async operation to execute
///
/// # Example
///
/// ```rust,ignore
/// use common::resilience::integration_examples::execute_with_resilience;
///
/// let result = execute_with_resilience(
///     &circuit_breaker,
///     &executor,
///     RetryConfig::default(),
///     || async {
///         // Your operation
///         Ok(42)
///     },
/// ).await?;
/// ```
pub async fn execute_with_resilience<F, Fut, T>(
    circuit_breaker: &CircuitBreaker,
    executor: &BoundedExecutor,
    retry_config: RetryConfig,
    mut operation: F,
) -> Result<T, CommonError>
where
    F: FnMut() -> Fut,
    Fut: std::future::Future<Output = Result<T, CommonError>>,
{
    // Check circuit breaker
    if !circuit_breaker.can_execute().await {
        return Err(CommonError::service(
            ErrorCategory::Network,
            format!("Circuit breaker '{}' is open", circuit_breaker.name()),
        ));
    }

    // Execute with bounded concurrency and retry
    let result = executor
        .execute(|| retry_with_backoff(&mut operation, retry_config))
        .await;

    // Update circuit breaker state
    match &result {
        Ok(_) => circuit_breaker.record_success().await,
        Err(_) => circuit_breaker.record_failure().await,
    }

    // Convert RetryError to CommonError
    result.map_err(|e| match e {
        RetryError::MaxRetriesExceeded { last_error, .. } => last_error,
        RetryError::NonRetryableError(err) => err,
    })
}

/// Example resilience configuration for different service types
pub mod presets {
    use super::*;

    /// Database operation resilience config
    pub fn database() -> (CircuitBreakerConfig, RetryConfig) {
        let cb_config = CircuitBreakerConfig {
            failure_threshold: 5,
            timeout: Duration::from_secs(30),
            success_threshold: 2,
        };

        let retry_config = RetryConfig {
            max_retries: 3,
            base_delay: Duration::from_millis(100),
            max_delay: Duration::from_secs(10),
            ..Default::default()
        };

        (cb_config, retry_config)
    }

    /// External API call resilience config
    pub fn external_api() -> (CircuitBreakerConfig, RetryConfig) {
        let cb_config = CircuitBreakerConfig {
            failure_threshold: 3,
            timeout: Duration::from_secs(60),
            success_threshold: 2,
        };

        let retry_config = RetryConfig {
            max_retries: 5,
            base_delay: Duration::from_millis(200),
            max_delay: Duration::from_secs(30),
            ..Default::default()
        };

        (cb_config, retry_config)
    }

    /// Internal microservice call resilience config
    pub fn internal_service() -> (CircuitBreakerConfig, RetryConfig) {
        let cb_config = CircuitBreakerConfig {
            failure_threshold: 10,
            timeout: Duration::from_secs(20),
            success_threshold: 3,
        };

        let retry_config = RetryConfig {
            max_retries: 3,
            base_delay: Duration::from_millis(50),
            max_delay: Duration::from_secs(5),
            ..Default::default()
        };

        (cb_config, retry_config)
    }

    /// Critical operation resilience config (stricter)
    pub fn critical() -> (CircuitBreakerConfig, RetryConfig) {
        let cb_config = CircuitBreakerConfig {
            failure_threshold: 2,
            timeout: Duration::from_secs(10),
            success_threshold: 5,
        };

        let retry_config = RetryConfig {
            max_retries: 5,
            base_delay: Duration::from_millis(500),
            max_delay: Duration::from_secs(60),
            ..Default::default()
        };

        (cb_config, retry_config)
    }
}

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

    #[tokio::test]
    async fn test_execute_with_resilience_success() {
        let cb_config = CircuitBreakerConfig::default();
        let circuit_breaker = CircuitBreaker::new("test", cb_config);
        let executor = BoundedExecutor::new(5);

        let result = execute_with_resilience(
            &circuit_breaker,
            &executor,
            RetryConfig::default(),
            || async { Ok::<_, CommonError>(42) },
        )
        .await;

        assert_eq!(result.unwrap(), 42);
    }

    #[tokio::test]
    async fn test_presets() {
        let (cb_config, retry_config) = presets::database();
        assert_eq!(cb_config.failure_threshold, 5);
        assert_eq!(retry_config.max_retries, 3);

        let (cb_config, retry_config) = presets::external_api();
        assert_eq!(cb_config.failure_threshold, 3);
        assert_eq!(retry_config.max_retries, 5);
    }
}
