mod context;
mod shutdown;

use context::AppContext;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let ctx = AppContext::init().await?;

    let http_port = ctx.config.git_http_port()?;
    let ssh_port = ctx.config.ssh_port()?;
    let rpc_addr = ctx.config.git_rpc_addr()?;
    let rpc_port = ctx.config.git_rpc_port()?;

    tracing::info!(
        "gitpod service starting (HTTP:{} / SSH:{} / gRPC:{}:{})",
        http_port,
        ssh_port,
        rpc_addr,
        rpc_port
    );

    let http_task = tokio::spawn(git::http::run_http(
        ctx.config.clone(),
        ctx.db.clone(),
        ctx.cache.clone(),
        ctx.redis_pool.clone(),
    ));

    let ssh_task = tokio::spawn(git::ssh::run_ssh(
        ctx.config.clone(),
        ctx.db.clone(),
        ctx.cache.clone(),
        ctx.redis_pool.clone(),
    ));

    let rpc_addr_parsed =
        format!("{}:{}", rpc_addr, rpc_port).parse::<std::net::SocketAddr>()?;
    let sync_service =
        git::sync::ReceiveSyncService::new(ctx.redis_pool.clone());
    let git_server = git::rpc::server::GitServer::new(
        rpc_addr_parsed,
        ctx.db.clone(),
        ctx.cache.clone(),
        sync_service,
    );
    let rpc_task = tokio::spawn(async move {
        git_server
            .serve()
            .await
            .map_err(|e| anyhow::anyhow!("{}", e))
    });

    tokio::select! {
        result = http_task => {
            match result {
                Ok(Ok(())) => tracing::info!("HTTP server stopped"),
                Ok(Err(e)) => tracing::error!("HTTP server error: {}", e),
                Err(e) => tracing::error!("HTTP task panicked: {}", e),
            }
        }
        result = ssh_task => {
            match result {
                Ok(Ok(())) => tracing::info!("SSH server stopped"),
                Ok(Err(e)) => tracing::error!("SSH server error: {}", e),
                Err(e) => tracing::error!("SSH task panicked: {}", e),
            }
        }
        result = rpc_task => {
            match result {
                Ok(Ok(())) => tracing::info!("gRPC server stopped"),
                Ok(Err(e)) => tracing::error!("gRPC server error: {}", e),
                Err(e) => tracing::error!("gRPC task panicked: {}", e),
            }
        }
        _ = shutdown::wait_for_shutdown_signal() => {
            tracing::info!("shutdown signal received, stopping gitpod service");
        }
    }

    Ok(())
}
