//! Service lifecycle: run the handler over stdio until the client hangs
//! up.

use rmcp::ServiceExt;
use tracing::info;

use crate::server::AeroContextServer;

/// Failure of the stdio MCP service lifecycle.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum ServeError {
    /// The MCP handshake over stdin/stdout failed.
    #[error("could not initialize the MCP stdio service")]
    Initialize(#[source] Box<rmcp::service::ServerInitializeError>),
    /// The running service task aborted instead of quitting.
    #[error("the MCP service task aborted")]
    Join(#[from] tokio::task::JoinError),
}

/// Serve an [`AeroContextServer`] over stdin/stdout until the client
/// disconnects.
///
/// Stdout belongs to the MCP transport for the whole call; diagnostics
/// must go through `tracing` only, with any subscriber writing to
/// stderr.
pub async fn serve_stdio() -> Result<(), ServeError> {
    let service = AeroContextServer::new()
        .serve(rmcp::transport::stdio())
        .await
        .map_err(|cause| ServeError::Initialize(Box::new(cause)))?;
    info!("serving MCP over stdio");
    // QuitReason::JoinError is a service that died, not a client that
    // hung up — collapsing it into Ok would exit 0 on a fatal failure.
    match service.waiting().await? {
        rmcp::service::QuitReason::JoinError(cause) => Err(ServeError::Join(cause)),
        reason => {
            info!(?reason, "MCP service stopped");
            Ok(())
        }
    }
}
