//! Error handling for Veza Rust services
//! 
//! This module provides standardized error handling across all Veza Rust services.

use thiserror::Error;

/// Main error type for Veza Rust services
#[derive(Error, Debug)]
pub enum VezaError {
    #[error("Authentication error: {0}")]
    Auth(String),

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

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

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

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

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

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

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

    #[error("Internal server error: {0}")]
    Internal(String),

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

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

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

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

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

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

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

    #[error("UUID error: {0}")]
    Uuid(#[from] uuid::Error),

    #[error("Chrono error: {0}")]
    Chrono(#[from] chrono::ParseError),

    #[error("Reqwest error: {0}")]
    Reqwest(#[from] reqwest::Error),

    #[error("Tokio error: {0}")]
    Tokio(#[from] tokio::task::JoinError),

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

/// Result type alias for Veza services
pub type VezaResult<T> = Result<T, VezaError>;

impl VezaError {
    /// Get the HTTP status code for this error
    pub fn status_code(&self) -> u16 {
        match self {
            VezaError::Auth(_) => 401,
            VezaError::Authorization(_) => 403,
            VezaError::NotFound(_) => 404,
            VezaError::Validation(_) => 400,
            VezaError::RateLimit(_) => 429,
            VezaError::Config(_) => 500,
            VezaError::Database(_) => 500,
            VezaError::Network(_) => 503,
            VezaError::WebSocket(_) => 1011,
            VezaError::Streaming(_) => 500,
            VezaError::Cache(_) => 500,
            VezaError::Serialization(_) => 400,
            VezaError::Internal(_) => 500,
            VezaError::Io(_) => 500,
            VezaError::Sqlx(_) => 500,
            VezaError::Json(_) => 400,
            VezaError::Uuid(_) => 400,
            VezaError::Chrono(_) => 400,
            VezaError::Reqwest(_) => 503,
            VezaError::Tokio(_) => 500,
            VezaError::Anyhow(_) => 500,
        }
    }

    /// Check if this error should be logged
    pub fn should_log(&self) -> bool {
        match self {
            VezaError::Auth(_) => false,
            VezaError::Authorization(_) => false,
            VezaError::NotFound(_) => false,
            VezaError::Validation(_) => false,
            VezaError::RateLimit(_) => true,
            VezaError::Config(_) => true,
            VezaError::Database(_) => true,
            VezaError::Network(_) => true,
            VezaError::WebSocket(_) => true,
            VezaError::Streaming(_) => true,
            VezaError::Cache(_) => true,
            VezaError::Serialization(_) => false,
            VezaError::Internal(_) => true,
            VezaError::Io(_) => true,
            VezaError::Sqlx(_) => true,
            VezaError::Json(_) => false,
            VezaError::Uuid(_) => false,
            VezaError::Chrono(_) => false,
            VezaError::Reqwest(_) => true,
            VezaError::Tokio(_) => true,
            VezaError::Anyhow(_) => true,
        }
    }

    /// Check if this error should be reported to monitoring
    pub fn should_report(&self) -> bool {
        match self {
            VezaError::Auth(_) => false,
            VezaError::Authorization(_) => false,
            VezaError::NotFound(_) => false,
            VezaError::Validation(_) => false,
            VezaError::RateLimit(_) => true,
            VezaError::Config(_) => true,
            VezaError::Database(_) => true,
            VezaError::Network(_) => true,
            VezaError::WebSocket(_) => true,
            VezaError::Streaming(_) => true,
            VezaError::Cache(_) => true,
            VezaError::Serialization(_) => false,
            VezaError::Internal(_) => true,
            VezaError::Io(_) => true,
            VezaError::Sqlx(_) => true,
            VezaError::Json(_) => false,
            VezaError::Uuid(_) => false,
            VezaError::Chrono(_) => false,
            VezaError::Reqwest(_) => true,
            VezaError::Tokio(_) => true,
            VezaError::Anyhow(_) => true,
        }
    }

    /// Get a user-friendly error message
    pub fn user_message(&self) -> &str {
        match self {
            VezaError::Auth(_) => "Authentication required",
            VezaError::Authorization(_) => "Access denied",
            VezaError::NotFound(_) => "Resource not found",
            VezaError::Validation(_) => "Invalid input",
            VezaError::RateLimit(_) => "Too many requests",
            VezaError::Config(_) => "Configuration error",
            VezaError::Database(_) => "Database error",
            VezaError::Network(_) => "Network error",
            VezaError::WebSocket(_) => "WebSocket error",
            VezaError::Streaming(_) => "Streaming error",
            VezaError::Cache(_) => "Cache error",
            VezaError::Serialization(_) => "Serialization error",
            VezaError::Internal(_) => "Internal server error",
            VezaError::Io(_) => "IO error",
            VezaError::Sqlx(_) => "Database error",
            VezaError::Json(_) => "Invalid JSON",
            VezaError::Uuid(_) => "Invalid UUID",
            VezaError::Chrono(_) => "Invalid date/time",
            VezaError::Reqwest(_) => "Network error",
            VezaError::Tokio(_) => "Task error",
            VezaError::Anyhow(_) => "Internal error",
        }
    }
}

/// Helper macros for creating errors
#[macro_export]
macro_rules! auth_error {
    ($msg:expr) => {
        $crate::error::server::VezaError::Auth($msg.to_string())
    };
    ($fmt:expr, $($arg:tt)*) => {
        $crate::error::server::VezaError::Auth(format!($fmt, $($arg)*))
    };
}

#[macro_export]
macro_rules! auth_error_bail {
    ($msg:expr) => {
        return Err($crate::auth_error!($msg))
    };
    ($fmt:expr, $($arg:tt)*) => {
        return Err($crate::auth_error!($fmt, $($arg)*))
    };
}

#[macro_export]
macro_rules! not_found_error {
    ($msg:expr) => {
        $crate::error::server::VezaError::NotFound($msg.to_string())
    };
    ($fmt:expr, $($arg:tt)*) => {
        $crate::error::server::VezaError::NotFound(format!($fmt, $($arg)*))
    };
}

#[macro_export]
macro_rules! validation_error {
    ($msg:expr) => {
        $crate::error::server::VezaError::Validation($msg.to_string())
    };
    ($fmt:expr, $($arg:tt)*) => {
        $crate::error::server::VezaError::Validation(format!($fmt, $($arg)*))
    };
}

#[macro_export]
macro_rules! internal_error {
    ($msg:expr) => {
        $crate::error::server::VezaError::Internal($msg.to_string())
    };
    ($fmt:expr, $($arg:tt)*) => {
        $crate::error::server::VezaError::Internal(format!($fmt, $($arg)*))
    };
}

#[macro_export]
macro_rules! database_error {
    ($msg:expr) => {
        $crate::error::server::VezaError::Database($msg.to_string())
    };
    ($fmt:expr, $($arg:tt)*) => {
        $crate::error::server::VezaError::Database(format!($fmt, $($arg)*))
    };
}

#[macro_export]
macro_rules! rate_limit_error {
    ($msg:expr) => {
        $crate::error::server::VezaError::RateLimit($msg.to_string())
    };
    ($fmt:expr, $($arg:tt)*) => {
        $crate::error::server::VezaError::RateLimit(format!($fmt, $($arg)*))
    };
}

/// Error context trait for adding context to errors
pub trait ErrorContext<T> {
    fn context<C>(self, context: C) -> VezaResult<T>
    where
        C: std::fmt::Display + Send + Sync + 'static;
}

impl<T, E> ErrorContext<T> for Result<T, E>
where
    E: Into<VezaError>,
{
    fn context<C>(self, context: C) -> VezaResult<T>
    where
        C: std::fmt::Display + Send + Sync + 'static,
    {
        self.map_err(|e| {
            let veza_err: VezaError = e.into();
            match veza_err {
                VezaError::Anyhow(e) => VezaError::Anyhow(e.context(context)),
                _ => VezaError::Anyhow(anyhow::anyhow!("{}: {}", context, veza_err)),
            }
        })
    }
}

/// Log error with appropriate level
pub fn log_error(error: &VezaError) {
    if error.should_log() {
        tracing::error!("Veza error: {}", error);
    } else {
        tracing::debug!("Veza error: {}", error);
    }
}

/// Convert error to JSON response
pub fn error_to_json(error: &VezaError) -> serde_json::Value {
    serde_json::json!({
        "error": {
            "code": format!("{:?}", error),
            "message": error.user_message(),
            "status_code": error.status_code(),
            "timestamp": chrono::Utc::now().to_rfc3339(),
        }
    })
}
