//! Error types for the Gemini SDK

use thiserror::Error;

/// Result type alias for the SDK
pub type Result<T> = std::result::Result<T, SDKError>;

/// Main error type for the Gemini SDK
#[derive(Debug, Error)]
pub enum SDKError {
    /// Configuration errors
    #[error("Configuration error: {0}")]
    Config(String),

    /// Agent not found
    #[error("Agent not found: {0}")]
    AgentNotFound(String),

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

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

    /// Network errors
    #[error("Network error: {0}")]
    Network(#[from] reqwest::Error),



    /// JSON parsing errors
    #[error("JSON parsing error: {0}")]
    Json(#[from] serde_json::Error),

    /// IO errors
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),

    /// Generic errors
    #[error("Generic error: {0}")]
    Generic(#[from] anyhow::Error),

    /// API errors
    #[error("API error: {message} (status: {status})")]
    Api { status: u16, message: String },

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

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

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

    /// Rate limit errors
    #[error("Rate limit exceeded: {0}")]
    RateLimit(String),

    /// Service unavailable errors
    #[error("Service unavailable: {0}")]
    ServiceUnavailable(String),
}

impl SDKError {
    /// Create a new configuration error
    pub fn config(msg: impl Into<String>) -> Self {
        Self::Config(msg.into())
    }

    /// Create a new agent not found error
    pub fn agent_not_found(agent_id: impl Into<String>) -> Self {
        Self::AgentNotFound(agent_id.into())
    }

    /// Create a new session error
    pub fn session(msg: impl Into<String>) -> Self {
        Self::Session(msg.into())
    }

    /// Create a new attachment error
    pub fn attachment(msg: impl Into<String>) -> Self {
        Self::Attachment(msg.into())
    }

    /// Create a new API error
    pub fn api(status: u16, message: impl Into<String>) -> Self {
        Self::Api {
            status,
            message: message.into(),
        }
    }

    /// Create a new timeout error
    pub fn timeout(msg: impl Into<String>) -> Self {
        Self::Timeout(msg.into())
    }

    /// Create a new invalid input error
    pub fn invalid_input(msg: impl Into<String>) -> Self {
        Self::InvalidInput(msg.into())
    }

    /// Create a new authentication error
    pub fn authentication(msg: impl Into<String>) -> Self {
        Self::Authentication(msg.into())
    }

    /// Create a new rate limit error
    pub fn rate_limit(msg: impl Into<String>) -> Self {
        Self::RateLimit(msg.into())
    }

    /// Create a new service unavailable error
    pub fn service_unavailable(msg: impl Into<String>) -> Self {
        Self::ServiceUnavailable(msg.into())
    }

    /// Check if the error is retryable
    pub fn is_retryable(&self) -> bool {
        match self {
            Self::Network(_) => true,
            Self::Api { status, .. } => {
                // Retry on server errors (5xx) and some client errors
                *status >= 500 || *status == 429 || *status == 408
            }
            Self::Timeout(_) => true,
            Self::RateLimit(_) => true,
            Self::ServiceUnavailable(_) => true,
            _ => false,
        }
    }

    /// Get the error category for logging/metrics
    pub fn category(&self) -> &'static str {
        match self {
            Self::Config(_) => "config",
            Self::AgentNotFound(_) => "agent",
            Self::Session(_) => "session",
            Self::Attachment(_) => "attachment",
            Self::Network(_) => "network",
            Self::Json(_) => "json",
            Self::Io(_) => "io",
            Self::Generic(_) => "generic",
            Self::Api { .. } => "api",
            Self::Timeout(_) => "timeout",
            Self::InvalidInput(_) => "input",
            Self::Authentication(_) => "auth",
            Self::RateLimit(_) => "rate_limit",
            Self::ServiceUnavailable(_) => "service",
        }
    }
}

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

    #[test]
    fn test_error_creation() {
        let err = SDKError::config("test config error");
        assert!(matches!(err, SDKError::Config(_)));
        assert_eq!(err.to_string(), "Configuration error: test config error");
    }

    #[test]
    fn test_error_retryable() {
        assert!(SDKError::api(500, "server error").is_retryable());
        assert!(SDKError::api(429, "rate limit").is_retryable());
        assert!(!SDKError::api(400, "bad request").is_retryable());
        assert!(!SDKError::config("test").is_retryable());
    }

    #[test]
    fn test_error_category() {
        assert_eq!(SDKError::config("test").category(), "config");
        assert_eq!(SDKError::agent_not_found("test").category(), "agent");
        assert_eq!(SDKError::api(500, "test").category(), "api");
    }
}
