//! Comprehensive retry mechanisms with exponential backoff for HFT systems
//!
//! This module provides sophisticated retry strategies optimized for high-frequency trading
//! environments, including exponential backoff with jitter, circuit breaker integration,
//! and latency-aware retry policies.

use crate::types::circuit_breaker::CircuitBreaker;
use crate::types::errors::{ErrorSeverity, FoxhuntError, FoxhuntResult};
use rand::Rng;
use serde::{Deserialize, Serialize};
use std::time::{Duration, Instant};
use tokio::time::sleep;
use tracing::{debug, error, info, warn};

/// Retry policy configuration with `HFT`-optimized defaults
#[derive(Debug, Clone, Serialize, Deserialize)]
/// RetryPolicy
/// 
/// Auto-generated documentation placeholder - enhance with specifics
pub struct RetryPolicy {
    /// Maximum number of retry attempts
    pub max_attempts: u32,
    /// Initial delay before first retry
    pub initial_delay: Duration,
    /// Maximum delay between retries
    pub max_delay: Duration,
    /// Backoff multiplier (typically 2.0 for exponential backoff)
    pub backoff_multiplier: f64,
    /// Jitter factor to prevent thundering herd (0.0 to 1.0)
    pub jitter_factor: f64,
    /// Maximum total retry duration
    pub max_total_duration: Duration,
    /// Whether to use circuit breaker integration
    pub use_circuit_breaker: bool,
    /// Errors that should not be retried
    pub non_retryable_errors: Vec<String>,
}

impl Default for RetryPolicy {
    fn default() -> Self {
        Self::hft_optimized()
    }
}

impl RetryPolicy {
    /// `HFT`-optimized retry policy with aggressive timeouts
    #[must_use]
    pub fn hft_optimized() -> Self {
        Self {
            max_attempts: 3,
            initial_delay: Duration::from_millis(10),
            max_delay: Duration::from_millis(500),
            backoff_multiplier: 2.0,
            jitter_factor: 0.1,
            max_total_duration: Duration::from_secs(2),
            use_circuit_breaker: true,
            non_retryable_errors: vec![
                "AuthenticationError".to_owned(),
                "AuthorizationError".to_owned(),
                "ValidationError".to_owned(),
                "ConfigurationError".to_owned(),
            ],
        }
    }

    /// Conservative retry policy for critical financial operations
    #[must_use]
    pub fn financial_conservative() -> Self {
        Self {
            max_attempts: 5,
            initial_delay: Duration::from_millis(100),
            max_delay: Duration::from_secs(5),
            backoff_multiplier: 1.5,
            jitter_factor: 0.2,
            max_total_duration: Duration::from_secs(30),
            use_circuit_breaker: true,
            non_retryable_errors: vec![
                "AuthenticationError".to_owned(),
                "AuthorizationError".to_owned(),
                "ValidationError".to_owned(),
                "ConfigurationError".to_owned(),
                "FinancialSafety".to_owned(),
            ],
        }
    }

    /// Aggressive retry policy for market data operations
    #[must_use]
    pub fn market_data_aggressive() -> Self {
        Self {
            max_attempts: 10,
            initial_delay: Duration::from_millis(5),
            max_delay: Duration::from_millis(200),
            backoff_multiplier: 1.8,
            jitter_factor: 0.05,
            max_total_duration: Duration::from_secs(10),
            use_circuit_breaker: true,
            non_retryable_errors: vec![
                "AuthenticationError".to_owned(),
                "ValidationError".to_owned(),
            ],
        }
    }

    /// Database operation retry policy with longer timeouts
    #[must_use]
    pub fn database_operations() -> Self {
        Self {
            max_attempts: 5,
            initial_delay: Duration::from_millis(50),
            max_delay: Duration::from_secs(10),
            backoff_multiplier: 2.5,
            jitter_factor: 0.3,
            max_total_duration: Duration::from_secs(60),
            use_circuit_breaker: true,
            non_retryable_errors: vec![
                "AuthenticationError".to_owned(),
                "ValidationError".to_owned(),
                "ConfigurationError".to_owned(),
            ],
        }
    }

    /// Network operation retry policy
    #[must_use]
    pub fn network_operations() -> Self {
        Self {
            max_attempts: 7,
            initial_delay: Duration::from_millis(25),
            max_delay: Duration::from_secs(3),
            backoff_multiplier: 2.2,
            jitter_factor: 0.15,
            max_total_duration: Duration::from_secs(20),
            use_circuit_breaker: true,
            non_retryable_errors: vec![
                "AuthenticationError".to_owned(),
                "AuthorizationError".to_owned(),
                "ValidationError".to_owned(),
            ],
        }
    }

