use clap::Parser;
use config::AppConfig;
use slog::{Drain, OwnedKVList, Record};

#[derive(Parser, Debug)]
#[command(name = "gitserver")]
#[command(version)]
struct Args {
    #[arg(long, default_value = "info")]
    log_level: String,
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let args = Args::parse();
    let cfg = AppConfig::load();
    let log = build_logger(&args.log_level);

    let http_handle = tokio::spawn(git::http::run_http(cfg.clone(), log.clone()));
    let ssh_handle = tokio::spawn(git::ssh::run_ssh(cfg, log.clone()));

    tokio::select! {
        result = http_handle => {
            match result {
                Ok(Ok(())) => slog::info!(log, "HTTP server stopped"),
                Ok(Err(e)) => slog::error!(log, "HTTP server error: {}", e),
                Err(e) => slog::error!(log, "HTTP server task panicked: {}", e),
            }
        }
        result = ssh_handle => {
            match result {
                Ok(Ok(())) => slog::info!(log, "SSH server stopped"),
                Ok(Err(e)) => slog::error!(log, "SSH server error: {}", e),
                Err(e) => slog::error!(log, "SSH server task panicked: {}", e),
            }
        }
        _ = tokio::signal::ctrl_c() => {
            slog::info!(log, "received shutdown signal");
        }
    }

    slog::info!(log, "shutting down");
    Ok(())
}

fn build_logger(level: &str) -> slog::Logger {
    let level_filter = match level {
        "trace" => 0usize,
        "debug" => 1usize,
        "info" => 2usize,
        "warn" => 3usize,
        "error" => 4usize,
        _ => 2usize,
    };

    struct StderrDrain(usize);

    impl Drain for StderrDrain {
        type Ok = ();
        type Err = ();
        #[inline]
        fn log(&self, record: &Record, _logger: &OwnedKVList) -> Result<(), ()> {
            let slog_level = match record.level() {
                slog::Level::Trace => 0,
                slog::Level::Debug => 1,
                slog::Level::Info => 2,
                slog::Level::Warning => 3,
                slog::Level::Error => 4,
                slog::Level::Critical => 5,
            };
            if slog_level < self.0 {
                return Ok(());
            }
            let _ = eprintln!(
                "{} [{}] {}:{} - {}",
                chrono::Utc::now().format("%Y-%m-%dT%H:%M:%S%.3fZ"),
                record.level().to_string(),
                record
                    .file()
                    .rsplit_once('/')
                    .map(|(_, s)| s)
                    .unwrap_or(record.file()),
                record.line(),
                record.msg(),
            );
            Ok(())
        }
    }

    let drain = StderrDrain(level_filter);
    let drain = std::sync::Mutex::new(drain);
    let drain = slog::Fuse::new(drain);
    slog::Logger::root(drain, slog::o!())
}
