use crate::{
    config::email::EmailConfig,
    db::DbPool,
    infra::{mail::mailer, redis::redis_client::RedisClient},
    repositories::email_log_repository::EmailLogRepository,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;

pub const MAIL_QUEUE: &str = "mail:verification:queue";

#[derive(Debug, Serialize, Deserialize)]
pub struct EmailJob {
    pub recipient: String,
    pub code: String,
}

pub fn start(redis: RedisClient, config: EmailConfig, pool: DbPool) {
    for worker_id in 0..config.worker_pool_size.max(1) {
        let redis = redis.clone();
        let config = config.clone();
        let pool = pool.clone();
        tokio::spawn(async move {
            loop {
                match redis.queue_pop::<EmailJob>(MAIL_QUEUE).await {
                    Ok(Some(job)) => {
                        let result =
                            mailer::send_verification(&config, &job.recipient, &job.code).await;
                        if let Err(error) = EmailLogRepository::new(pool.clone())
                            .add(
                                None,
                                job.recipient,
                                if result.is_ok() { "sent" } else { "failed" }.into(),
                                result.err().map(|e| e.to_string()),
                            )
                            .await
                        {
                            tracing::error!(worker_id, %error, "failed to persist email log");
                        }
                    }
                    Ok(None) => tokio::time::sleep(Duration::from_millis(500)).await,
                    Err(error) => {
                        tracing::warn!(worker_id, %error, "mail queue unavailable");
                        tokio::time::sleep(Duration::from_secs(2)).await;
                    }
                }
            }
        });
    }
}
