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

/// Single error type for the management API. Always serializes to
/// `{"error":"..."}` per the protocol spec; the HTTP status is chosen so the
/// client behaves correctly:
///
/// - 401 Unauthorized clears the local access_token (intentional fallback in
///   the Flutter client — see CONSOLE_API.md §3.6).
/// - 200 OK + JSON `error` for business failures (bad creds, validation).
///   Most non-auth handlers should return BadRequest or Conflict instead so
///   the operator can distinguish them in logs.
#[derive(Debug)]
pub enum ApiError {
    Unauthorized,
    BadCredentials,
    AccountDisabled,
    Unverified,
    Forbidden(String),
    NotFound,
    BadRequest(String),
    Internal(String),
}

impl IntoResponse for ApiError {
    fn into_response(self) -> Response {
        let (status, msg) = match self {
            ApiError::Unauthorized => (StatusCode::UNAUTHORIZED, "unauthorized".to_string()),
            ApiError::BadCredentials => (StatusCode::UNAUTHORIZED, "bad credentials".to_string()),
            ApiError::AccountDisabled => (StatusCode::FORBIDDEN, "account disabled".to_string()),
            ApiError::Unverified => (StatusCode::FORBIDDEN, "unverified".to_string()),
            // Returning HTTP 200 + {"error": ...} for share-rule rejections.
            // Flutter's _jsonDecodeActionResp at ab_model.dart:2002 surfaces
            // the JSON `error` field as a toast and stays signed-in; using
            // 403 here would trigger the global 401/403 logout path and yank
            // the user's session.
            ApiError::Forbidden(m) => (StatusCode::OK, m),
            ApiError::NotFound => (StatusCode::NOT_FOUND, "not found".to_string()),
            ApiError::BadRequest(m) => (StatusCode::BAD_REQUEST, m),
            ApiError::Internal(m) => {
                hbb_common::log::error!("api internal error: {}", m);
                (StatusCode::OK, "internal error".to_string())
            }
        };
        (status, Json(json!({ "error": msg }))).into_response()
    }
}

impl From<hbb_common::anyhow::Error> for ApiError {
    fn from(e: hbb_common::anyhow::Error) -> Self {
        ApiError::Internal(e.to_string())
    }
}
