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

#[derive(Parser, Debug)]
#[command(name = "email-worker")]
#[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);

    slog::info!(log, "Starting email worker");
    let service = AppService::new(cfg).await?;

    let (shutdown_tx, shutdown_rx) = tokio::sync::broadcast::channel::<()>(1);
    let log_for_signal = log.clone();
    tokio::spawn(async move {
        tokio::signal::ctrl_c().await.ok();
        slog::info!(log_for_signal, "shutting down email worker");
        let _ = shutdown_tx.send(());
    });

    service.start_email_workers(shutdown_rx).await?;
    slog::info!(log, "email worker stopped");
    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!())
}
