//! Health check endpoints for API Gateway service
//!
//! Provides HTTP-based health and readiness endpoints for Kubernetes probes.
//! These endpoints don't require authentication to allow load balancers and
//! orchestrators to check service health.

use axum::{extract::State, http::StatusCode, routing::get, Json, Router};
use serde::Serialize;
use std::sync::Arc;
use sqlx::PgPool;
use redis::aio::ConnectionManager;

/// Health status response
#[derive(Serialize)]
pub struct HealthResponse {
    /// Service status
    pub status: &'static str,
    /// Service name
    pub service: &'static str,
    /// Service version
    pub version: &'static str,
}

/// Detailed health status with dependency checks
#[derive(Serialize)]
pub struct DetailedHealthResponse {
    /// Service status
    pub status: &'static str,
    /// Service name
    pub service: &'static str,
    /// Service version
    pub version: &'static str,
    /// Database connectivity
    pub database: bool,
    /// Redis connectivity
    pub redis: bool,
}

/// Shared health check state
#[derive(Clone)]
pub struct HealthState {
    /// PostgreSQL connection pool
    pub db_pool: Option<PgPool>,
    /// Redis connection manager
    pub redis: Option<ConnectionManager>,
}

/// Creates the health check router with /health and /ready endpoints
pub fn health_router(state: HealthState) -> Router {
    Router::new()
        .route("/health", get(health_check))
        .route("/ready", get(readiness_check))
        .with_state(Arc::new(state))
}

/// Liveness probe - indicates service is running
/// Returns 200 OK if the service process is alive
async fn health_check() -> Json<HealthResponse> {
    Json(HealthResponse {
        status: "healthy",
        service: "api",
        version: env!("CARGO_PKG_VERSION"),
    })
}

/// Readiness probe - indicates service is ready to accept requests
/// Checks database and Redis connectivity
async fn readiness_check(
    State(state): State<Arc<HealthState>>,
) -> Result<Json<DetailedHealthResponse>, StatusCode> {
    let mut db_ok = false;
    let mut redis_ok = false;

    // Check database connectivity
    if let Some(ref pool) = state.db_pool {
        db_ok = sqlx::query("SELECT 1")
            .fetch_one(pool)
            .await
            .is_ok();
    }

    // Check Redis connectivity
    if let Some(ref mut redis_conn) = state.redis.clone() {
        use redis::AsyncCommands;
        redis_ok = redis_conn.ping::<()>().await.is_ok();
    }

    // Service is ready only if all dependencies are healthy
    let is_ready = db_ok && redis_ok;
    let status = if is_ready { "ready" } else { "not_ready" };
    let http_status = if is_ready {
        StatusCode::OK
    } else {
        StatusCode::SERVICE_UNAVAILABLE
    };

    let response = DetailedHealthResponse {
        status,
        service: "api",
        version: env!("CARGO_PKG_VERSION"),
        database: db_ok,
        redis: redis_ok,
    };

    if is_ready {
        Ok(Json(response))
    } else {
        Err(http_status)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_health_check() {
        let response = health_check().await;
        assert_eq!(response.status, "healthy");
        assert_eq!(response.service, "api");
    }

    #[tokio::test]
    async fn test_readiness_check_without_deps() {
        let state = Arc::new(HealthState {
            db_pool: None,
            redis: None,
        });

        let result = readiness_check(State(state)).await;
        assert!(result.is_err());
    }
}
