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

#[derive(Debug)]
pub enum AppError {
    Unauthorized,
    BadRequest(String),
    NotFound(String),
    /// 403 — authenticated but lacks permission (e.g. non-admin calling an
    /// admin-only endpoint).
    Forbidden(String),
    /// 409 — request is valid but conflicts with current state (e.g. an
    /// analysis is already running for this case).
    Conflict(String),
    /// 503 — the feature is unavailable right now (e.g. no LLM configured).
    ServiceUnavailable(String),
    Internal(String),
    /// 302 redirect. Used by web auth to send unauthenticated users to login.
    Redirect(String),
}

impl IntoResponse for AppError {
    fn into_response(self) -> Response {
        if let Self::Redirect(url) = &self {
            return Response::builder()
                .status(StatusCode::FOUND)
                .header(axum::http::header::LOCATION, url)
                .body(axum::body::Body::empty())
                .unwrap();
        }

        let (status, message) = match &self {
            Self::Unauthorized => (StatusCode::UNAUTHORIZED, "Unauthorized".to_owned()),
            Self::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg.clone()),
            Self::NotFound(msg) => (StatusCode::NOT_FOUND, msg.clone()),
            Self::Forbidden(msg) => (StatusCode::FORBIDDEN, msg.clone()),
            Self::Conflict(msg) => (StatusCode::CONFLICT, msg.clone()),
            Self::ServiceUnavailable(msg) => (StatusCode::SERVICE_UNAVAILABLE, msg.clone()),
            Self::Internal(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg.clone()),
            Self::Redirect(_) => unreachable!(),
        };

        let body = axum::Json(json!({ "error": message }));
        (status, body).into_response()
    }
}

impl From<std::io::Error> for AppError {
    fn from(err: std::io::Error) -> Self {
        Self::Internal(err.to_string())
    }
}
