//! cube-core: 共享脚手架。所有 cube app 通过这个 crate 拿基础 router、tracing、shutdown。

use std::path::Path;

use axum::{routing::get, Router};
use tokio::net::TcpListener;
use tokio::signal;
use tower_http::services::{ServeDir, ServeFile};
use tower_http::trace::TraceLayer;

/// 拼一个带 healthz + 静态前端 SPA fallback 的基础 router。
///
/// `dist_dir` 是前端 vite build 输出目录（容器内一般是 `/dist`）。
pub fn base(dist_dir: impl AsRef<Path>) -> Router {
    let dist = dist_dir.as_ref().to_path_buf();
    let index = dist.join("index.html");
    let static_svc = ServeDir::new(&dist).fallback(ServeFile::new(index));

    Router::new()
        .route("/healthz", get(healthz))
        .fallback_service(static_svc)
        .layer(TraceLayer::new_for_http())
}

async fn healthz() -> &'static str {
    "ok"
}

/// 初始化 tracing：JSON to stdout，吃 RUST_LOG env。
pub fn init_tracing() {
    use tracing_subscriber::{fmt, EnvFilter};
    let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
    fmt().json().with_env_filter(filter).init();
}

/// 起服务，绑定 0.0.0.0:port，挂 SIGTERM/SIGINT 优雅 shutdown。
pub async fn serve(app: Router, port: u16) -> std::io::Result<()> {
    let addr = format!("0.0.0.0:{port}");
    let listener = TcpListener::bind(&addr).await?;
    tracing::info!(%addr, "cube app listening");
    axum::serve(listener, app)
        .with_graceful_shutdown(shutdown_signal())
        .await
}

async fn shutdown_signal() {
    let ctrl_c = async { signal::ctrl_c().await.expect("install ctrl_c handler") };
    let term = async {
        signal::unix::signal(signal::unix::SignalKind::terminate())
            .expect("install SIGTERM handler")
            .recv()
            .await;
    };
    tokio::select! {
        _ = ctrl_c => tracing::info!("ctrl-c received, shutting down"),
        _ = term => tracing::info!("SIGTERM received, shutting down"),
    }
}
