"""Tests for MQTTManager — connect, publish, subscribe, bytes payload, reconnect."""

from __future__ import annotations

import asyncio
import json
from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from device_agent.mqtt.client import MQTTManager


def _make_manager(**kwargs) -> MQTTManager:
    defaults = dict(
        device_id="test-device",
        mqtt_host="localhost",
        mqtt_port=1883,
        use_tls=False,
        auto_reconnect=False,
    )
    defaults.update(kwargs)
    return MQTTManager(**defaults)


def test_device_topic_prefix():
    m = _make_manager()
    assert m.device_topic_prefix == "devices/test-device"


def test_on_topic_registers_handler():
    m = _make_manager()
    handler = AsyncMock()
    m.on_topic("signaling/request", handler)
    assert "devices/test-device/signaling/request" in m._handlers
    assert handler in m._handlers["devices/test-device/signaling/request"]


@pytest.mark.asyncio
async def test_publish_raises_when_not_connected():
    m = _make_manager()
    with pytest.raises(RuntimeError, match="MQTT not connected"):
        await m.publish("info", {"type": "android"})


@pytest.mark.asyncio
async def test_publish_encodes_json():
    m = _make_manager()
    mock_client = AsyncMock()
    m._client = mock_client
    m._connected = True

    await m.publish("info", {"type": "android"})

    mock_client.publish.assert_awaited_once()
    topic, payload = mock_client.publish.call_args[0]
    assert topic == "devices/test-device/info"
    assert json.loads(payload) == {"type": "android"}


@pytest.mark.asyncio
async def test_disconnect_cancels_listen_task():
    m = _make_manager()
    m._connected = True

    mock_task = AsyncMock()
    mock_task.done.return_value = False
    mock_task.cancel = MagicMock()
    m._listen_task = mock_task

    mock_client = AsyncMock()
    m._client = mock_client

    await m.disconnect()

    mock_task.cancel.assert_called_once()
    assert m._connected is False
    assert m._client is None


def test_initial_state():
    m = _make_manager()
    assert m.connected is False
    assert m._client is None
    assert m._shutting_down is False


def test_auto_reconnect_default():
    m = MQTTManager(
        device_id="d", mqtt_host="h", mqtt_port=1883, use_tls=False,
    )
    assert m._auto_reconnect is True
