//! Error types for storage operations

use common::error::{CommonError, ErrorCategory};
use thiserror::Error;

/// Result type for storage operations
pub type StorageResult<T> = Result<T, StorageError>;

/// Storage-related errors
#[derive(Error, Debug)]
pub enum StorageError {
    /// IO error during file operations
    #[error("IO error: {message}")]
    IoError {
        /// Error message describing the IO failure
        message: String,
    },

    /// Network error during remote storage operations
    #[error("Network error: {message}")]
    NetworkError {
        /// Error message describing the network failure
        message: String,
    },

    /// Authentication/authorization error
    #[error("Authentication error: {message}")]
    AuthError {
        /// Error message describing the authentication failure
        message: String,
    },

    /// Data not found at the specified path
    #[error("Data not found at path: {path}")]
    NotFound {
        /// The path where data was not found
        path: String,
    },

    /// Permission denied for storage operation
    #[error("Permission denied for operation on: {path}")]
    PermissionDenied {
        /// The path where permission was denied
        path: String,
    },

    /// Storage quota exceeded
    #[error("Storage quota exceeded (used: {used}, limit: {limit})")]
    QuotaExceeded {
        /// Current storage usage in bytes
        used: u64,
        /// Storage quota limit in bytes
        limit: u64,
    },

    /// Data integrity check failed
    #[error("Data integrity check failed for: {path} (expected: {expected}, actual: {actual})")]
    IntegrityError {
        /// The path where integrity check failed
        path: String,
        /// Expected checksum or hash value
        expected: String,
        /// Actual checksum or hash value
        actual: String,
    },

    /// Serialization/deserialization error
    #[error("Serialization error: {message}")]
    SerializationError {
        /// Error message describing the serialization failure
        message: String,
    },

    /// Compression/decompression error
    #[error("Compression error: {message}")]
    CompressionError {
        /// Error message describing the compression failure
        message: String,
    },

    /// Configuration error
    #[error("Configuration error: {message}")]
    ConfigError {
        /// Error message describing the configuration issue
        message: String,
    },

    /// Operation failed with detailed context
    #[error("Operation '{operation}' failed on path '{path}': {source}")]
    OperationFailed {
        /// The operation that failed
        operation: String,
        /// The path where the operation failed
        path: String,
        /// The underlying error that caused the failure
        #[source]
        source: std::sync::Arc<CommonError>,
    },
    /// Timeout during storage operation
    #[error("Operation timed out after {timeout_ms}ms")]
    Timeout {
        /// Timeout duration in milliseconds
        timeout_ms: u64,
    },

    /// Rate limit exceeded
    #[error("Rate limit exceeded, retry after {retry_after_ms}ms")]
    RateLimited {
        /// Time to wait before retrying in milliseconds
        retry_after_ms: u64,
    },

    /// Generic storage error
    #[error("Storage error: {message}")]
    Generic {
        /// Error message describing the generic failure
        message: String,
    },

    /// S3-specific error
    #[cfg(feature = "s3")]
    #[error("S3 error: {message}")]
    S3Error {
        /// Error message describing the S3 failure
        message: String,
    },
}

impl StorageError {
    /// Check if the error is retryable
    pub fn is_retryable(&self) -> bool {
        match self {
            StorageError::NetworkError { .. } => true,
            StorageError::Timeout { .. } => true,
            StorageError::RateLimited { .. } => true,
            StorageError::Generic { .. } => true,
            #[cfg(feature = "s3")]
            StorageError::S3Error { .. } => true,
            _ => false,
        }
    }

    /// Get retry delay in milliseconds for retryable errors
    pub fn retry_delay_ms(&self) -> Option<u64> {
        match self {
            StorageError::NetworkError { .. } => Some(100),
            StorageError::Timeout { .. } => Some(200),
            StorageError::RateLimited { retry_after_ms } => Some(*retry_after_ms),
            StorageError::Generic { .. } => Some(100),
            #[cfg(feature = "s3")]
            StorageError::S3Error { .. } => Some(100),
            _ => None,
        }
    }

    /// Check if error indicates a transient issue
    pub fn is_transient(&self) -> bool {
        matches!(
            self,
            StorageError::NetworkError { .. }
                | StorageError::Timeout { .. }
                | StorageError::RateLimited { .. }
        )
    }

