//! Authentication and Authorization Errors

use axum::http::StatusCode;
use thiserror::Error;

/// Authentication and authorization specific errors
#[derive(Error, Debug, Clone)]
pub enum AuthError {
    /// Authentication errors
    #[error("Authentication error: {0}")]
    Authentication(String),

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

    /// JWT token errors
    #[error("Token error: {0}")]
    Token(String),

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

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

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

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

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

    /// Unauthorized access
    #[error("Unauthorized: {0}")]
    Unauthorized(String),

    /// Forbidden access
    #[error("Forbidden: {0}")]
    Forbidden(String),
}

impl AuthError {
    pub fn status_code(&self) -> StatusCode {
        match self {
            Self::Authentication(_) => StatusCode::UNAUTHORIZED,
            Self::Authorization(_) => StatusCode::FORBIDDEN,
            Self::Token(_) => StatusCode::UNAUTHORIZED,
            Self::UserNotFound(_) => StatusCode::NOT_FOUND,
            Self::RoleNotFound(_) => StatusCode::NOT_FOUND,
            Self::PermissionNotFound(_) => StatusCode::NOT_FOUND,
            Self::SessionNotFound(_) => StatusCode::NOT_FOUND,
            Self::SessionExpired(_) => StatusCode::UNAUTHORIZED,
            Self::Unauthorized(_) => StatusCode::UNAUTHORIZED,
            Self::Forbidden(_) => StatusCode::FORBIDDEN,
        }
    }

    pub fn error_code(&self) -> &'static str {
        match self {
            Self::Authentication(_) => "AUTHENTICATION_ERROR",
            Self::Authorization(_) => "AUTHORIZATION_ERROR",
            Self::Token(_) => "TOKEN_ERROR",
            Self::UserNotFound(_) => "USER_NOT_FOUND",
            Self::RoleNotFound(_) => "ROLE_NOT_FOUND",
            Self::PermissionNotFound(_) => "PERMISSION_NOT_FOUND",
            Self::SessionNotFound(_) => "SESSION_NOT_FOUND",
            Self::SessionExpired(_) => "SESSION_EXPIRED",
            Self::Unauthorized(_) => "UNAUTHORIZED",
            Self::Forbidden(_) => "FORBIDDEN",
        }
    }
}

// Conversions from external auth-related errors
impl From<jsonwebtoken::errors::Error> for AuthError {
    fn from(error: jsonwebtoken::errors::Error) -> Self {
        Self::Token(error.to_string())
    }
}

impl From<argon2::Error> for AuthError {
    fn from(error: argon2::Error) -> Self {
        Self::Authentication(format!("Password hashing error: {}", error))
    }
}

// Helper functions for creating common auth errors
pub fn user_not_found(identifier: &str) -> AuthError {
    AuthError::UserNotFound(identifier.to_string())
}

pub fn session_not_found(session_id: &str) -> AuthError {
    AuthError::SessionNotFound(session_id.to_string())
}

pub fn session_expired(session_id: &str) -> AuthError {
    AuthError::SessionExpired(session_id.to_string())
}

pub fn unauthorized(message: &str) -> AuthError {
    AuthError::Unauthorized(message.to_string())
}

pub fn forbidden(message: &str) -> AuthError {
    AuthError::Forbidden(message.to_string())
}
