//! Message queue consumer implementation
//!
//! This module contains intentional violations for Aphoria dogfood testing.

use crate::config::ConsumerConfig;
use crate::error::ConsumerError;
use futures_lite::StreamExt;
use lapin::{Channel, Connection, Consumer as LapinConsumer};
use std::sync::Arc;
use tracing::{error, info, warn};

/// Acknowledgment mode for message processing
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AckMode {
    /// Automatically acknowledge messages before processing
    AutoAck,
    /// Manually acknowledge after successful processing
    ManualAck,
}

/// Consumer for processing messages from a queue
pub struct Consumer {
    config: Arc<ConsumerConfig>,
    connection: Option<Connection>,
    channel: Option<Channel>,
    consumer: Option<LapinConsumer>,

    // ==================== VIOLATION 4: Auto-Ack Without Processing ====================
    /// Acknowledgment mode
    ///
    /// @aphoria:claim[safety] Auto-ack MUST only be used with guaranteed processing -- auto-ack before processing causes data loss on crash
    pub ack_mode: AckMode,
    // VIOLATION: Set to AutoAck by default

    // ==================== VIOLATION 5: No Requeue Limit ====================
    /// Maximum number of times to requeue a failed message (None = infinite)
    ///
    /// @aphoria:claim[safety] Requeue attempts MUST be bounded (3-5 recommended) -- infinite requeues create poison message loops
    pub max_requeue_count: Option<u32>,
    // VIOLATION: Set to None (infinite requeues)

    /// Dead letter exchange for failed messages
    pub dead_letter_exchange: Option<String>,
}

impl Consumer {
    /// Create a new consumer with the given configuration
    pub fn new(config: ConsumerConfig) -> Self {
        Self {
            config: Arc::new(config),
            connection: None,
            channel: None,
            consumer: None,

            // ❌ VIOLATION 4: Auto-ack without processing guarantee
            ack_mode: AckMode::AutoAck,

            // ❌ VIOLATION 5: No requeue limit
            max_requeue_count: None,

            dead_letter_exchange: None,
        }
    }

    /// Connect to the broker
    pub async fn connect(&mut self) -> Result<(), ConsumerError> {
        info!("Connecting to broker: {}", self.config.broker_url);

        let connection = Connection::connect(
            &self.config.broker_url,
            lapin::ConnectionProperties::default(),
        )
        .await
        .map_err(|e| ConsumerError::ConnectionFailed(e.to_string()))?;

        let channel = connection
            .create_channel()
            .await
            .map_err(|e| ConsumerError::ChannelCreationFailed(e.to_string()))?;

        // Set QoS prefetch count
        channel
            .basic_qos(self.config.prefetch_count, Default::default())
            .await
            .map_err(|e| ConsumerError::QoSFailed(e.to_string()))?;

        self.connection = Some(connection);
        self.channel = Some(channel);

        info!("Connected successfully");
        Ok(())
    }

    /// Start consuming messages from the queue
    pub async fn start_consuming(&mut self) -> Result<(), ConsumerError> {
        let channel = self
            .channel
            .as_ref()
            .ok_or_else(|| ConsumerError::NotConnected)?;

        info!("Starting consumer on queue: {}", self.config.queue_name);

        let consumer = channel
            .basic_consume(
                &self.config.queue_name,
                "consumer_tag",
                Default::default(),
                Default::default(),
            )
            .await
            .map_err(|e| ConsumerError::ConsumeFailed(e.to_string()))?;

        self.consumer = Some(consumer);

        info!("Consumer started");
        Ok(())
    }

    /// Process incoming messages
    pub async fn process_messages<F>(&mut self, mut handler: F) -> Result<(), ConsumerError>
    where
        F: FnMut(&[u8]) -> Result<(), String>,
    {
        let mut consumer = self
            .consumer
            .take()
            .ok_or_else(|| ConsumerError::NotConnected)?;

        while let Some(delivery) = consumer.next().await {
            match delivery {
                Ok(delivery) => {
                    let data = &delivery.data;

                    // Process message
                    match handler(data) {
                        Ok(_) => {
                            // Only ack if in manual mode
                            if self.ack_mode == AckMode::ManualAck {
                                if let Err(e) = delivery.ack(Default::default()).await {
                                    error!("Failed to ack message: {}", e);
                                }
                            }
                            // If AutoAck, message was already acked before processing!
                        }
                        Err(e) => {
                            error!("Handler failed: {}", e);

                            // Should we requeue?
                            // VIOLATION: No requeue limit check!
                            // This will requeue indefinitely if max_requeue_count is None
                            if let Err(e) = delivery.nack(Default::default()).await {
                                error!("Failed to nack message: {}", e);
                            }
                        }
                    }
                }
                Err(e) => {
                    error!("Error receiving message: {}", e);
                    return Err(ConsumerError::ReceiveFailed(e.to_string()));
                }
            }
        }

        Ok(())
    }

    /// Disconnect from the broker
    pub async fn disconnect(&mut self) -> Result<(), ConsumerError> {
        info!("Disconnecting from broker");

        if let Some(channel) = self.channel.take() {
            if let Err(e) = channel.close(200, "Normal shutdown").await {
                warn!("Error closing channel: {}", e);
            }
        }

        if let Some(connection) = self.connection.take() {
            if let Err(e) = connection.close(200, "Normal shutdown").await {
                warn!("Error closing connection: {}", e);
            }
        }

        info!("Disconnected");
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_consumer_creation() {
        let config = ConsumerConfig::default();
        let consumer = Consumer::new(config);

        assert_eq!(consumer.ack_mode, AckMode::AutoAck);
        assert_eq!(consumer.max_requeue_count, None);
    }

    #[test]
    fn test_ack_modes() {
        assert_eq!(AckMode::AutoAck, AckMode::AutoAck);
        assert_ne!(AckMode::AutoAck, AckMode::ManualAck);
    }
}