    /// Calculate delay for a specific attempt with jitter
    #[must_use]
    pub fn calculate_delay(&self, attempt: u32) -> Duration {
        if attempt == 0 {
            return Duration::ZERO;
        }

        let delay = self.initial_delay.as_millis() as f64
            * self.backoff_multiplier.powi((attempt - 1) as i32);

        let delay = Duration::from_millis(delay as u64);
        let delay = std::cmp::min(delay, self.max_delay);

        // Add jitter to prevent thundering herd
        if self.jitter_factor > 0.0 {
            let mut rng = rand::thread_rng();
            let jitter_range = delay.as_millis() as f64 * self.jitter_factor;
            let jitter = rng.gen_range(-jitter_range..=jitter_range);
            let jittered_delay = delay.as_millis() as f64 + jitter;
            Duration::from_millis(jittered_delay.max(0.0) as u64)
        } else {
            delay
        }
    }

    /// Check if an error should be retried
    #[must_use]
    pub fn should_retry(&self, error: &FoxhuntError) -> bool {
        // Check if error type is in non-retryable list
        let error_name = format!("{error:?}")
            .split(' ')
            .next()
            .unwrap_or("")
            .to_owned();
        if self.non_retryable_errors.contains(&error_name) {
            return false;
        }

        // Don't retry critical errors by default
        if error.severity() == ErrorSeverity::Critical {
            return false;
        }

        // Check specific error types
        match error {
            FoxhuntError::FinancialSafety { .. } => false,
            FoxhuntError::Authentication { .. } => false,
            FoxhuntError::Authorization { .. } => false,
            FoxhuntError::Validation { .. } => false,
            FoxhuntError::Configuration { .. } => false,
            _ => true,
        }
    }
}

/// Retry context for tracking retry attempts and timing
#[derive(Debug)]
/// RetryContext
/// 
/// Auto-generated documentation placeholder - enhance with specifics
pub struct RetryContext {
    /// Attempt
    pub attempt: u32,
    /// Start Time
    pub start_time: Instant,
    /// Last Error
    pub last_error: Option<FoxhuntError>,
    /// Total Delay
    pub total_delay: Duration,
}

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

impl RetryContext {
    #[must_use]
    pub fn new() -> Self {
        Self {
            attempt: 0,
            start_time: Instant::now(),
            last_error: None,
            total_delay: Duration::ZERO,
        }
    }

    #[must_use]
    pub fn elapsed(&self) -> Duration {
        self.start_time.elapsed()
    }

    #[must_use]
    pub fn should_continue(&self, policy: &RetryPolicy) -> bool {
        self.attempt < policy.max_attempts && self.elapsed() < policy.max_total_duration
    }
}

/// Retry executor with circuit breaker integration
pub struct RetryExecutor {
    policy: RetryPolicy,
    circuit_breaker: Option<CircuitBreaker>,
    service_name: String,
}

impl RetryExecutor {
    #[must_use]
    pub const fn new(service_name: String, policy: RetryPolicy) -> Self {
        Self {
            policy,
            circuit_breaker: None,
            service_name,
        }
    }

    pub fn with_circuit_breaker(mut self, circuit_breaker: CircuitBreaker) -> Self {
        self.circuit_breaker = Some(circuit_breaker);
        self
    }

