//! Error Conversion and Retry Logic Tests for Storage Crate
//!
//! This module provides comprehensive tests for:
//! - StorageError to CommonError conversion (all 15 variants)
//! - StorageError::retry_delay_ms (retryable vs non-retryable errors)
//! - std::io::Error to StorageError conversion (all ErrorKind mappings)

use common::error::{CommonError, ErrorCategory};
use std::io::ErrorKind;
use storage::error::StorageError;

// =============================================================================
// STORAGE ERROR TO COMMON ERROR CONVERSION
// =============================================================================

#[test]
fn test_storage_error_to_common_error_io() {
    let storage_err = StorageError::IoError {
        message: "disk full".to_string(),
    };
    let common_err: CommonError = storage_err.into();
    assert_eq!(common_err.category(), ErrorCategory::System);
}

#[test]
fn test_storage_error_to_common_error_network() {
    let storage_err = StorageError::NetworkError {
        message: "host unreachable".to_string(),
    };
    let common_err: CommonError = storage_err.into();
    assert_eq!(common_err.category(), ErrorCategory::Network);
}

#[test]
fn test_storage_error_to_common_error_auth() {
    let storage_err = StorageError::AuthError {
        message: "bad credentials".to_string(),
    };
    let common_err: CommonError = storage_err.into();
    assert_eq!(common_err.category(), ErrorCategory::Security);
}

#[test]
fn test_storage_error_to_common_error_config() {
    let storage_err = StorageError::ConfigError {
        message: "invalid path".to_string(),
    };
    let common_err: CommonError = storage_err.into();
    assert_eq!(common_err.category(), ErrorCategory::Configuration);
}

#[test]
fn test_storage_error_to_common_error_not_found() {
    let storage_err = StorageError::NotFound {
        path: "/data/model.bin".to_string(),
    };
    let common_err: CommonError = storage_err.into();
    assert_eq!(common_err.category(), ErrorCategory::System);
}

#[test]
fn test_storage_error_to_common_error_operation_failed() {
    use std::sync::Arc;
    let storage_err = StorageError::OperationFailed {
        operation: "write".to_string(),
        path: "/data/checkpoint.bin".to_string(),
        source: Arc::new(CommonError::Network("network failed".to_string())),
    };
    let common_err: CommonError = storage_err.into();
    assert_eq!(common_err.category(), ErrorCategory::System);
}

#[test]
fn test_storage_error_to_common_error_permission_denied() {
    let storage_err = StorageError::PermissionDenied {
        path: "/etc/secrets".to_string(),
    };
    let common_err: CommonError = storage_err.into();
    assert_eq!(common_err.category(), ErrorCategory::Security);
}

#[test]
fn test_storage_error_to_common_error_timeout() {
    let storage_err = StorageError::Timeout { timeout_ms: 5000 };
    let common_err: CommonError = storage_err.into();
    assert_eq!(common_err.category(), ErrorCategory::System);
}

#[test]
fn test_storage_error_to_common_error_rate_limited() {
    let storage_err = StorageError::RateLimited {
        retry_after_ms: 1000,
    };
    let common_err: CommonError = storage_err.into();
    assert_eq!(common_err.category(), ErrorCategory::System);
}

#[test]
fn test_storage_error_to_common_error_quota_exceeded() {
    let storage_err = StorageError::QuotaExceeded {
        used: 1000,
        limit: 500,
    };
    let common_err: CommonError = storage_err.into();
    assert_eq!(common_err.category(), ErrorCategory::System);
}

#[test]
fn test_storage_error_to_common_error_serialization() {
    let storage_err = StorageError::SerializationError {
        message: "invalid JSON".to_string(),
    };
    let common_err: CommonError = storage_err.into();
    assert_eq!(common_err.category(), ErrorCategory::System);
}

#[test]
fn test_storage_error_to_common_error_compression() {
    let storage_err = StorageError::CompressionError {
        message: "gzip failed".to_string(),
    };
    let common_err: CommonError = storage_err.into();
    assert_eq!(common_err.category(), ErrorCategory::System);
}

