"""
Tests for RabbitMQ client.
"""

import pytest
import json
from unittest.mock import Mock, patch, MagicMock
import pika

from app.queues.rabbitmq_client import RabbitMQClient, create_rabbitmq_client


class TestRabbitMQClient:
    """Tests for RabbitMQClient class."""
    
    def test_initialization(self):
        """Test RabbitMQ client initialization."""
        client = RabbitMQClient(
            rabbitmq_url="amqp://guest:guest@localhost:5672",
            prefetch_count=1
        )
        
        assert client.rabbitmq_url == "amqp://guest:guest@localhost:5672"
        assert client.prefetch_count == 1
        assert client.connection is None
        assert client.channel is None
        assert client.queues == {
            'scraping_tasks': 'scraping_tasks',
            'sentiment_analysis_tasks': 'sentiment_analysis_tasks',
            'energy_calculation_tasks': 'energy_calculation_tasks',
            'results': 'results'
        }
    
    @patch('pika.BlockingConnection')
    def test_connect_success(self, mock_connection):
        """Test successful connection to RabbitMQ."""
        # Setup mocks
        mock_conn_instance = Mock()
        mock_channel_instance = Mock()
        mock_connection.return_value = mock_conn_instance
        mock_conn_instance.channel.return_value = mock_channel_instance
        
        # Create client and connect
        client = RabbitMQClient()
        client.connect()
        
        # Verify connection and channel created
        mock_connection.assert_called_once()
        mock_conn_instance.channel.assert_called_once()
        mock_channel_instance.basic_qos.assert_called_once_with(prefetch_count=1)
        
        # Verify queues declared
        assert mock_channel_instance.queue_declare.call_count == 4
        
        # Verify client state
        assert client.connection == mock_conn_instance
        assert client.channel == mock_channel_instance
    
    @patch('pika.BlockingConnection')
    def test_publish_message(self, mock_connection):
        """Test publishing a message to a queue."""
        # Setup mocks
        mock_conn_instance = Mock()
        mock_channel_instance = Mock()
        mock_connection.return_value = mock_conn_instance
        mock_conn_instance.channel.return_value = mock_channel_instance
        
        # Create client and connect
        client = RabbitMQClient()
        client.connect()
        
        # Publish message
        client.publish_message(
            queue_name='scraping_tasks',
            data={'match_id': 123, 'source': 'twitter'},
            event_type='scraping.task.created'
        )
        
        # Verify publish called
        mock_channel_instance.basic_publish.assert_called_once()
        call_args = mock_channel_instance.basic_publish.call_args
        
        # Check routing key
        assert call_args[1]['routing_key'] == 'scraping_tasks'
        
        # Check message body is valid JSON
        message_body = call_args[1]['body']
        message = json.loads(message_body)
        
        assert message['event'] == 'scraping.task.created'
        assert message['version'] == '1.0'
        assert message['data']['match_id'] == 123
        assert message['data']['source'] == 'twitter'
        assert 'timestamp' in message
        
        # Check message properties
        properties = call_args[1]['properties']
        assert properties.delivery_mode == 2  # Persistent
    
    @patch('pika.BlockingConnection')
    def test_consume_messages(self, mock_connection):
        """Test consuming messages from a queue."""
        # Setup mocks
        mock_conn_instance = Mock()
        mock_channel_instance = Mock()
        mock_connection.return_value = mock_conn_instance
        mock_conn_instance.channel.return_value = mock_channel_instance
        
        # Create client and connect
        client = RabbitMQClient()
        client.connect()
        
        # Mock start_consuming to avoid blocking
        mock_channel_instance.start_consuming = Mock(side_effect=KeyboardInterrupt())
        
        # Define callback
        callback = Mock()
        
        # Consume messages
        try:
            client.consume_messages(queue_name='scraping_tasks', callback=callback)
        except KeyboardInterrupt:
            pass
        
        # Verify consume called
        mock_channel_instance.basic_consume.assert_called_once()
        call_args = mock_channel_instance.basic_consume.call_args
        
        assert call_args[1]['queue'] == 'scraping_tasks'
        assert call_args[1]['auto_ack'] is False
        assert call_args[1]['on_message_callback'] == callback
        
        # Verify start_consuming called
        mock_channel_instance.start_consuming.assert_called_once()
    
    @patch('pika.BlockingConnection')
    def test_ack_message(self, mock_connection):
        """Test acknowledging a message."""
        # Setup mocks
        mock_conn_instance = Mock()
        mock_channel_instance = Mock()
        mock_connection.return_value = mock_conn_instance
        mock_conn_instance.channel.return_value = mock_channel_instance
        
        # Create client and connect
        client = RabbitMQClient()
        client.connect()
        
        # Acknowledge message
        client.ack_message(delivery_tag=123)
        
        # Verify ack called
        mock_channel_instance.basic_ack.assert_called_once_with(delivery_tag=123)
    
    @patch('pika.BlockingConnection')
    def test_reject_message(self, mock_connection):
        """Test rejecting a message."""
        # Setup mocks
        mock_conn_instance = Mock()
        mock_channel_instance = Mock()
        mock_connection.return_value = mock_conn_instance
        mock_conn_instance.channel.return_value = mock_channel_instance
        
        # Create client and connect
        client = RabbitMQClient()
        client.connect()
        
        # Reject message without requeue
        client.reject_message(delivery_tag=123, requeue=False)
        
        # Verify reject called
        mock_channel_instance.basic_reject.assert_called_once_with(
            delivery_tag=123,
            requeue=False
        )
    
    @patch('pika.BlockingConnection')
    def test_close_connection(self, mock_connection):
        """Test closing connection to RabbitMQ."""
        # Setup mocks
        mock_conn_instance = Mock()
        mock_channel_instance = Mock()
        mock_connection.return_value = mock_conn_instance
        mock_conn_instance.channel.return_value = mock_channel_instance
        
        # Create client and connect
        client = RabbitMQClient()
        client.connect()
        
        # Close connection
        client.close()
        
        # Verify channel and connection closed
        mock_channel_instance.close.assert_called_once()
        mock_conn_instance.close.assert_called_once()


class TestCreateRabbitMQClient:
    """Tests for create_rabbitmq_client factory function."""
    
    def test_create_with_defaults(self):
        """Test creating client with default parameters."""
        client = create_rabbitmq_client()
        
        assert client.rabbitmq_url == "amqp://guest:guest@localhost:5672"
        assert client.prefetch_count == 1
        assert isinstance(client, RabbitMQClient)
    
    def test_create_with_custom_url(self):
        """Test creating client with custom URL."""
        client = create_rabbitmq_client(
            rabbitmq_url="amqp://user:pass@remote:5672"
        )
        
        assert client.rabbitmq_url == "amqp://user:pass@remote:5672"
        assert client.prefetch_count == 1
    
    def test_create_with_custom_prefetch(self):
        """Test creating client with custom prefetch count."""
        client = create_rabbitmq_client(prefetch_count=5)
        
        assert client.rabbitmq_url == "amqp://guest:guest@localhost:5672"
        assert client.prefetch_count == 5
