mod config;
mod db;
mod models;
mod services;

use crate::config::{AppSettings, ConfigFile};
use anyhow::Result;
use axum::Router;
use axum::routing::get;
use tokio::signal;
use tracing::info;
use tracing_subscriber;

#[tokio::main]
async fn main() -> Result<()> {
    init_logging();

    info!("Starting server");

    let app_settings = load_app_settings();

    let pool = db::initialize_db(&app_settings).await?;
    let app = create_app(pool);

    let listener = tokio::net::TcpListener::bind(format!(
        "{}:{}",
        &app_settings.config.server.host, &app_settings.config.server.port
    ))
    .await?;
    info!(
        "Server starting on http://{}:{}",
        &app_settings.config.server.host, &app_settings.config.server.port
    );

    axum::serve(listener, app)
        .with_graceful_shutdown(shutdown_signal())
        .await?;
    info!("Server stopped");

    Ok(())
}

fn create_app(pool: sqlx::SqlitePool) -> Router {
    Router::new()
        .route("/health", get(health_check))
        .nest("/api", api::routes::routes())
        .with_state(pool)
}

async fn health_check() -> &'static str {
    "OK"
}

fn init_logging() {
    tracing_subscriber::fmt()
        .with_target(false)
        .compact()
        // .with_env_filter(EnvFilter::from_default_env())
        // .json() // For Production
        .init();
}

fn load_app_settings() -> AppSettings {
    AppSettings::default();
    let app_settings = AppSettings::get_app_settings();

    AppSettings::ensure_default_directory(&app_settings)
        .expect("Failed to create default directory");

    let config = ConfigFile::load_from_file(&AppSettings::get_app_settings())
        .expect("Failed to load config file");

    let app_settings = AppSettings {
        config,
        ..app_settings
    };
    app_settings
}

async fn shutdown_signal() {
    let ctrl_c = async {
        signal::ctrl_c()
            .await
            .expect("failed to install CTRL+C handler");
    };

    #[cfg(unix)]
    let terminate = async {
        signal::unix::signal(signal::unix::SignalKind::terminate())
            .expect("failed to install terminate handler")
            .recv()
            .await;
    };

    #[cfg(not(unix))]
    let terminate = std::future::pending::<()>();

    tokio::select! {
        _ = ctrl_c => {},
        _ = terminate => {},
    }

    info!("Signal received, shutting down");
}
