use chainmq::{AppContext, Job, JobContext, JobRegistry, Result, WorkerBuilder, async_trait};
use serde::{Deserialize, Serialize};
use std::sync::Arc;

#[derive(Serialize, Deserialize)]
struct EmailJob {
    to: String,
}

#[async_trait]
impl Job for EmailJob {
    async fn perform(&self, _ctx: &JobContext) -> Result<()> {
        println!("[emails] {}", self.to);
        Ok(())
    }
    fn name() -> &'static str {
        "EmailJob"
    }
    fn queue_name() -> &'static str {
        "emails"
    }
}

#[derive(Serialize, Deserialize)]
struct ReportJob {
    id: u32,
}

#[async_trait]
impl Job for ReportJob {
    async fn perform(&self, _ctx: &JobContext) -> Result<()> {
        println!("[reports] {}", self.id);
        Ok(())
    }
    fn name() -> &'static str {
        "ReportJob"
    }
    fn queue_name() -> &'static str {
        "reports"
    }
}

#[derive(Clone, Default)]
struct AppState;
impl AppContext for AppState {
    fn clone_context(&self) -> Arc<dyn AppContext> {
        Arc::new(self.clone())
    }
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    tracing_subscriber::fmt::try_init().ok();
    let app = Arc::new(AppState::default());
    let mut reg_emails = JobRegistry::new();
    reg_emails.register::<EmailJob>();
    let mut reg_reports = JobRegistry::new();
    reg_reports.register::<ReportJob>();

    let redis = option_env!("REDIS_URL").unwrap_or("redis://localhost:6370");

    let mut w_emails = WorkerBuilder::new_with_redis_uri(redis, reg_emails)
        .with_app_context(app.clone())
        .with_queue_name("emails")
        .with_concurrency(3)
        .spawn()
        .await?;

    let mut w_reports = WorkerBuilder::new_with_redis_uri(redis, reg_reports)
        .with_app_context(app)
        .with_queue_name("reports")
        .with_concurrency(2)
        .spawn()
        .await?;

    w_emails.start().await?;
    w_reports.start().await?;
    println!("[multi-workers] running: emails + reports");

    Ok(())
}
