use axum::{Json, Router, extract::State, http::StatusCode, response::IntoResponse, routing::get};
use serde::Serialize;
use utoipa::ToSchema;

use crate::AppState;

/// Liveness payload: the process is running.
#[derive(Serialize, ToSchema)]
pub(crate) struct Live {
    /// Always `"ok"` when the process serves requests.
    pub status: &'static str,
}

/// Readiness payload: dependencies were checked.
#[derive(Serialize, ToSchema)]
pub(crate) struct Ready {
    /// `"ok"` when ready, `"degraded"` otherwise.
    pub status: &'static str,
    /// Whether the database responded to a ping.
    pub database: bool,
}

/// Liveness probe — no dependencies checked.
#[utoipa::path(get, path = "/health/live", responses((status = 200, body = Live)))]
pub(crate) async fn live() -> Json<Live> {
    Json(Live { status: "ok" })
}

/// Readiness probe — confirms the database answers.
#[utoipa::path(
    get,
    path = "/health/ready",
    responses(
        (status = 200, body = Ready, description = "Ready"),
        (status = 503, body = Ready, description = "A dependency is unavailable")
    )
)]
pub(crate) async fn ready(State(state): State<AppState>) -> impl IntoResponse {
    match state.db.ping().await {
        Ok(()) => (
            StatusCode::OK,
            Json(Ready {
                status: "ok",
                database: true,
            }),
        ),
        Err(_) => (
            StatusCode::SERVICE_UNAVAILABLE,
            Json(Ready {
                status: "degraded",
                database: false,
            }),
        ),
    }
}

/// Health routes, parameterized over [`AppState`].
pub(crate) fn routes() -> Router<AppState> {
    Router::new()
        .route("/health/live", get(live))
        .route("/health/ready", get(ready))
}
