//! Common error types and utilities
//!
//! This module provides shared error types and utilities used across
//! all Foxhunt services.

use serde::{Deserialize, Serialize};
use std::fmt;
use std::time::Duration;
use thiserror::Error;

/// Common error type for all Foxhunt services
#[derive(Debug, Error)]
pub enum CommonError {
    /// Database operation failed - wraps database-specific errors
    #[error("Database error: {0}")]
    Database(#[from] crate::database::DatabaseError),
    /// Configuration is invalid or missing required parameters
    #[error("Configuration error: {0}")]
    Configuration(String),
    /// Network communication error occurred
    #[error("Network error: {0}")]
    Network(String),
    /// Service-specific error with categorization for metrics
    #[error("Service error: {category} - {message}")]
    Service {
        /// Error category for classification
        category: ErrorCategory,
        /// Descriptive error message
        message: String,
    },
    /// Input validation failed
    #[error("Validation error: {0}")]
    Validation(String),
    /// Operation exceeded maximum allowed execution time
    #[error("Timeout error: operation took {actual_ms}ms, max allowed {max_ms}ms")]
    Timeout {
        /// Actual execution time in milliseconds
        actual_ms: u64,
        /// Maximum allowed execution time in milliseconds
        max_ms: u64,
    },
}

/// Error categories for classification and metrics
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[allow(clippy::module_name_repetitions)]
pub enum ErrorCategory {
    /// Market data related errors
    MarketData,
    /// Trading and order management errors
    Trading,
    /// Network and communication errors
    Network,
    /// System and infrastructure errors
    System,
    /// Configuration errors
    Configuration,
    /// Validation errors
    Validation,
    /// Critical errors requiring immediate attention
    Critical,
    /// Connection errors (data providers)
    Connection,
    /// Authentication errors
    Authentication,
    /// Rate limiting errors
    RateLimit,
    /// Data parsing errors
    Parse,
    /// Subscription errors
    Subscription,
    /// Financial safety and calculation errors
    FinancialSafety,
    /// Risk management and circuit breakers
    RiskManagement,
    /// Database and persistence layer
    Database,
    /// Broker connectivity and execution
    Broker,
    /// Machine learning and AI errors
    MachineLearning,
    /// Security and authentication errors
    Security,
    /// Business logic errors
    BusinessLogic,
    /// Resource errors (not found, conflicts)
    Resource,
    /// Development and testing errors
    Development,
    /// Risk management errors
    Risk,
    /// Machine learning errors (alias for `MachineLearning`)
    ML,
    /// Unknown/other errors
    Other,
}

impl fmt::Display for ErrorCategory {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::MarketData => write!(f, "MARKET_DATA"),
            Self::Trading => write!(f, "TRADING"),
            Self::Network => write!(f, "NETWORK"),
            Self::System => write!(f, "SYSTEM"),
            Self::Configuration => write!(f, "CONFIGURATION"),
            Self::Validation => write!(f, "VALIDATION"),
            Self::Critical => write!(f, "CRITICAL"),
            Self::Connection => write!(f, "CONNECTION"),
            Self::Authentication => write!(f, "AUTHENTICATION"),
            Self::RateLimit => write!(f, "RATE_LIMIT"),
            Self::Parse => write!(f, "PARSE"),
            Self::Subscription => write!(f, "SUBSCRIPTION"),
            Self::FinancialSafety => write!(f, "FINANCIAL_SAFETY"),
            Self::RiskManagement => write!(f, "RISK_MANAGEMENT"),
            Self::Database => write!(f, "DATABASE"),
            Self::Broker => write!(f, "BROKER"),
            Self::MachineLearning => write!(f, "MACHINE_LEARNING"),
            Self::Security => write!(f, "SECURITY"),
            Self::BusinessLogic => write!(f, "BUSINESS_LOGIC"),
            Self::Resource => write!(f, "RESOURCE"),
            Self::Development => write!(f, "DEVELOPMENT"),
            Self::Risk => write!(f, "RISK"),
            Self::ML => write!(f, "ML"),
            Self::Other => write!(f, "OTHER"),
        }
    }
}