    /// Execute operation with retry logic
    pub async fn execute<F, Fut, T>(&self, operation: F) -> FoxhuntResult<T>
    where
        F: Fn() -> Fut,
        Fut: std::future::Future<Output = FoxhuntResult<T>>,
    {
        let mut context = RetryContext::new();

        loop {
            context.attempt += 1;

            // Check circuit breaker before attempting operation
            if let Some(ref cb) = self.circuit_breaker {
                if self.policy.use_circuit_breaker && cb.is_open().await {
                    return Err(FoxhuntError::CircuitBreakerOpen {
                        service: self.service_name.clone(),
                        message: "Circuit breaker is open, operation not attempted".to_owned(),
                        context: Some(format!(
                            "Attempt {} of {}",
                            context.attempt, self.policy.max_attempts
                        )),
                    });
                }
            }

            debug!(
                service = %self.service_name,
                attempt = context.attempt,
                max_attempts = self.policy.max_attempts,
                elapsed = ?context.elapsed(),
                "Executing operation with retry"
            );

            // Execute the operation
            let result = operation().await;

            match result {
                Ok(value) => {
                    // Record success in circuit breaker
                    if let Some(ref cb) = self.circuit_breaker {
                        cb.record_success().await;
                    }

                    if context.attempt > 1 {
                        info!(
                            service = %self.service_name,
                            attempt = context.attempt,
                            total_delay = ?context.total_delay,
                            elapsed = ?context.elapsed(),
                            "Operation succeeded after retry"
                        );
                    }

                    return Ok(value);
                }
                Err(error) => {
                    // Record failure in circuit breaker
                    if let Some(ref cb) = self.circuit_breaker {
                        cb.record_failure(&error).await;
                    }

                    context.last_error = Some(error.clone());

                    // Check if we should retry this error
                    if !self.policy.should_retry(&error) {
                        warn!(
                            service = %self.service_name,
                            error = ?error,
                            attempt = context.attempt,
                            "Error is not retryable, failing immediately"
                        );
                        return Err(error);
                    }

                    // Check if we should continue retrying
                    if !context.should_continue(&self.policy) {
                        error!(
                            service = %self.service_name,
                            error = ?error,
                            attempts = context.attempt,
                            max_attempts = self.policy.max_attempts,
                            elapsed = ?context.elapsed(),
                            max_duration = ?self.policy.max_total_duration,
                            "Retry limit exceeded, failing operation"
                        );

                        return Err(FoxhuntError::RetryExhausted {
                            service: self.service_name.clone(),
                            attempts: context.attempt,
                            last_error_description: error.to_string(),
                            elapsed: context.elapsed(),
                        });
                    }

                    // Calculate delay and wait before next attempt
                    let delay = self.policy.calculate_delay(context.attempt);
                    context.total_delay += delay;

                    warn!(
                        service = %self.service_name,
                        error = ?error,
                        attempt = context.attempt,
                        max_attempts = self.policy.max_attempts,
                        delay = ?delay,
                        next_attempt = context.attempt + 1,
                        "Operation failed, retrying after delay"
                    );

                    if delay > Duration::ZERO {
                        sleep(delay).await;
                    }
                }
            }
        }
    }

    /// Execute operation with custom retry predicate
    pub async fn execute_with_predicate<F, Fut, T, P>(
        &self,
        operation: F,
        should_retry: P,
    ) -> FoxhuntResult<T>
    where
        F: Fn() -> Fut,
        Fut: std::future::Future<Output = FoxhuntResult<T>>,
        P: Fn(&FoxhuntError) -> bool,
    {
        let mut context = RetryContext::new();

        loop {
            context.attempt += 1;

            // Check circuit breaker before attempting operation
            if let Some(ref cb) = self.circuit_breaker {
                if self.policy.use_circuit_breaker && cb.is_open().await {
                    return Err(FoxhuntError::CircuitBreakerOpen {
                        service: self.service_name.clone(),
                        message: "Circuit breaker is open, operation not attempted".to_owned(),
                        context: Some(format!(
                            "Attempt {} of {}",
                            context.attempt, self.policy.max_attempts
                        )),
                    });
                }
            }

            let result = operation().await;

            match result {
                Ok(value) => {
                    if let Some(ref cb) = self.circuit_breaker {
                        cb.record_success().await;
                    }
                    return Ok(value);
                }
                Err(error) => {
                    if let Some(ref cb) = self.circuit_breaker {
                        cb.record_failure(&error).await;
                    }

                    context.last_error = Some(error.clone());

                    // Use custom retry predicate
                    if !should_retry(&error) {
                        return Err(error);
                    }

                    if !context.should_continue(&self.policy) {
                        return Err(FoxhuntError::RetryExhausted {
                            service: self.service_name.clone(),
                            attempts: context.attempt,
                            last_error_description: error.to_string(),
                            elapsed: context.elapsed(),
                        });
                    }

                    let delay = self.policy.calculate_delay(context.attempt);
                    context.total_delay += delay;

                    if delay > Duration::ZERO {
                        sleep(delay).await;
                    }
                }
            }
        }
    }
}

/// Convenience macro for creating retry executors
#[macro_export]
macro_rules! retry_executor {
    ($service:expr, $policy:expr) => {
        $crate::retry::RetryExecutor::new($service.to_string(), $policy)
    };
    ($service:expr, $policy:expr, $circuit_breaker:expr) => {
        $crate::retry::RetryExecutor::new($service.to_string(), $policy)
            .with_circuit_breaker($circuit_breaker)
    };
}

/// Convenience function for simple retry operations
pub async fn retry_operation<F, Fut, T>(
    service_name: &str,
    operation: F,
    policy: RetryPolicy,
) -> FoxhuntResult<T>
where
    F: Fn() -> Fut,
    Fut: std::future::Future<Output = FoxhuntResult<T>>,
{
    let executor = RetryExecutor::new(service_name.to_owned(), policy);
    executor.execute(operation).await
}

