use axum::{Json, http::StatusCode, response::IntoResponse};
use serde_json::json;
use thiserror::Error;

pub type Result<T> = std::result::Result<T, AppError>;

#[derive(Error, Debug)]
pub enum AppError {
    #[error("Configuration error: {0}")]
    Configuration(String),

    #[error("Message bus error: {0}")]
    MessageBus(#[from] async_nats::Error),

    #[error("Message bus publish error: {0}")]
    MessageBusPublish(#[from] async_nats::PublishError),

    #[error("Message bus subscribe error: {0}")]
    MessageBusSubscribe(String),

    #[error("Message bus connect error: {0}")]
    MessageBusConnect(String),

    #[error("HTTP request to another service failed: {0}")]
    ServiceRequest(#[from] reqwest::Error),

    #[error("Bad request: {0}")]
    BadRequest(String),

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

impl IntoResponse for AppError {
    fn into_response(self) -> axum::response::Response {
        let (status, message) = match &self {
            AppError::Configuration(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg.clone()),
            AppError::MessageBus(err) => (StatusCode::SERVICE_UNAVAILABLE, err.to_string()),
            AppError::MessageBusPublish(err) => (StatusCode::SERVICE_UNAVAILABLE, err.to_string()),
            AppError::MessageBusSubscribe(msg) => (StatusCode::SERVICE_UNAVAILABLE, msg.clone()),
            AppError::MessageBusConnect(msg) => (StatusCode::SERVICE_UNAVAILABLE, msg.clone()),
            AppError::ServiceRequest(err) => (StatusCode::BAD_GATEWAY, err.to_string()),
            AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg.clone()),
            AppError::Internal(err) => (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()),
        };
        let body = Json(json!({ "error": message }));
        (status, body).into_response()
    }
}

// 手动实现针对 async-nats 泛型错误类型的 From 转换
impl From<async_nats::error::Error<async_nats::ConnectErrorKind>> for AppError {
    fn from(err: async_nats::error::Error<async_nats::ConnectErrorKind>) -> Self {
        AppError::MessageBusConnect(err.to_string())
    }
}

impl From<async_nats::SubscribeError> for AppError {
    fn from(err: async_nats::SubscribeError) -> Self {
        AppError::MessageBusSubscribe(err.to_string())
    }
}
