BareGit
//! Executable entry point for Lisa's Phase 1 foundation.

use std::{
    env,
    path::PathBuf,
    process::ExitCode,
    time::{SystemTime, UNIX_EPOCH},
};

use lisa::{app::LisaApp, config::AppConfig, logging, state::StateStore};

const CONFIGURATION_ERROR_EXIT: u8 = 78;

struct Command
{
    config_path: PathBuf,
    health: bool,
}

#[tokio::main]
async fn main() -> ExitCode {
    let command = match command() {
        Ok(command) => command,
        Err(error) => {
            eprintln!("{error}");
            return ExitCode::FAILURE;
        }
    };
    let config = match AppConfig::load(&command.config_path) {
        Ok(config) => config,
        Err(error) => {
            eprintln!("configuration error: {error}");
            return ExitCode::from(CONFIGURATION_ERROR_EXIT);
        }
    };
    logging::init(&config.runtime.log_level);
    if command.health
    {
        return health(&config).await;
    }
    let result = match LisaApp::connect(config).await {
        Ok(app) => app.run().await,
        Err(error) => Err(error),
    };
    match result {
        Ok(()) => ExitCode::SUCCESS,
        Err(error) => {
            eprintln!("Lisa stopped: {error}");
            if error.isPermanentConfigurationFailure()
            {
                ExitCode::from(CONFIGURATION_ERROR_EXIT)
            }
            else
            {
                ExitCode::FAILURE
            }
        }
    }
}

async fn health(config: &AppConfig) -> ExitCode
{
    let store = match StateStore::open(&config.runtime.database_path)
    {
        Ok(store) => store,
        Err(error) =>
        {
            eprintln!("health check failed: {error}");
            return ExitCode::FAILURE;
        }
    };
    let now_ms = SystemTime::now().duration_since(UNIX_EPOCH)
        .unwrap_or_default().as_millis().try_into().unwrap_or(u64::MAX);
    match store.health(now_ms).await
    {
        Ok(snapshot) =>
        {
            println!(
                "{{\"candidate_count\":{},\"ready_to_send_count\":{},\"oldest_ready_age_ms\":{}}}",
                snapshot.candidate_count,
                snapshot.ready_to_send_count,
                snapshot.oldest_ready_age_ms.map_or_else(|| "null".to_owned(), |age| age.to_string()),
            );
            ExitCode::SUCCESS
        }
        Err(error) =>
        {
            eprintln!("health check failed: {error}");
            ExitCode::FAILURE
        }
    }
}

fn command() -> Result<Command, String> {
    let mut arguments = env::args_os().skip(1);
    let mut config_path = PathBuf::from("config.toml");
    let mut health = false;
    while let Some(argument) = arguments.next()
    {
        if argument == "--health"
        {
            health = true;
        }
        else if argument == "--config"
        {
            config_path = arguments.next().map(PathBuf::from)
                .ok_or_else(|| "--config requires a path".to_owned())?;
        }
        else
        {
            return Err("usage: lisa [--config PATH] [--health]".to_owned());
        }
    }
    Ok(Command { config_path, health })
}