/// Error severity levels for prioritization and alerting
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[allow(clippy::module_name_repetitions)]
pub enum ErrorSeverity {
    /// Debug level - for development and troubleshooting
    Debug,
    /// Low severity - informational errors (alias for Info)
    Low,
    /// Info level - informational messages
    Info,
    /// Medium severity - recoverable errors (alias for Warn)
    Medium,
    /// Warning level - potentially problematic situations
    Warn,
    /// High severity - significant errors requiring attention (alias for Error)
    High,
    /// Error level - error conditions that should be addressed
    Error,
    /// Critical level - serious error conditions requiring immediate attention
    Critical,
}

impl fmt::Display for ErrorSeverity {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Debug => write!(f, "DEBUG"),
            Self::Low => write!(f, "LOW"),
            Self::Info => write!(f, "INFO"),
            Self::Medium => write!(f, "MEDIUM"),
            Self::Warn => write!(f, "WARN"),
            Self::High => write!(f, "HIGH"),
            Self::Error => write!(f, "ERROR"),
            Self::Critical => write!(f, "CRITICAL"),
        }
    }
}

/// Retry strategies for error recovery
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum RetryStrategy {
    /// Do not retry - error is permanent
    NoRetry,
    /// Retry immediately without delay
    Immediate,
    /// Linear backoff with fixed intervals
    Linear {
        /// Base delay in milliseconds between retries
        base_delay_ms: u64,
    },
    /// Exponential backoff with jitter
    Exponential {
        /// Base delay in milliseconds for exponential backoff
        base_delay_ms: u64,
        /// Maximum delay cap in milliseconds
        max_delay_ms: u64,
    },
    /// Wait for circuit breaker to close
    CircuitBreaker,
}

impl RetryStrategy {
    /// Calculate delay for retry attempt
    #[must_use]
    pub fn calculate_delay(&self, attempt: u32) -> Option<Duration> {
        match self {
            Self::NoRetry => None,
            Self::Immediate => Some(Duration::from_millis(0)),
            Self::Linear { base_delay_ms } => Some(Duration::from_millis(
                base_delay_ms.saturating_mul(u64::from(attempt)),
            )),
            Self::Exponential {
                base_delay_ms,
                max_delay_ms,
            } => {
                let delay_ms = base_delay_ms.saturating_mul(2_u64.saturating_pow(attempt.min(10)));
                let capped_delay = delay_ms.min(*max_delay_ms);

                // Add simple jitter (±10%)
                #[allow(clippy::integer_division)]
                let jitter_ms = capped_delay / 10;
                #[allow(clippy::integer_division)]
                let final_delay = capped_delay.saturating_sub(jitter_ms / 2);

                Some(Duration::from_millis(final_delay))
            },
            Self::CircuitBreaker => Some(Duration::from_secs(30)),
        }
    }

    /// Get maximum recommended retry attempts
    #[must_use]
    pub const fn max_attempts(&self) -> Option<u32> {
        match self {
            Self::NoRetry => Some(0),
            Self::Immediate => Some(3),
            Self::Linear { .. } => Some(5),
            Self::Exponential { .. } => Some(7),
            Self::CircuitBreaker => Some(1),
        }
    }
}

/// Convenience functions for creating common errors
impl CommonError {
    /// Create a configuration error
    pub fn config<S: Into<String>>(message: S) -> Self {
        Self::Configuration(message.into())
    }

    /// Create a network error
    pub fn network<S: Into<String>>(message: S) -> Self {
        Self::Network(message.into())
    }

    /// Create a service error with category
    pub fn service<S: Into<String>>(category: ErrorCategory, message: S) -> Self {
        Self::Service {
            category,
            message: message.into(),
        }
    }

    /// Create a validation error
    pub fn validation<S: Into<String>>(message: S) -> Self {
        Self::Validation(message.into())
    }

    /// Create a timeout error
    pub const fn timeout(actual_ms: u64, max_ms: u64) -> Self {
        Self::Timeout { actual_ms, max_ms }
    }

