use axum::Json;
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};

use crate::codec::frame::ErrorCode;
use crate::error::Error;

/// Error type for HTTP handlers, rendered as a JSON error response.
pub struct HttpError {
    pub status: StatusCode,
    pub code: ErrorCode,
    pub message: String,
}

impl HttpError {
    pub fn new(status: StatusCode, code: ErrorCode, message: impl Into<String>) -> Self {
        Self {
            status,
            code,
            message: message.into(),
        }
    }
}

impl IntoResponse for HttpError {
    fn into_response(self) -> Response {
        let body = serde_json::json!({
            "error": {
                "code": u16::from(self.code),
                "name": error_code_name(self.code),
                "message": self.message,
            }
        });
        (self.status, Json(body)).into_response()
    }
}

impl From<Error> for HttpError {
    fn from(err: Error) -> Self {
        let status = match &err {
            Error::AuthFailed => StatusCode::UNAUTHORIZED,
            Error::PermissionDenied => StatusCode::FORBIDDEN,
            Error::NamespaceNotFound(_) => StatusCode::NOT_FOUND,
            Error::TopicNotFound(_) => StatusCode::NOT_FOUND,
            Error::QueueFull { .. } => StatusCode::SERVICE_UNAVAILABLE,
            Error::ResourceExhausted(_) => StatusCode::SERVICE_UNAVAILABLE,
            Error::MessageTooLarge { .. } => StatusCode::PAYLOAD_TOO_LARGE,
            Error::InvalidTopic(_) => StatusCode::BAD_REQUEST,
            _ => StatusCode::INTERNAL_SERVER_ERROR,
        };
        let code = ErrorCode::from_error(&err);
        Self {
            status,
            code,
            message: err.to_string(),
        }
    }
}

pub fn error_code_name(code: ErrorCode) -> &'static str {
    match code {
        ErrorCode::AuthFailed => "AUTH_FAILED",
        ErrorCode::PermissionDenied => "PERMISSION_DENIED",
        ErrorCode::NamespaceNotFound => "NAMESPACE_NOT_FOUND",
        ErrorCode::TopicNotFound => "TOPIC_NOT_FOUND",
        ErrorCode::InvalidFrame => "INVALID_REQUEST",
        ErrorCode::FrameTooLarge => "REQUEST_TOO_LARGE",
        ErrorCode::UnsupportedVersion => "UNSUPPORTED_VERSION",
        ErrorCode::NotAuthenticated => "NOT_AUTHENTICATED",
        ErrorCode::InternalError => "INTERNAL_ERROR",
        ErrorCode::ResourceExhausted => "RESOURCE_EXHAUSTED",
        ErrorCode::SlowConsumer => "SLOW_CONSUMER",
        ErrorCode::OffsetNotAvailable => "OFFSET_NOT_AVAILABLE",
        ErrorCode::ServerDraining => "SERVER_DRAINING",
        ErrorCode::MessageTooLarge => "MESSAGE_TOO_LARGE",
        ErrorCode::InvalidTopic => "INVALID_TOPIC",
        ErrorCode::InvalidOperation => "INVALID_OPERATION",
    }
}
