//! Job Queue Service
//!
//! Provides job queueing functionality using Apalis and Redis/Valkey.
//! Used to enqueue background jobs for notification processing and delivery.

use apalis::prelude::*;
use apalis_redis::{RedisConfig, RedisStorage};
use std::sync::Arc;
use tokio::sync::RwLock;
use uuid::Uuid;

use crate::jobs::{DeliverNotificationJob, ProcessEventJob, DELIVER_NOTIFICATION_QUEUE, PROCESS_EVENT_QUEUE};

/// Job queue service for enqueueing background jobs
pub struct JobQueue {
    event_storage: Arc<RwLock<RedisStorage<ProcessEventJob>>>,
    delivery_storage: Arc<RwLock<RedisStorage<DeliverNotificationJob>>>,
}

impl Clone for JobQueue {
    fn clone(&self) -> Self {
        Self {
            event_storage: Arc::clone(&self.event_storage),
            delivery_storage: Arc::clone(&self.delivery_storage),
        }
    }
}

impl JobQueue {
    /// Create a new job queue connected to Redis/Valkey
    pub async fn new(redis_url: &str) -> anyhow::Result<Self> {
        let conn = apalis_redis::connect(redis_url).await?;

        let event_storage = RedisStorage::new_with_config(
            conn.clone(),
            RedisConfig::default().set_namespace(PROCESS_EVENT_QUEUE),
        );

        let delivery_storage = RedisStorage::new_with_config(
            conn,
            RedisConfig::default().set_namespace(DELIVER_NOTIFICATION_QUEUE),
        );

        Ok(Self {
            event_storage: Arc::new(RwLock::new(event_storage)),
            delivery_storage: Arc::new(RwLock::new(delivery_storage)),
        })
    }

    /// Queue a job to process an event and create notifications
    pub async fn queue_process_event(&self, event_id: Uuid) -> anyhow::Result<()> {
        let job = ProcessEventJob::new(event_id);
        let mut storage = self.event_storage.write().await;
        storage.push(job).await?;
        tracing::debug!(event_id = %event_id, "Queued ProcessEventJob");
        Ok(())
    }

    /// Queue a job to deliver a notification
    pub async fn queue_deliver_notification(&self, delivery_id: Uuid) -> anyhow::Result<()> {
        let job = DeliverNotificationJob::new(delivery_id);
        let mut storage = self.delivery_storage.write().await;
        storage.push(job).await?;
        tracing::debug!(delivery_id = %delivery_id, "Queued DeliverNotificationJob");
        Ok(())
    }

    /// Queue a retry for notification delivery with incremented attempt
    pub async fn queue_delivery_retry(&self, delivery_id: Uuid, attempt: u32) -> anyhow::Result<()> {
        let job = DeliverNotificationJob::with_attempt(delivery_id, attempt);
        let mut storage = self.delivery_storage.write().await;
        storage.push(job).await?;
        tracing::debug!(delivery_id = %delivery_id, attempt = attempt, "Queued DeliverNotificationJob retry");
        Ok(())
    }

    /// Get the event storage for worker registration
    pub fn event_storage(&self) -> Arc<RwLock<RedisStorage<ProcessEventJob>>> {
        Arc::clone(&self.event_storage)
    }

    /// Get the delivery storage for worker registration
    pub fn delivery_storage(&self) -> Arc<RwLock<RedisStorage<DeliverNotificationJob>>> {
        Arc::clone(&self.delivery_storage)
    }
}