    /// Create a machine learning specific service error
    pub fn ml<S: Into<String>, M: Into<String>>(model_name: S, message: M) -> Self {
        Self::Service {
            category: ErrorCategory::MachineLearning,
            message: format!("{}: {}", model_name.into(), message.into()),
        }
    }

    /// Create a serialization error
    pub fn serialization<S: Into<String>>(message: S) -> Self {
        Self::Service {
            category: ErrorCategory::Parse,
            message: format!("Serialization error: {}", message.into()),
        }
    }

    /// Create an internal error
    pub fn internal<S: Into<String>>(message: S) -> Self {
        Self::Service {
            category: ErrorCategory::System,
            message: format!("Internal error: {}", message.into()),
        }
    }

    /// Create a resource exhausted error
    pub fn resource_exhausted<S: Into<String>>(resource: S) -> Self {
        Self::Service {
            category: ErrorCategory::Resource,
            message: format!("Resource exhausted: {}", resource.into()),
        }
    }

    /// Get the error category for classification and metrics
    pub const fn category(&self) -> ErrorCategory {
        match self {
            Self::Database(_) => ErrorCategory::Database,
            Self::Configuration(_) => ErrorCategory::Configuration,
            Self::Network(_) => ErrorCategory::Network,
            Self::Service { category, .. } => *category,
            Self::Validation(_) => ErrorCategory::Validation,
            Self::Timeout { .. } => ErrorCategory::System,
        }
    }

    /// Get error severity level
    pub const fn severity(&self) -> ErrorSeverity {
        match self {
            Self::Database(_) => ErrorSeverity::Critical,
            Self::Configuration(_) => ErrorSeverity::Critical,
            Self::Network(_) => ErrorSeverity::Error,
            Self::Service { category, .. } => match category {
                ErrorCategory::Critical
                | ErrorCategory::FinancialSafety
                | ErrorCategory::Authentication => ErrorSeverity::Critical,
                ErrorCategory::Trading
                | ErrorCategory::RiskManagement
                | ErrorCategory::Database => ErrorSeverity::Error,
                _ => ErrorSeverity::Warn,
            },
            Self::Validation(_) => ErrorSeverity::Warn,
            Self::Timeout { .. } => ErrorSeverity::Error,
        }
    }

    /// Check if the error is retryable
    pub const fn is_retryable(&self) -> bool {
        match self {
            Self::Database(_) => true,       // Database operations can be retried
            Self::Configuration(_) => false, // Configuration errors are permanent
            Self::Network(_) => true,        // Network errors are often transient
            Self::Service { category, .. } => !matches!(
                category,
                ErrorCategory::Authentication
                    | ErrorCategory::Configuration
                    | ErrorCategory::Validation
            ),
            Self::Validation(_) => false, // Validation errors are permanent
            Self::Timeout { .. } => true, // Timeouts can be retried
        }
    }

    /// Get retry strategy for this error
    pub const fn retry_strategy(&self) -> RetryStrategy {
        if !self.is_retryable() {
            return RetryStrategy::NoRetry;
        }

        match self {
            Self::Database(_) => RetryStrategy::Exponential {
                base_delay_ms: 1000,
                max_delay_ms: 10000,
            },
            Self::Network(_) => RetryStrategy::Linear { base_delay_ms: 500 },
            Self::Service { category, .. } => match category {
                ErrorCategory::Network | ErrorCategory::Connection => {
                    RetryStrategy::Linear { base_delay_ms: 500 }
                },
                ErrorCategory::RateLimit => RetryStrategy::Exponential {
                    base_delay_ms: 5000,
                    max_delay_ms: 60000,
                },
                _ => RetryStrategy::Immediate,
            },
            Self::Timeout { .. } => RetryStrategy::Linear {
                base_delay_ms: 1000,
            },
            _ => RetryStrategy::NoRetry,
        }
    }
}

/// Result type for common operations
pub type CommonResult<T> = Result<T, CommonError>;
