//! Error types for the data module

pub use common::error::ErrorSeverity;
use thiserror::Error;

/// Result type alias for data module operations
pub type Result<T> = std::result::Result<T, DataError>;

/// Data module error types
#[derive(Error, Debug)]
pub enum DataError {
    /// Network connectivity errors
    #[error("Network error: {message}")]
    Network {
        /// Error message
        message: String,
    },

    /// `FIX` protocol errors
    #[error("FIX protocol error: {message}")]
    FixProtocol {
        /// Error message
        message: String,
    },

    /// Authentication errors
    #[error("Authentication error: {message}")]
    Authentication {
        /// Error message
        message: String,
    },

    /// Configuration errors
    #[error("Configuration error in field '{field}': {message}")]
    Configuration {
        /// Field name with configuration error
        field: String,
        /// Error message
        message: String,
    },

    /// Message parsing errors
    #[error("Message parsing error: {message}")]
    MessageParsing {
        /// Error message
        message: String,
    },

    /// Session management errors
    #[error("Session error: {message}")]
    Session {
        /// Error message
        message: String,
    },

    /// Order management errors
    #[error("Order error: {message}")]
    Order {
        /// Error message
        message: String,
    },

    /// Timeout errors
    #[error("Operation timed out: {message}")]
    Timeout {
        /// Error message
        message: String,
    },

    /// Parse errors
    #[error("Parse error: {message}")]
    Parse {
        /// Error message
        message: String,
    },

    /// Validation errors (consolidated)
    #[error("Validation error in field '{field}': {message}")]
    Validation {
        /// Field name with validation error
        field: String,
        /// Error message
        message: String,
    },

    /// Simple validation error
    #[error("Validation error: {0}")]
    ValidationSimple(String),

    /// Serialization errors (consolidated)
    #[error("Serialization error: {message}")]
    Serialization {
        /// Error message
        message: String,
    },

    /// Compression errors
    #[error("Compression error: {0}")]
    Compression(String),

    /// Storage errors
    #[error("Storage error: {0}")]
    Storage(String),

    /// Dataset not found
    #[error("Not found: {0}")]
    NotFound(String),

    /// Broker-specific errors
    #[error("Broker error: {message}")]
    Broker {
        /// Error message
        message: String,
    },

    /// Connection errors
    #[error("Connection error: {0}")]
    Connection(String),

    /// Subscription errors
    #[error("Subscription error: {message}")]
    Subscription {
        /// Error message
        message: String,
    },

    /// API errors
    #[error("API error: {message} (status: {status:?})")]
    Api {
        /// Error message
        message: String,
        /// HTTP status code if available
        status: Option<String>,
    },

    /// Invalid parameter errors
    #[error("Invalid parameter '{field}': {message}")]
    InvalidParameter {
        /// Parameter name
        field: String,
        /// Error message
        message: String,
    },

    /// Unsupported operation errors
    #[error("Unsupported operation: {0}")]
    Unsupported(String),

    /// Not implemented errors
    #[error("Not implemented: {0}")]
    NotImplemented(String),

    /// Initialization errors
    #[error("Initialization error: {0}")]
    Initialization(String),

    /// Invalid format errors
    #[error("Invalid format: {0}")]
    InvalidFormat(String),

    /// Conversion errors
    #[error("Conversion error: {0}")]
    Conversion(String),

    /// Rate limiting errors
    #[error("Rate limit exceeded")]
    RateLimit,

