mod cli;
mod error;
mod filters;
mod routes;
mod theme;

use std::sync::OnceLock;

use clap::Parser;
use tokio::net::TcpListener;

use crate::error::Result;
use crate::theme::{BASE_CSS, MARKDOWN_CSS};

/// Package name, sourced from `Cargo.toml` at compile time.
pub const PKG_NAME: &str = env!("CARGO_PKG_NAME");

/// Package version, sourced from `Cargo.toml` at compile time.
pub const PKG_VERSION: &str = env!("CARGO_PKG_VERSION");

/// Hostname used in breadcrumbs and clone URLs, set once from `--hostname`.
static HOSTNAME: OnceLock<String> = OnceLock::new();

/// SSH clone URL path prefix, set once from `--ssh-prefix`.
static SSH_PREFIX: OnceLock<String> = OnceLock::new();

/// Returns the configured hostname, or `"index"` if none was provided.
pub fn hostname() -> &'static str {
    HOSTNAME.get().map_or("index", |s| s.as_str())
}

/// Returns the configured SSH prefix, or an empty string if none was provided.
pub fn ssh_prefix() -> &'static str {
    SSH_PREFIX.get().map_or("", |s| s.as_str())
}

#[tokio::main]
async fn main() -> Result<()> {
    let args = cli::Args::parse();

    if let Some(hostname) = args.hostname {
        let _ = HOSTNAME.set(hostname);
    }
    if let Some(ssh_prefix) = args.ssh_prefix {
        let _ = SSH_PREFIX.set(ssh_prefix);
    }

    // Concatenate base styles, markdown styles, and theme variables into a
    // single stylesheet served at /style.css.
    let theme_vars = match args.theme_file {
        Some(path) => std::fs::read_to_string(path)?,
        None => args.theme.css().to_owned(),
    };
    let css = format!("{theme_vars}\n{BASE_CSS}\n{MARKDOWN_CSS}");

    let state = routes::AppState {
        root: args.root,
        css,
    };

    let router = routes::router(state);
    let socket = TcpListener::bind("0.0.0.0:3000").await?;

    axum::serve(socket, router).await?;

    Ok(())
}