#[test]
fn test_storage_error_to_common_error_integrity_error() {
    let storage_err = StorageError::IntegrityError {
        path: "/data/model.bin".to_string(),
        expected: "abc123".to_string(),
        actual: "def456".to_string(),
    };
    let common_err: CommonError = storage_err.into();
    assert_eq!(common_err.category(), ErrorCategory::System);
}

#[test]
fn test_storage_error_to_common_error_generic() {
    let storage_err = StorageError::Generic {
        message: "unknown error".to_string(),
    };
    let common_err: CommonError = storage_err.into();
    assert_eq!(common_err.category(), ErrorCategory::System);
}

#[cfg(feature = "s3")]
#[test]
fn test_storage_error_to_common_error_s3() {
    let storage_err = StorageError::S3Error {
        message: "S3 operation failed".to_string(),
    };
    let common_err: CommonError = storage_err.into();
    assert_eq!(common_err.category(), ErrorCategory::Network);
}

// =============================================================================
// RETRY DELAY TESTS
// =============================================================================

#[test]
fn test_storage_error_retry_delay_ms_network_error() {
    let err = StorageError::NetworkError {
        message: "connection failed".to_string(),
    };
    assert_eq!(err.retry_delay_ms(), Some(100));
}

#[test]
fn test_storage_error_retry_delay_ms_timeout() {
    let err = StorageError::Timeout { timeout_ms: 500 };
    assert_eq!(err.retry_delay_ms(), Some(200));
}

#[test]
fn test_storage_error_retry_delay_ms_rate_limited() {
    let err = StorageError::RateLimited {
        retry_after_ms: 1500,
    };
    assert_eq!(err.retry_delay_ms(), Some(1500));
}

#[test]
fn test_storage_error_retry_delay_ms_generic() {
    let err = StorageError::Generic {
        message: "generic error".to_string(),
    };
    assert_eq!(err.retry_delay_ms(), Some(100));
}

#[cfg(feature = "s3")]
#[test]
fn test_storage_error_retry_delay_ms_s3() {
    let err = StorageError::S3Error {
        message: "S3 error".to_string(),
    };
    assert_eq!(err.retry_delay_ms(), Some(100));
}

#[test]
fn test_storage_error_retry_delay_ms_not_found() {
    let err = StorageError::NotFound {
        path: "/data/missing.bin".to_string(),
    };
    assert_eq!(err.retry_delay_ms(), None);
}

#[test]
fn test_storage_error_retry_delay_ms_quota_exceeded() {
    let err = StorageError::QuotaExceeded {
        used: 1000,
        limit: 500,
    };
    assert_eq!(err.retry_delay_ms(), None);
}

#[test]
fn test_storage_error_retry_delay_ms_permission_denied() {
    let err = StorageError::PermissionDenied {
        path: "/etc/forbidden".to_string(),
    };
    assert_eq!(err.retry_delay_ms(), None);
}

#[test]
fn test_storage_error_retry_delay_ms_serialization_error() {
    let err = StorageError::SerializationError {
        message: "bad JSON".to_string(),
    };
    assert_eq!(err.retry_delay_ms(), None);
}

#[test]
fn test_storage_error_retry_delay_ms_integrity_error() {
    let err = StorageError::IntegrityError {
        path: "/data/file.bin".to_string(),
        expected: "abc".to_string(),
        actual: "def".to_string(),
    };
    assert_eq!(err.retry_delay_ms(), None);
}

// =============================================================================
// IO ERROR TO STORAGE ERROR CONVERSION
// =============================================================================

#[test]
fn test_io_error_to_storage_error_not_found() {
    let io_err = std::io::Error::new(ErrorKind::NotFound, "file not found");
    let storage_err: StorageError = io_err.into();
    assert!(matches!(storage_err, StorageError::NotFound { .. }));
    if let StorageError::NotFound { path } = storage_err {
        assert_eq!(path, "unknown"); // Default value from conversion
    }
}

