"""Tests for MQTT client module."""

import threading
import time
from unittest.mock import MagicMock, patch

import pytest

from voicemqtt.mqtt_client import MQTTActivator


class TestMQTTActivator:
    """Tests for MQTTActivator class."""
    
    def test_init_default_values(self):
        """Test initialization with default values."""
        activator = MQTTActivator()
        assert activator.host == "localhost"
        assert activator.port == 1883
        assert activator.topic == "voicemqtt/activate"
        assert activator.timeout == 5.0
        assert activator.activated is False
        assert activator.connected is False
    
    def test_init_custom_values(self):
        """Test initialization with custom values."""
        activator = MQTTActivator(
            host="192.168.1.5",
            port=1884,
            topic="custom/topic",
            timeout=10.0
        )
        assert activator.host == "192.168.1.5"
        assert activator.port == 1884
        assert activator.topic == "custom/topic"
        assert activator.timeout == 10.0
    
    @patch("voicemqtt.mqtt_client.mqtt.Client")
    def test_connect_success(self, mock_client_class):
        """Test successful connection."""
        mock_client = MagicMock()
        mock_client_class.return_value = mock_client
        
        activator = MQTTActivator()
        
        # Simulate successful connection in background
        def set_connected():
            time.sleep(0.1)
            activator.connected = True
        
        threading.Thread(target=set_connected).start()
        
        result = activator.connect()
        
        assert result is True
        mock_client.connect.assert_called_once_with("localhost", 1883, 60)
        mock_client.loop_start.assert_called_once()
    
    @patch("voicemqtt.mqtt_client.mqtt.Client")
    def test_connect_failure(self, mock_client_class):
        """Test connection failure."""
        mock_client = MagicMock()
        mock_client_class.return_value = mock_client
        mock_client.connect.side_effect = Exception("Connection refused")
        
        activator = MQTTActivator(timeout=0.1)
        result = activator.connect()
        
        assert result is False
    
    @patch("voicemqtt.mqtt_client.mqtt.Client")
    def test_on_connect_success(self, mock_client_class):
        """Test on_connect callback with success."""
        mock_client = MagicMock()
        mock_client_class.return_value = mock_client
        
        activator = MQTTActivator()
        activator._on_connect(mock_client, None, {}, 0)
        
        assert activator.connected is True
        mock_client.subscribe.assert_called_once_with("voicemqtt/activate")
    
    @patch("voicemqtt.mqtt_client.mqtt.Client")
    def test_on_connect_failure(self, mock_client_class):
        """Test on_connect callback with failure."""
        mock_client = MagicMock()
        mock_client_class.return_value = mock_client
        
        activator = MQTTActivator()
        activator._on_connect(mock_client, None, {}, 1)
        
        assert activator.connected is False
    
    def test_on_message_activation(self):
        """Test on_message with activation command."""
        activator = MQTTActivator()
        
        # Create mock message
        mock_msg = MagicMock()
        mock_msg.topic = "voicemqtt/activate"
        
        # Test various activation payloads
        activation_commands = [b"record", b"start", b"on", b"1", b"true", b"yes"]
        
        for cmd in activation_commands:
            activator.activated = False
            mock_msg.payload = cmd
            activator._on_message(None, None, mock_msg)
            assert activator.activated is True, f"Failed for command: {cmd}"
    
    def test_on_message_non_activation(self):
        """Test on_message with non-activation command."""
        activator = MQTTActivator()
        
        mock_msg = MagicMock()
        mock_msg.topic = "voicemqtt/activate"
        mock_msg.payload = b"stop"
        
        activator._on_message(None, None, mock_msg)
        assert activator.activated is False
    
    def test_wait_for_activation(self):
        """Test waiting for activation."""
        activator = MQTTActivator()
        
        # Set activated in background thread
        def activate():
            time.sleep(0.1)
            activator.activated = True
        
        threading.Thread(target=activate).start()
        
        result = activator.wait_for_activation()
        
        assert result is True
        assert activator.activated is False  # Should be reset
    
    def test_is_activated(self):
        """Test is_activated method."""
        activator = MQTTActivator()
        
        # Test when activated
        activator.activated = True
        result = activator.is_activated()
        assert result is True
        assert activator.activated is False  # Should be reset
        
        # Test when not activated
        result = activator.is_activated()
        assert result is False
    
    @patch("voicemqtt.mqtt_client.mqtt.Client")
    def test_disconnect(self, mock_client_class):
        """Test disconnection."""
        mock_client = MagicMock()
        mock_client_class.return_value = mock_client
        
        activator = MQTTActivator()
        activator.client = mock_client
        
        activator.disconnect()
        
        mock_client.loop_stop.assert_called_once()
        mock_client.disconnect.assert_called_once()

    @patch("voicemqtt.mqtt_client.mqtt.Client")
    def test_publish_success(self, mock_client_class):
        """Test publishing message when connected."""
        mock_client = MagicMock()
        mock_client_class.return_value = mock_client
        mock_result = MagicMock()
        mock_result.rc = 0
        mock_client.publish.return_value = mock_result
        
        activator = MQTTActivator()
        activator.client = mock_client
        activator.connected = True
        
        result = activator.publish("test/topic", "1")
        
        assert result is True
        mock_client.publish.assert_called_once_with("test/topic", "1", qos=0, retain=False)

    @patch("voicemqtt.mqtt_client.mqtt.Client")
    def test_publish_failure_not_connected(self, mock_client_class):
        """Test publishing when not connected."""
        mock_client = MagicMock()
        mock_client_class.return_value = mock_client
        
        activator = MQTTActivator()
        activator.client = mock_client
        activator.connected = False
        
        result = activator.publish("test/topic", "1")
        
        assert result is False
        mock_client.publish.assert_not_called()

    @patch("voicemqtt.mqtt_client.mqtt.Client")
    def test_publish_failure_return_code(self, mock_client_class):
        """Test publishing with error return code."""
        mock_client = MagicMock()
        mock_client_class.return_value = mock_client
        mock_result = MagicMock()
        mock_result.rc = 1  # Error code
        mock_client.publish.return_value = mock_result
        
        activator = MQTTActivator()
        activator.client = mock_client
        activator.connected = True
        
        result = activator.publish("test/topic", "1")
        
        assert result is False
