"""Tests for messaging/ module."""

import json
from unittest.mock import patch

import pytest

# --- Existing Tests ---


class TestMessagingModels:
    """Test messaging models."""

    def test_incoming_message_creation(self):
        """Test IncomingMessage dataclass."""
        from messaging.models import IncomingMessage

        msg = IncomingMessage(
            text="Hello",
            chat_id="123",
            user_id="456",
            message_id="789",
            platform="telegram",
        )
        assert msg.text == "Hello"
        assert msg.chat_id == "123"
        assert msg.platform == "telegram"
        assert msg.is_reply() is False

    def test_incoming_message_with_reply(self):
        """Test IncomingMessage as a reply."""
        from messaging.models import IncomingMessage

        msg = IncomingMessage(
            text="Reply text",
            chat_id="123",
            user_id="456",
            message_id="789",
            platform="discord",
            reply_to_message_id="100",
        )
        assert msg.is_reply() is True
        assert msg.reply_to_message_id == "100"


class TestMessagingBase:
    """Test MessagingPlatform ABC."""

    def test_platform_is_abstract(self):
        """Verify MessagingPlatform cannot be instantiated."""
        from messaging.platforms.base import MessagingPlatform

        with pytest.raises(TypeError):
            MessagingPlatform()


class TestSessionStore:
    """Test SessionStore."""

    def test_session_store_init(self, tmp_path):
        """Test SessionStore initialization."""
        from messaging.session import SessionStore

        store = SessionStore(storage_path=str(tmp_path / "sessions.json"))
        assert store._trees == {}

    # --- Tree Tests ---

    def test_save_and_get_tree(self, tmp_path):
        """Test saving and retrieving trees."""
        from messaging.session import SessionStore

        store = SessionStore(storage_path=str(tmp_path / "sessions.json"))

        tree_data = {
            "root": "r1",
            "nodes": {"r1": {"content": "root"}, "n1": {"content": "child"}},
        }
        store.save_tree("r1", tree_data)

        loaded = store.get_tree("r1")
        assert loaded == tree_data

        # Verify node mapping
        node_map = store.get_node_mapping()
        assert node_map["r1"] == "r1"
        assert node_map["n1"] == "r1"

    def test_register_node(self, tmp_path):
        """Test manual node registration."""
        from messaging.session import SessionStore

        store = SessionStore(storage_path=str(tmp_path / "sessions.json"))
        store.register_node("n_manual", "r_manual")
        assert store.get_node_mapping()["n_manual"] == "r_manual"

    # --- Persistence & Edge Cases ---

    def test_load_existing_file_with_trees(self, tmp_path):
        """Test loading file with trees (legacy sessions ignored)."""
        from messaging.session import SessionStore

        data = {
            "sessions": {},
            "trees": {"r1": {"root_id": "r1", "nodes": {"r1": {}}}},
            "node_to_tree": {"r1": "r1"},
            "message_log": {},
        }

        p = tmp_path / "sessions.json"
        with open(p, "w") as f:
            json.dump(data, f)

        store = SessionStore(storage_path=str(p))
        assert store.get_tree("r1") is not None

    def test_load_corrupt_file(self, tmp_path):
        """Test loading corrupt/invalid json file."""
        p = tmp_path / "sessions.json"
        with open(p, "w") as f:
            f.write("{invalid json")

        from messaging.session import SessionStore

        # Should log error and start empty, avoiding crash
        store = SessionStore(storage_path=str(p))
        assert store._trees == {}

    def test_save_error_handling(self, tmp_path):
        """Test error during save."""
        from messaging.session import SessionStore

        store = SessionStore(storage_path=str(tmp_path / "sessions.json"))
        store.save_tree("r1", {"root_id": "r1", "nodes": {"r1": {}}})

        # Mock open to raise exception
        with patch("builtins.open", side_effect=OSError("Disk full")):
            store.save_tree("r2", {"root_id": "r2", "nodes": {"r2": {}}})

        # Should log error but not crash. Tree should be in memory.
        assert "r2" in store._trees


class TestTreeQueueManager:
    """Test TreeQueueManager."""

    def test_tree_queue_manager_init(self):
        """Test TreeQueueManager initialization."""
        from messaging.trees.queue_manager import TreeQueueManager

        mgr = TreeQueueManager()
        assert mgr.get_tree_count() == 0

    def test_tree_not_busy_initially(self):
        """Test tree is not busy when no messages."""
        from messaging.trees.queue_manager import TreeQueueManager

        mgr = TreeQueueManager()
        assert mgr.is_tree_busy("nonexistent") is False

    def test_get_queue_size_empty(self):
        """Test queue size is 0 for non-existent node."""
        from messaging.trees.queue_manager import TreeQueueManager

        mgr = TreeQueueManager()
        assert mgr.get_queue_size("nonexistent") == 0

    @pytest.mark.asyncio
    async def test_create_tree_and_enqueue(self):
        """Test creating a tree and enqueueing."""
        from messaging.models import IncomingMessage
        from messaging.trees.queue_manager import TreeQueueManager

        mgr = TreeQueueManager()
        processed = []

        async def processor(node_id, node):
            processed.append(node_id)

        incoming = IncomingMessage(
            text="test", chat_id="1", user_id="1", message_id="1", platform="test"
        )

        await mgr.create_tree("1", incoming, "status_1")
        was_queued = await mgr.enqueue("1", processor)

        # First message should process immediately, not queue
        assert was_queued is False

    @pytest.mark.asyncio
    async def test_cancel_tree_empty(self):
        """Test cancelling non-existent tree."""
        from messaging.trees.queue_manager import TreeQueueManager

        mgr = TreeQueueManager()
        cancelled = await mgr.cancel_tree("nonexistent")
        assert cancelled == []
