use std::sync::Arc;
use anyhow::Result;
use colored::Colorize;
use serde::Serialize;
use tokio::time::Duration;
use tracing::info;
use crate::api::{buscar_biblioteca, criar_cliente_http, executar_com_retry};
use crate::i18n::{t, Mensagem};
use crate::output::{emitir_ndjson, imprimir_linha_health, simbolo_health};
use crate::storage::carregar_chaves_api;
#[derive(Debug, Serialize)]
pub struct RelatorioHealth {
pub config_ok: bool,
pub keys_count: usize,
pub api_alcancavel: bool,
pub detalhes_api: Option<String>,
}
pub async fn executar_health(json: bool) -> Result<i32> {
info!("Iniciando health check");
if !json {
imprimir_linha_health(&t(Mensagem::HealthExecutando).cyan().to_string());
}
let cliente = match criar_cliente_http() {
Ok(c) => {
if !json {
imprimir_linha_health(&format!(
"{} {}",
simbolo_health(true),
t(Mensagem::HealthConfigOk).green()
));
}
c
}
Err(err) => {
let detalhe = err.to_string();
if json {
let relatorio = RelatorioHealth {
config_ok: false,
keys_count: 0,
api_alcancavel: false,
detalhes_api: Some(detalhe.clone()),
};
emitir_ndjson("health", &relatorio);
} else {
imprimir_linha_health(&format!(
"{} {} — {}",
simbolo_health(false),
t(Mensagem::HealthConfigFalhou).red(),
detalhe
));
}
return Ok(74);
}
};
let chaves = match carregar_chaves_api() {
Ok(c) if !c.is_empty() => {
if !json {
imprimir_linha_health(&format!(
"{} {} {}",
simbolo_health(true),
t(Mensagem::HealthKeysOk).green(),
c.len().to_string().bold()
));
}
c
}
_ => {
if json {
let relatorio = RelatorioHealth {
config_ok: true,
keys_count: 0,
api_alcancavel: false,
detalhes_api: None,
};
emitir_ndjson("health", &relatorio);
} else {
imprimir_linha_health(&format!(
"{} {}",
simbolo_health(false),
t(Mensagem::HealthKeysFaltando).yellow()
));
}
return Ok(66);
}
};
let cliente_arc = Arc::new(cliente);
let probe_result = tokio::time::timeout(
Duration::from_secs(10),
executar_com_retry(&chaves, move |chave| {
let c = Arc::clone(&cliente_arc);
async move { buscar_biblioteca(&c, &chave, "react", "health probe").await }
}),
)
.await;
let api_alcancavel = matches!(probe_result, Ok(Ok(_)));
let detalhes_api = match &probe_result {
Err(_) => Some("timeout after 10s".to_string()),
Ok(Err(e)) => Some(e.to_string()),
Ok(Ok(_)) => None,
};
if json {
let relatorio = RelatorioHealth {
config_ok: true,
keys_count: chaves.len(),
api_alcancavel,
detalhes_api: detalhes_api.clone(),
};
emitir_ndjson("health", &relatorio);
} else if api_alcancavel {
imprimir_linha_health(&format!(
"{} {}",
simbolo_health(true),
t(Mensagem::HealthApiOk).green()
));
} else {
let detalhe = detalhes_api.as_deref().unwrap_or("");
imprimir_linha_health(&format!(
"{} {} — {}",
simbolo_health(false),
t(Mensagem::HealthApiOffline).red(),
detalhe
));
}
if api_alcancavel {
Ok(0)
} else {
Ok(69)
}
}