use std::net::SocketAddr;
use std::time::Duration;
use tracing_subscriber::EnvFilter;

/// Upper bound on how long we're willing to wait for the crawler daemon
/// to drain before letting `main` return. Without it a wedged background
/// task (e.g. a chromiumoxide handler stuck on a dead WS) blocks the
/// process from exiting after Ctrl-C / SIGTERM.
const CRAWLER_SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(5);

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    dotenvy::dotenv().ok();
    tracing_subscriber::fmt()
        .with_env_filter(
            EnvFilter::try_from_default_env().unwrap_or_else(|_| {
                "info,mangalord=debug,chromiumoxide::conn=off,chromiumoxide::handler=off".into()
            }),
        )
        .init();

    let config = mangalord::config::Config::from_env()?;
    let addr: SocketAddr = config.bind_address.parse()?;
    let mangalord::app::AppHandle { router, supervisors } =
        mangalord::app::build(config).await?;

    tracing::info!(%addr, "mangalord listening");
    let listener = tokio::net::TcpListener::bind(addr).await?;
    axum::serve(listener, router)
        .with_graceful_shutdown(shutdown_signal())
        .await?;

    // Drain background daemons (crawler + analysis) before exiting so
    // Chromium gets a clean shutdown rather than relying on kill-on-drop.
    // Bounded by a timeout so a wedged shutdown path can't trap the process.
    if tokio::time::timeout(CRAWLER_SHUTDOWN_TIMEOUT, supervisors.shutdown())
        .await
        .is_err()
    {
        tracing::warn!(
            timeout_s = CRAWLER_SHUTDOWN_TIMEOUT.as_secs(),
            "daemon shutdown exceeded timeout; abandoning"
        );
    }
    Ok(())
}

/// Wait for either Ctrl-C (interactive shell) or SIGTERM (Docker /
/// Kubernetes / Podman / systemd stop) and log which arrived. Without
/// the SIGTERM branch, `docker compose stop` runs out its grace period
/// and skips straight to SIGKILL — the daemon never gets the
/// `daemon.shutdown().await` path, leaking Chromium.
async fn shutdown_signal() {
    use tokio::signal::unix::{signal, SignalKind};
    let mut sigterm = match signal(SignalKind::terminate()) {
        Ok(s) => s,
        Err(e) => {
            // SignalKind::terminate() is supported on every Unix the
            // tokio runtime runs on; if registration fails we still
            // honour Ctrl-C so the process is at least
            // interactive-shutdownable.
            tracing::warn!(error = %e, "could not install SIGTERM handler; falling back to ctrl_c only");
            let _ = tokio::signal::ctrl_c().await;
            tracing::info!("ctrl-c received; shutting down");
            return;
        }
    };
    tokio::select! {
        _ = tokio::signal::ctrl_c() => {
            tracing::info!("ctrl-c received; shutting down");
        }
        _ = sigterm.recv() => {
            tracing::info!("SIGTERM received; shutting down");
        }
    }
}