#[test]
fn test_io_error_to_storage_error_permission_denied() {
    let io_err = std::io::Error::new(ErrorKind::PermissionDenied, "access denied");
    let storage_err: StorageError = io_err.into();
    assert!(matches!(storage_err, StorageError::PermissionDenied { .. }));
    if let StorageError::PermissionDenied { path } = storage_err {
        assert_eq!(path, "unknown"); // Default value from conversion
    }
}

#[test]
fn test_io_error_to_storage_error_already_exists() {
    let io_err = std::io::Error::new(ErrorKind::AlreadyExists, "file exists");
    let storage_err: StorageError = io_err.into();
    // AlreadyExists maps to IoError in current implementation
    assert!(matches!(storage_err, StorageError::IoError { .. }));
    if let StorageError::IoError { message } = storage_err {
        assert!(message.contains("file exists"));
    }
}

#[test]
fn test_io_error_to_storage_error_timed_out() {
    let io_err = std::io::Error::new(ErrorKind::TimedOut, "operation timed out");
    let storage_err: StorageError = io_err.into();
    assert!(matches!(
        storage_err,
        StorageError::Timeout { timeout_ms: 5000 }
    ));
}

#[test]
fn test_io_error_to_storage_error_broken_pipe() {
    let io_err = std::io::Error::new(ErrorKind::BrokenPipe, "pipe broken");
    let storage_err: StorageError = io_err.into();
    assert!(matches!(storage_err, StorageError::IoError { .. }));
    if let StorageError::IoError { message } = storage_err {
        assert!(message.contains("pipe broken"));
    }
}

#[test]
fn test_io_error_to_storage_error_connection_reset() {
    let io_err = std::io::Error::new(ErrorKind::ConnectionReset, "connection reset");
    let storage_err: StorageError = io_err.into();
    assert!(matches!(storage_err, StorageError::IoError { .. }));
    if let StorageError::IoError { message } = storage_err {
        assert!(message.contains("connection reset"));
    }
}

#[test]
fn test_io_error_to_storage_error_other() {
    let io_err = std::io::Error::other("unknown error");
    let storage_err: StorageError = io_err.into();
    assert!(matches!(storage_err, StorageError::IoError { .. }));
    if let StorageError::IoError { message } = storage_err {
        assert!(message.contains("unknown error"));
    }
}

// =============================================================================
// EDGE CASES AND BOUNDARY CONDITIONS
// =============================================================================

#[test]
fn test_storage_error_empty_message() {
    let storage_err = StorageError::Generic {
        message: String::new(),
    };
    let common_err: CommonError = storage_err.into();
    assert_eq!(common_err.category(), ErrorCategory::System);
}

#[test]
fn test_storage_error_long_path() {
    let long_path = "a".repeat(1000);
    let storage_err = StorageError::NotFound {
        path: long_path.clone(),
    };
    assert!(matches!(storage_err, StorageError::NotFound { .. }));
    if let StorageError::NotFound { path } = storage_err {
        assert_eq!(path.len(), 1000);
    }
}

#[test]
fn test_storage_error_rate_limited_zero_retry() {
    let err = StorageError::RateLimited { retry_after_ms: 0 };
    assert_eq!(err.retry_delay_ms(), Some(0));
}

#[test]
fn test_storage_error_timeout_zero() {
    let err = StorageError::Timeout { timeout_ms: 0 };
    assert_eq!(err.retry_delay_ms(), Some(200));
}

#[test]
fn test_storage_error_integrity_error_same_values() {
    let err = StorageError::IntegrityError {
        path: "/data/file.bin".to_string(),
        expected: "abc123".to_string(),
        actual: "abc123".to_string(),
    };
    // Even with matching values, it's still an integrity error (logic error in caller)
    assert_eq!(err.retry_delay_ms(), None);
}