    /// Get error category for metrics and monitoring
    pub fn category(&self) -> &'static str {
        match self {
            StorageError::IoError { .. } => "io",
            StorageError::NetworkError { .. } => "network",
            StorageError::AuthError { .. } => "auth",
            StorageError::NotFound { .. } => "not_found",
            StorageError::PermissionDenied { .. } => "permission",
            StorageError::QuotaExceeded { .. } => "quota",
            StorageError::IntegrityError { .. } => "integrity",
            StorageError::SerializationError { .. } => "serialization",
            StorageError::CompressionError { .. } => "compression",
            StorageError::ConfigError { .. } => "config",
            StorageError::OperationFailed { .. } => "operation_failed",
            StorageError::Timeout { .. } => "timeout",
            StorageError::RateLimited { .. } => "rate_limit",
            StorageError::Generic { .. } => "generic",

            #[cfg(feature = "s3")]
            StorageError::S3Error { .. } => "s3",
        }
    }
    /// Create a sanitized error message safe for logging (removes sensitive info)
    pub fn safe_message(&self) -> String {
        match self {
            StorageError::AuthError { .. } => {
                "Authentication error (details masked for security)".to_owned()
            },

            _ => self.to_string(),
        }
    }
}

// Conversion implementations for common error types

impl From<std::io::Error> for StorageError {
    fn from(err: std::io::Error) -> Self {
        use std::io::ErrorKind;
        match err.kind() {
            ErrorKind::NotFound => StorageError::NotFound {
                path: "unknown".to_owned(),
            },
            ErrorKind::PermissionDenied => StorageError::PermissionDenied {
                path: "unknown".to_owned(),
            },
            ErrorKind::TimedOut => StorageError::Timeout { timeout_ms: 5000 },
            _ => StorageError::IoError {
                message: err.to_string(),
            },
        }
    }
}

impl From<serde_json::Error> for StorageError {
    fn from(err: serde_json::Error) -> Self {
        StorageError::SerializationError {
            message: err.to_string(),
        }
    }
}

impl From<bincode::Error> for StorageError {
    fn from(err: bincode::Error) -> Self {
        StorageError::SerializationError {
            message: err.to_string(),
        }
    }
}

// Conversion to CommonError for integration with common error system
impl From<StorageError> for CommonError {
    fn from(err: StorageError) -> Self {
        let category = match &err {
            StorageError::IoError { .. } => ErrorCategory::System,
            StorageError::NetworkError { .. } => ErrorCategory::Network,
            StorageError::AuthError { .. } => ErrorCategory::Security,
            StorageError::NotFound { .. } => ErrorCategory::System,
            StorageError::PermissionDenied { .. } => ErrorCategory::Security,
            StorageError::QuotaExceeded { .. } => ErrorCategory::System,
            StorageError::IntegrityError { .. } => ErrorCategory::System,
            StorageError::SerializationError { .. } => ErrorCategory::System,
            StorageError::CompressionError { .. } => ErrorCategory::System,
            StorageError::ConfigError { .. } => ErrorCategory::Configuration,
            StorageError::OperationFailed { .. } => ErrorCategory::System,
            StorageError::Timeout { .. } => ErrorCategory::System,
            StorageError::RateLimited { .. } => ErrorCategory::System,
            StorageError::Generic { .. } => ErrorCategory::System,
            StorageError::S3Error { .. } => ErrorCategory::Network,
        };

        CommonError::service(category, err.to_string())
    }
}

// Note: AWS SDK error conversions removed - we use object_store now

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

    #[test]
    fn test_error_retryability() {
        assert!(StorageError::NetworkError {
            message: "test".to_owned()
        }
        .is_retryable());

        assert!(!StorageError::NotFound {
            path: "test".to_owned()
        }
        .is_retryable());

        assert!(StorageError::Timeout { timeout_ms: 1000 }.is_retryable());
    }

    #[test]
    fn test_error_categories() {
        assert_eq!(
            StorageError::IoError {
                message: "test".to_owned()
            }
            .category(),
            "io"
        );

        assert_eq!(
            StorageError::NetworkError {
                message: "test".to_owned()
            }
            .category(),
            "network"
        );

        assert_eq!(
            StorageError::NotFound {
                path: "test".to_owned()
            }
            .category(),
            "not_found"
        );
    }

    #[test]
    fn test_error_transient() {
        assert!(StorageError::NetworkError {
            message: "test".to_owned()
        }
        .is_transient());

        assert!(!StorageError::ConfigError {
            message: "test".to_owned()
        }
        .is_transient());
    }

    #[test]
    fn test_safe_message() {
        let auth_error = StorageError::AuthError {
            message: "sensitive auth details".to_owned(),
        };

        let safe_msg = auth_error.safe_message();
        assert!(safe_msg.contains("masked"));
        assert!(!safe_msg.contains("sensitive"));
    }
}
