//! Handler for health checks.

use axum::{extract::State, Json};
use tracing::instrument;

use crate::{
    dto::HealthResponse, error::Result, state::AppState, store_helpers::store_get_with_timeout,
};
use stemedb_storage::{key_codec, CircuitBreakerStore, QuarantineStore};

/// Health check endpoint.
///
/// Returns service status ("healthy"), API version, and the total number of assertions
/// currently stored in the database. Useful for monitoring and load balancer health checks.
///
/// Also updates Prometheus gauges for assertions_total, quarantine_pending,
/// and circuit_breakers_open on each call.
#[utoipa::path(
    get,
    path = "/v1/health",
    responses(
        (status = 200, description = "Service is healthy", body = HealthResponse),
    ),
    tag = "health"
)]
#[instrument(skip(state))]
pub async fn health_check(State(state): State<AppState>) -> Result<Json<HealthResponse>> {
    // Count assertions in the database
    let assertions_count = count_assertions(&state).await?;

    // Update Prometheus gauges (best-effort — don't fail health check)
    metrics::gauge!("stemedb_assertions_total").set(assertions_count as f64);

    let pending_count =
        state.quarantine_store.list_pending(usize::MAX).await.map(|v| v.len()).unwrap_or_default();
    metrics::gauge!("stemedb_quarantine_pending").set(pending_count as f64);

    let tripped_count = state
        .circuit_breaker_store
        .list_tripped(usize::MAX)
        .await
        .map(|v| v.len())
        .unwrap_or_default();
    metrics::gauge!("stemedb_circuit_breakers_open").set(tripped_count as f64);

    Ok(Json(HealthResponse {
        status: "healthy".to_string(),
        version: env!("CARGO_PKG_VERSION").to_string(),
        assertions_count,
    }))
}

/// Count the number of assertions in the database.
async fn count_assertions(state: &AppState) -> Result<u64> {
    // Read the atomic assertion count maintained by the ingestion pipeline (P5.1: Store-level timeout)
    let count_key = key_codec::assertion_count_key();
    match store_get_with_timeout(&*state.store, &count_key).await? {
        Some(bytes) if bytes.len() == 8 => {
            Ok(u64::from_le_bytes(bytes.try_into().unwrap_or([0u8; 8])))
        }
        _ => Ok(0),
    }
}