    // External error types with automatic From trait generation
    /// I/O errors
    #[error(transparent)]
    Io(#[from] std::io::Error),

    /// JSON serialization/deserialization errors
    #[error(transparent)]
    Json(#[from] serde_json::Error),

    /// HTTP client errors
    #[error(transparent)]
    Http(#[from] reqwest::Error),

    /// WebSocket errors
    #[error(transparent)]
    WebSocket(#[from] tungstenite::Error),

    /// Time parsing errors
    #[error(transparent)]
    Time(#[from] chrono::ParseError),

    /// URL parsing errors
    #[error(transparent)]
    Url(#[from] url::ParseError),

    /// Bincode serialization errors
    #[error("Bincode error: {0}")]
    Bincode(#[from] bincode::Error),

    /// `Parquet` file errors
    #[error("Parquet error: {0}")]
    Parquet(#[from] parquet::errors::ParquetError),

    /// `Arrow` data errors
    #[error("Arrow error: {0}")]
    Arrow(#[from] arrow::error::ArrowError),

    /// `Redis` cache errors
    #[cfg(feature = "redis-cache")]
    #[error("Redis error: {0}")]
    Redis(#[from] redis::RedisError),

    /// Generic errors with transparent forwarding
    #[error(transparent)]
    Generic(#[from] anyhow::Error),

    /// Trading engine errors
    #[error("Trading engine error: {0}")]
    TradingEngine(#[from] common::CommonTypeError),

    /// Configuration module errors
    #[error("Config error: {0}")]
    ConfigError(#[from] config::error::ConfigError),
}

// Display implementation is now automatically generated by thiserror

// Error trait and From trait implementations are now automatically generated by thiserror

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

    /// Create a `FIX` protocol error
    pub fn fix_protocol<S: Into<String>>(message: S) -> Self {
        Self::FixProtocol {
            message: message.into(),
        }
    }

    /// Create an authentication error
    pub fn authentication<S: Into<String>>(message: S) -> Self {
        Self::Authentication {
            message: message.into(),
        }
    }

    /// Create a configuration error
    pub fn configuration<F: Into<String>, M: Into<String>>(field: F, message: M) -> Self {
        Self::Configuration {
            field: field.into(),
            message: message.into(),
        }
    }

    /// Create a message parsing error
    pub fn message_parsing<S: Into<String>>(message: S) -> Self {
        Self::MessageParsing {
            message: message.into(),
        }
    }

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

    /// Create an order error
    pub fn order<S: Into<String>>(message: S) -> Self {
        Self::Order {
            message: message.into(),
        }
    }

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

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

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

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

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

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

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

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

    /// Create an initialization error
    pub fn initialization<S: Into<String>>(message: S) -> Self {
        Self::Initialization(message.into())
    }

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

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

    /// Create an API error
    pub fn api<S: Into<String>, T: Into<String>>(message: S, status: Option<T>) -> Self {
        Self::Api {
            message: message.into(),
            status: status.map(|s| s.into()),
        }
    }

    /// Check if error is retryable
    pub fn is_retryable(&self) -> bool {
        match self {
            Self::Network { .. } => true,
            Self::Timeout { .. } => true,
            Self::Connection(_) => true,
            Self::RateLimit => true,
            Self::Io(_) => true,
            Self::Http(_) => true,
            Self::WebSocket(_) => true,
            Self::Session { .. } => true,
            Self::Storage(_) => true, // Storage errors may be transient
            #[cfg(feature = "redis-cache")]
            Self::Redis(_) => true,
            _ => false,
        }
    }

    /// Get error severity level
    pub fn severity(&self) -> ErrorSeverity {
        match self {
            Self::Authentication { .. } => ErrorSeverity::Critical,
            Self::Configuration { .. } => ErrorSeverity::Critical,
            Self::FixProtocol { .. } => ErrorSeverity::High,
            Self::Order { .. } => ErrorSeverity::High,
            Self::Network { .. } => ErrorSeverity::Medium,
            Self::Session { .. } => ErrorSeverity::Medium,
            Self::Timeout { .. } => ErrorSeverity::Medium,
            Self::NotFound(_) => ErrorSeverity::Medium,
            Self::MessageParsing { .. } => ErrorSeverity::Low,
            Self::Broker { .. } => ErrorSeverity::Medium,
            _ => ErrorSeverity::Low,
        }
    }

    /// Get error category for monitoring
    pub fn category(&self) -> &'static str {
        match self {
            Self::Network { .. } => "NETWORK",
            Self::FixProtocol { .. } => "FIX_PROTOCOL",
            Self::Authentication { .. } => "AUTHENTICATION",
            Self::Configuration { .. } => "CONFIGURATION",
            Self::MessageParsing { .. } => "MESSAGE_PARSING",
            Self::Session { .. } => "SESSION",
            Self::Order { .. } => "ORDER",
            Self::Timeout { .. } => "TIMEOUT",
            Self::Parse { .. } => "PARSING",
            Self::Validation { .. } => "VALIDATION",
            Self::ValidationSimple(_) => "VALIDATION",
            Self::Serialization { .. } => "SERIALIZATION",
            Self::Compression(_) => "COMPRESSION",
            Self::Storage(_) => "STORAGE",
            Self::NotFound(_) => "NOT_FOUND",
            Self::Broker { .. } => "BROKER",
            Self::Connection(_) => "CONNECTION",
            Self::Subscription { .. } => "SUBSCRIPTION",
            Self::Api { .. } => "API",
            Self::InvalidParameter { .. } => "INVALID_PARAMETER",
            Self::Unsupported(_) => "UNSUPPORTED",
            Self::NotImplemented(_) => "NOT_IMPLEMENTED",
            Self::Initialization(_) => "INITIALIZATION",
            Self::InvalidFormat(_) => "INVALID_FORMAT",
            Self::Conversion(_) => "CONVERSION",
            Self::RateLimit => "RATE_LIMIT",
            Self::Io(_) => "IO",
            Self::Json(_) => "JSON",
            Self::Http(_) => "HTTP",
            Self::WebSocket(_) => "WEBSOCKET",
            Self::Time(_) => "TIME",
            Self::Url(_) => "URL",
            Self::Bincode(_) => "BINCODE",
            Self::Parquet(_) => "PARQUET",
            Self::Arrow(_) => "ARROW",
            #[cfg(feature = "redis-cache")]
            Self::Redis(_) => "REDIS",
            Self::Generic(_) => "GENERIC",
            Self::TradingEngine(_) => "TRADING_ENGINE",
            Self::ConfigError(_) => "CONFIG",
        }
    }
}

// ErrorSeverity is imported from common::error

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

    #[test]
    fn test_error_creation() {
        let error = DataError::network("Connection failed");
        assert!(matches!(error, DataError::Network { .. }));
        assert!(error.is_retryable());
        assert_eq!(error.severity(), ErrorSeverity::Medium);
        assert_eq!(error.category(), "NETWORK");
    }

    #[test]
    fn test_error_severity() {
        assert_eq!(
            DataError::authentication("Invalid credentials").severity(),
            ErrorSeverity::Critical
        );
        assert_eq!(
            DataError::timeout("Request timeout").severity(),
            ErrorSeverity::Medium
        );
    }

    #[test]
    fn test_retryable_errors() {
        assert!(DataError::network("test").is_retryable());
        assert!(DataError::timeout("test").is_retryable());
        assert!(!DataError::authentication("test").is_retryable());
        assert!(!DataError::configuration("field", "test").is_retryable());
    }

    #[test]
    fn test_automatic_from_conversions() {
        // Test that thiserror automatically converts external errors
        let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "test");
        let data_error: DataError = io_error.into();
        assert!(matches!(data_error, DataError::Io(_)));
        assert!(data_error.is_retryable());
        assert_eq!(data_error.category(), "IO");

        let json_error = serde_json::from_str::<i32>("invalid").unwrap_err();
        let data_error: DataError = json_error.into();
        assert!(matches!(data_error, DataError::Json(_)));
        assert_eq!(data_error.category(), "JSON");
    }

    #[test]
    fn test_new_error_variants() {
        let compression_error = DataError::compression("Failed to compress");
        assert!(matches!(compression_error, DataError::Compression(_)));
        assert_eq!(compression_error.category(), "COMPRESSION");

        let storage_error = DataError::storage("Storage failed");
        assert!(matches!(storage_error, DataError::Storage(_)));
        assert_eq!(storage_error.category(), "STORAGE");

        let rate_limit_error = DataError::RateLimit;
        assert!(matches!(rate_limit_error, DataError::RateLimit));
        assert!(rate_limit_error.is_retryable());
        assert_eq!(rate_limit_error.category(), "RATE_LIMIT");
    }

    #[test]
    fn test_consolidated_variants() {
        // Test that consolidated variants work properly
        let validation_error = DataError::validation("field", "invalid");
        assert!(matches!(validation_error, DataError::Validation { .. }));
        assert_eq!(validation_error.category(), "VALIDATION");

        let simple_validation_error = DataError::validation_simple("invalid");
        assert!(matches!(
            simple_validation_error,
            DataError::ValidationSimple(_)
        ));
        assert_eq!(simple_validation_error.category(), "VALIDATION");

        let api_error = DataError::api("API failed", Some("404"));
        assert!(matches!(api_error, DataError::Api { .. }));
        assert_eq!(api_error.category(), "API");
    }

    #[test]
    fn test_error_categories() {
        assert_eq!(DataError::network("test").category(), "NETWORK");
        assert_eq!(DataError::timeout("test").category(), "TIMEOUT");
        assert_eq!(DataError::parse("test").category(), "PARSING");
        assert_eq!(DataError::RateLimit.category(), "RATE_LIMIT");
        assert_eq!(
            DataError::authentication("test").category(),
            "AUTHENTICATION"
        );
        assert_eq!(
            DataError::NotFound("test".to_string()).category(),
            "NOT_FOUND"
        );
    }

    #[test]
    fn test_error_display() {
        let error = DataError::network("Connection refused");
        let display = format!("{}", error);
        assert!(display.contains("Network"));

        let error = DataError::validation("price", "must be positive");
        let display = format!("{}", error);
        assert!(display.contains("price"));
        assert!(display.contains("must be positive"));
    }

    #[test]
    fn test_non_retryable_errors() {
        assert!(!DataError::parse("test").is_retryable());
        assert!(!DataError::validation("field", "test").is_retryable());
        assert!(!DataError::ValidationSimple("test".to_string()).is_retryable());
        assert!(!DataError::NotFound("test".to_string()).is_retryable());
    }

    #[test]
    fn test_severity_levels() {
        assert_eq!(DataError::network("test").severity(), ErrorSeverity::Medium);
        assert_eq!(DataError::timeout("test").severity(), ErrorSeverity::Medium);
        assert_eq!(
            DataError::authentication("test").severity(),
            ErrorSeverity::Critical
        );
        assert_eq!(DataError::RateLimit.severity(), ErrorSeverity::Low);
        assert_eq!(DataError::parse("test").severity(), ErrorSeverity::Low);
    }

    #[test]
    fn test_severity_display() {
        assert_eq!(format!("{}", ErrorSeverity::Low), "LOW");
        assert_eq!(format!("{}", ErrorSeverity::Medium), "MEDIUM");
        assert_eq!(format!("{}", ErrorSeverity::High), "HIGH");
        assert_eq!(format!("{}", ErrorSeverity::Critical), "CRITICAL");
    }

    #[test]
    fn test_error_with_context() {
        let error = DataError::network("Connection failed");
        let error_string = format!("{}", error);
        assert!(error_string.contains("Network"));
    }

    #[test]
    fn test_not_found_error() {
        let error = DataError::NotFound("dataset123".to_string());
        assert!(!error.is_retryable());
        assert_eq!(error.severity(), ErrorSeverity::Medium);
        assert_eq!(error.category(), "NOT_FOUND");
    }

    #[test]
    fn test_compression_error() {
        let error = DataError::compression("ZSTD compression failed");
        assert!(matches!(error, DataError::Compression(_)));
        assert!(!error.is_retryable());
        assert_eq!(error.category(), "COMPRESSION");
    }

    #[test]
    fn test_storage_error() {
        let error = DataError::storage("Disk full");
        assert!(matches!(error, DataError::Storage(_)));
        assert!(error.is_retryable());
        assert_eq!(error.category(), "STORAGE");
    }

    #[test]
    fn test_timeout_error() {
        let error = DataError::timeout("Request timeout after 30s");
        assert!(error.is_retryable());
        assert_eq!(error.severity(), ErrorSeverity::Medium);
    }

    #[test]
    fn test_configuration_error() {
        let error = DataError::configuration("api_key", "Missing required field");
        assert!(!error.is_retryable());
        assert_eq!(error.severity(), ErrorSeverity::Critical);
    }

    #[test]
    fn test_api_error_with_code() {
        let error = DataError::api("Unauthorized", Some("401"));
        assert!(matches!(error, DataError::Api { .. }));
        assert_eq!(error.category(), "API");
    }

    #[test]
    fn test_api_error_without_code() {
        let error = DataError::api("Server error", None::<String>);
        assert!(matches!(error, DataError::Api { .. }));
        assert_eq!(error.category(), "API");
    }

    #[test]
    fn test_websocket_error() {
        let ws_error = tungstenite::Error::ConnectionClosed;
        let error = DataError::WebSocket(ws_error);
        assert!(error.is_retryable());
        assert_eq!(error.category(), "WEBSOCKET");
    }
}
