"""Tests for Signal media delivery in send_message_tool.py."""

import asyncio
import sys
from types import ModuleType
from unittest.mock import MagicMock, AsyncMock, patch

import pytest

from gateway.config import Platform


def _make_httpx_mock():
    """Create a mock httpx module with proper sync json()."""

    class AsyncBaseTransport:
        pass

    class Proxy:
        pass

    class MockResp:
        status_code = 200
        def json(self):
            return {"timestamp": 1234567890}
        def raise_for_status(self):
            pass

    class MockClient:
        async def __aenter__(self):
            return self
        async def __aexit__(self, *a):
            pass
        async def post(self, *args, **kwargs):
            return MockResp()

    httpx_mock = ModuleType("httpx")
    httpx_mock.AsyncClient = lambda timeout=None: MockClient()
    httpx_mock.AsyncBaseTransport = AsyncBaseTransport  # Needed by Telegram adapter
    httpx_mock.Proxy = Proxy  # Needed by telegram-bot library
    return httpx_mock


@pytest.fixture(autouse=True)
def inject_httpx(monkeypatch):
    """Inject mock httpx into sys.modules before imports."""
    monkeypatch.setitem(sys.modules, "httpx", _make_httpx_mock())


class TestSendSignalMediaFiles:
    """Test that _send_signal correctly handles media_files parameter."""

    def test_send_signal_basic_text_without_media(self):
        """Backward compatibility: text-only signal messages work."""
        from tools.send_message_tool import _send_signal

        extra = {"http_url": "http://localhost:8080", "account": "+155****4567"}

        result = asyncio.run(_send_signal(extra, "+155****9999", "Hello world"))

        assert result["success"] is True
        assert result["platform"] == "signal"
        assert result["chat_id"] == "+155****9999"

    def test_send_signal_with_attachments(self, tmp_path):
        """Signal messages with media_files include attachments in JSON-RPC."""
        from tools.send_message_tool import _send_signal

        img_path = tmp_path / "test.png"
        img_path.write_bytes(b"\x89PNG")

        extra = {"http_url": "http://localhost:8080", "account": "+155****4567"}

        result = asyncio.run(
            _send_signal(extra, "+155****9999", "Check this out", media_files=[(str(img_path), False)])
        )

        assert result["success"] is True
        assert result["platform"] == "signal"

    def test_send_signal_with_missing_media_file(self):
        """Missing media files should generate warnings but not fail."""
        from tools.send_message_tool import _send_signal

        extra = {"http_url": "http://localhost:8080", "account": "+155****4567"}

        result = asyncio.run(
            _send_signal(extra, "+155****9999", "File missing?", media_files=[("/nonexistent.png", False)])
        )

        assert result["success"] is True  # Should succeed despite missing file
        assert "warnings" in result
        assert "Some media files were skipped" in str(result["warnings"])


class TestSendSignalMediaRestrictions:
    """Test that the restriction block handles Signal media correctly."""

    def test_signal_allows_text_only_media_via_send_to_platform(self):
        """Signal should accept text-only media files (no message) via _send_to_platform."""
        import httpx
        if not hasattr(httpx, 'Proxy') or not hasattr(httpx, 'URL'):
            pytest.skip("httpx type annotations incompatible with telegram library")
        from tools.send_message_tool import _send_to_platform

        mock_result = {"success": True, "platform": "signal"}
        with patch("tools.send_message_tool._send_signal", new=AsyncMock(return_value=mock_result)):
            config = MagicMock()
            config.platforms = {Platform.SIGNAL: MagicMock(enabled=True)}
            config.get_home_channel.return_value = None

            result = asyncio.run(
                _send_to_platform(
                    Platform.SIGNAL,
                    config,
                    "+155****9999",
                    "",  # Empty message - media is the message
                    media_files=[("/tmp/test.png", False)]
                )
            )

            assert result["success"] is True

    def test_non_media_platforms_reject_text_only_media(self):
        """Slack should reject text-only media (no MESSAGE content)."""
        import httpx
        if not hasattr(httpx, 'Proxy') or not hasattr(httpx, 'URL'):
            pytest.skip("httpx type annotations incompatible with telegram library")
        from tools.send_message_tool import _send_to_platform

        config = MagicMock()
        config.platforms = {Platform.SLACK: MagicMock(enabled=True)}
        config.get_home_channel.return_value = None

        # Empty message with media_files should trigger restriction block
        result = asyncio.run(
            _send_to_platform(
                Platform.SLACK,
                config,
                "C012AB3CD",
                "",  # Empty message - media is the only content
                media_files=[("/tmp/test.png", False)]
            )
        )

        assert "error" in result
        assert "only supported for" in result["error"]


class TestSendSignalMediaWarningMessages:
    """Test warning messages are updated to include signal."""

    def test_warning_includes_signal_when_media_omitted(self):
        """Non-media platforms should show a warning mentioning signal in the supported list."""
        import httpx
        if not hasattr(httpx, 'Proxy') or not hasattr(httpx, 'URL'):
            pytest.skip("httpx type annotations incompatible with telegram library")
        from tools.send_message_tool import _send_to_platform
        from hermes_cli.plugins import discover_plugins
        from gateway.platform_registry import platform_registry

        config = MagicMock()
        config.platforms = {Platform.SLACK: MagicMock(enabled=True)}
        config.get_home_channel.return_value = None

        # Slack migrated to a bundled plugin (#41112) — delivery now flows
        # through the registry's standalone_sender_fn instead of the old
        # tools.send_message_tool._send_slack helper. Patch the registry entry's
        # sender so the slack send succeeds and the media-omitted warning (which
        # must mention signal) gets attached to the result.
        discover_plugins()
        slack_entry = platform_registry.get("slack")
        original_sender = slack_entry.standalone_sender_fn
        slack_entry.standalone_sender_fn = AsyncMock(return_value={"success": True})
        try:
            result = asyncio.run(
                _send_to_platform(
                    Platform.SLACK,
                    config,
                    "C012AB3CD",
                    "Test message with media",
                    media_files=[("/tmp/test.png", False)]
                )
            )
        finally:
            slack_entry.standalone_sender_fn = original_sender

        assert result.get("warnings") is not None
        # Check that the warning mentions signal as supported
        found = any("signal" in w.lower() for w in result["warnings"])
        assert found, f"Expected 'signal' in warnings but got: {result.get('warnings')}"


class TestSendSignalGroupChats:
    """Test that _send_signal handles group chats correctly."""

    def test_send_signal_group_with_attachments(self, tmp_path):
        """Group chat messages with attachments should use groupId parameter."""
        from tools.send_message_tool import _send_signal

        img_path = tmp_path / "test_attachment.pdf"
        img_path.write_bytes(b"%PDF-1.4")

        extra = {"http_url": "http://localhost:8080", "account": "+155****4567"}

        result = asyncio.run(
            _send_signal(extra, "group:abc123==", "Group file", media_files=[(str(img_path), False)])
        )

        assert result["success"] is True


class TestSendSignalConfigLoading:
    """Verify Signal config loading works."""

    def test_signal_platform_exists(self):
        """Platform.SIGNAL should be a valid platform."""
        assert hasattr(Platform, "SIGNAL")
        assert Platform.SIGNAL.value == "signal"