/// Convenience function for retry with circuit breaker
pub async fn retry_with_circuit_breaker<F, Fut, T>(
    service_name: &str,
    operation: F,
    policy: RetryPolicy,
    circuit_breaker: CircuitBreaker,
) -> FoxhuntResult<T>
where
    F: Fn() -> Fut,
    Fut: std::future::Future<Output = FoxhuntResult<T>>,
{
    let executor =
        RetryExecutor::new(service_name.to_owned(), policy).with_circuit_breaker(circuit_breaker);
    executor.execute(operation).await
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::sync::atomic::{AtomicU32, Ordering};
    use std::sync::Arc;

    #[tokio::test]
    async fn test_retry_policy_calculation() {
        let policy = RetryPolicy::hft_optimized();

        // Test delay calculation
        assert_eq!(policy.calculate_delay(0), Duration::ZERO);

        let delay1 = policy.calculate_delay(1);
        let delay2 = policy.calculate_delay(2);
        let delay3 = policy.calculate_delay(3);

        // Should be increasing (with potential jitter)
        assert!(delay1 >= policy.initial_delay);
        assert!(
            delay2 >= delay1 || (delay2.as_millis() as f64) >= (delay1.as_millis() as f64 * 0.8)
        );
        assert!(
            delay3 >= delay2 || (delay3.as_millis() as f64) >= (delay2.as_millis() as f64 * 0.8)
        );
    }

    #[tokio::test]
    async fn test_successful_operation() {
        let executor = RetryExecutor::new("test".to_string(), RetryPolicy::hft_optimized());

        let result = executor
            .execute(|| async { Ok::<i32, FoxhuntError>(42) })
            .await;

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

    #[tokio::test]
    async fn test_retry_on_failure() {
        let attempt_count = Arc::new(AtomicU32::new(0));
        let attempt_count_clone = attempt_count.clone();

        let executor = RetryExecutor::new("test".to_string(), RetryPolicy::hft_optimized());

        let result = executor
            .execute(move || {
                let count = attempt_count_clone.fetch_add(1, Ordering::SeqCst) + 1;
                async move {
                    if count < 3 {
                        Err(FoxhuntError::Network {
                            reason: "Temporary failure".to_string(),
                            endpoint: None,
                            operation: None,
                            source_description: None,
                        })
                    } else {
    // Ok variant
                        Ok(42)
                    }
                }
            })
            .await;

        assert!(result.is_ok());
        assert_eq!(result.unwrap(), 42);
        assert_eq!(attempt_count.load(Ordering::SeqCst), 3);
    }

    #[tokio::test]
    async fn test_non_retryable_error() {
        let attempt_count = Arc::new(AtomicU32::new(0));
        let attempt_count_clone = attempt_count.clone();

        let executor = RetryExecutor::new("test".to_string(), RetryPolicy::hft_optimized());

        let result = executor
            .execute(move || {
                attempt_count_clone.fetch_add(1, Ordering::SeqCst);
                async move {
                    Err::<i32, FoxhuntError>(FoxhuntError::Authentication {
                        reason: "Invalid credentials".to_string(),
                        method: None,
                        user_id: None,
                    })
                }
            })
            .await;

        assert!(result.is_err());
        assert_eq!(attempt_count.load(Ordering::SeqCst), 1); // Should not retry
    }

    #[tokio::test]
    async fn test_retry_exhaustion() {
        let attempt_count = Arc::new(AtomicU32::new(0));
        let attempt_count_clone = attempt_count.clone();

        let mut policy = RetryPolicy::hft_optimized();
        policy.max_attempts = 2;
        policy.initial_delay = Duration::from_millis(1); // Speed up test

        let executor = RetryExecutor::new("test".to_string(), policy);

        let result = executor
            .execute(move || {
                attempt_count_clone.fetch_add(1, Ordering::SeqCst);
                async move {
                    Err::<i32, FoxhuntError>(FoxhuntError::Network {
                        reason: "Persistent failure".to_string(),
                        endpoint: None,
                        operation: None,
                        source_description: None,
                    })
                }
            })
            .await;

        assert!(result.is_err());
        assert_eq!(attempt_count.load(Ordering::SeqCst), 2);

        if let Err(FoxhuntError::RetryExhausted { attempts, .. }) = result {
            assert_eq!(attempts, 2);
        } else {
            panic!("Expected RetryExhausted error");
        }
    }
}
