//! API Key Error Types
//!
//! Unified error handling for API key operations

use thiserror::Error;

/// API Key related errors
#[derive(Error, Debug)]
pub enum ApiKeyError {
    #[error("API key not found: {key_id}")]
    NotFound { key_id: String },

    #[error("API key expired: {key_id}")]
    Expired { key_id: String },

    #[error("API key revoked: {key_id}")]
    Revoked { key_id: String },

    #[error("API key suspended: {key_id}")]
    Suspended { key_id: String },

    #[error("Invalid API key format")]
    InvalidFormat,

    #[error("Insufficient permissions: required {required}, have {actual}")]
    InsufficientPermissions { required: String, actual: String },

    #[error("Rate limit exceeded: retry after {retry_after_secs} seconds")]
    RateLimited { retry_after_secs: u64 },

    #[error("IP blocked: {ip}")]
    IpBlocked { ip: String },

    #[error("Rotation required: {reason}")]
    RotationRequired { reason: String },

    #[error("Anomaly detected: {anomaly_type}")]
    AnomalyDetected { anomaly_type: String },

    #[error("Database error: {0}")]
    Database(#[from] sqlx::Error),

    #[error("Cache error: {message}")]
    Cache { message: String },

    #[error("External service error: {service} - {message}")]
    ExternalService { service: String, message: String },

    #[error("Configuration error: {0}")]
    Config(String),

    #[error("Internal error: {0}")]
    Internal(#[from] anyhow::Error),
}

/// Result type for API key operations
pub type ApiKeyResult<T> = Result<T, ApiKeyError>;

impl ApiKeyError {
    /// Check if the error is retryable
    pub fn is_retryable(&self) -> bool {
        matches!(
            self,
            ApiKeyError::RateLimited { .. } | ApiKeyError::Database(_) | ApiKeyError::Internal(_)
        )
    }

    /// Check if the error is a client error (4xx)
    pub fn is_client_error(&self) -> bool {
        matches!(
            self,
            ApiKeyError::NotFound { .. }
                | ApiKeyError::Expired { .. }
                | ApiKeyError::Revoked { .. }
                | ApiKeyError::Suspended { .. }
                | ApiKeyError::InvalidFormat
                | ApiKeyError::InsufficientPermissions { .. }
                | ApiKeyError::RateLimited { .. }
                | ApiKeyError::IpBlocked { .. }
                | ApiKeyError::RotationRequired { .. }
        )
    }

    /// Get HTTP status code for the error
    pub fn status_code(&self) -> u16 {
        match self {
            ApiKeyError::NotFound { .. } => 404,
            ApiKeyError::Expired { .. } => 401,
            ApiKeyError::Revoked { .. } => 401,
            ApiKeyError::Suspended { .. } => 403,
            ApiKeyError::InvalidFormat => 400,
            ApiKeyError::InsufficientPermissions { .. } => 403,
            ApiKeyError::RateLimited { .. } => 429,
            ApiKeyError::IpBlocked { .. } => 403,
            ApiKeyError::RotationRequired { .. } => 401,
            ApiKeyError::AnomalyDetected { .. } => 403,
            ApiKeyError::Database(_) => 500,
            ApiKeyError::Cache { .. } => 500,
            ApiKeyError::ExternalService { .. } => 502,
            ApiKeyError::Config(_) => 500,
            ApiKeyError::Internal(_) => 500,
        }
    }

    /// Get error code for API responses
    pub fn error_code(&self) -> &'static str {
        match self {
            ApiKeyError::NotFound { .. } => "api_key.not_found",
            ApiKeyError::Expired { .. } => "api_key.expired",
            ApiKeyError::Revoked { .. } => "api_key.revoked",
            ApiKeyError::Suspended { .. } => "api_key.suspended",
            ApiKeyError::InvalidFormat => "api_key.invalid_format",
            ApiKeyError::InsufficientPermissions { .. } => "api_key.insufficient_permissions",
            ApiKeyError::RateLimited { .. } => "api_key.rate_limited",
            ApiKeyError::IpBlocked { .. } => "api_key.ip_blocked",
            ApiKeyError::RotationRequired { .. } => "api_key.rotation_required",
            ApiKeyError::AnomalyDetected { .. } => "api_key.anomaly_detected",
            ApiKeyError::Database(_) => "internal.database_error",
            ApiKeyError::Cache { .. } => "internal.cache_error",
            ApiKeyError::ExternalService { .. } => "external.service_error",
            ApiKeyError::Config(_) => "internal.config_error",
            ApiKeyError::Internal(_) => "internal.error",
        }
    }
}

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

    #[test]
    fn test_error_status_codes() {
        assert_eq!(
            ApiKeyError::NotFound {
                key_id: "test".into()
            }
            .status_code(),
            404
        );
        assert_eq!(
            ApiKeyError::Expired {
                key_id: "test".into()
            }
            .status_code(),
            401
        );
        assert_eq!(ApiKeyError::InvalidFormat.status_code(), 400);
        assert_eq!(
            ApiKeyError::RateLimited {
                retry_after_secs: 60
            }
            .status_code(),
            429
        );
    }

    #[test]
    fn test_error_codes() {
        assert_eq!(
            ApiKeyError::NotFound {
                key_id: "test".into()
            }
            .error_code(),
            "api_key.not_found"
        );
        assert_eq!(
            ApiKeyError::RateLimited {
                retry_after_secs: 60
            }
            .error_code(),
            "api_key.rate_limited"
        );
    }

    #[test]
    fn test_is_client_error() {
        assert!(ApiKeyError::NotFound {
            key_id: "test".into()
        }
        .is_client_error());
        assert!(ApiKeyError::InvalidFormat.is_client_error());
        assert!(!ApiKeyError::Database(sqlx::Error::RowNotFound).is_client_error());
    }
}
