"""Matrix client validation: room_id format and quoting."""

from __future__ import annotations

import aiohttp
import pytest
from aioresponses import aioresponses

from notify_bridge_core.notifications.matrix.client import MatrixClient


HOMESERVER = "https://matrix.example.com"
TOKEN = "secret-bearer-token-1234567890"


@pytest.mark.asyncio
async def test_rejects_path_injection_room_id() -> None:
    async with aiohttp.ClientSession() as sess:
        client = MatrixClient(sess, HOMESERVER, TOKEN)
        result = await client.send_message("!abc:host/../../etc/passwd", "hi")
    assert result["success"] is False
    assert "room_id" in result["error"]


@pytest.mark.asyncio
async def test_rejects_empty_room_id() -> None:
    async with aiohttp.ClientSession() as sess:
        client = MatrixClient(sess, HOMESERVER, TOKEN)
        result = await client.send_message("", "hi")
    assert result["success"] is False
    assert "room_id" in result["error"]


@pytest.mark.asyncio
async def test_rejects_unicode_control_chars_in_room_id() -> None:
    async with aiohttp.ClientSession() as sess:
        client = MatrixClient(sess, HOMESERVER, TOKEN)
        result = await client.send_message("!abc\x00:host", "hi")
    assert result["success"] is False


@pytest.mark.asyncio
async def test_url_encodes_room_id_special_chars() -> None:
    """``!`` and ``:`` must reach the server URL-encoded."""
    captured: list[str] = []

    with aioresponses() as mocked:
        # Match any PUT under the rooms path; capture the URL we got.
        mocked.put(
            "https://matrix.example.com/_matrix/client/v3/rooms/%21abc%3Ahost.example/send/m.room.message",
            status=200, body='{}', repeat=True,
        )
        # aioresponses doesn't expose URL templates well, so use a regex mock.
        import re
        mocked.put(
            re.compile(r"https://matrix\.example\.com/_matrix/client/v3/rooms/[^/]+/send/m\.room\.message/.*"),
            status=200, body='{}', repeat=True,
        )

        async with aiohttp.ClientSession() as sess:
            client = MatrixClient(sess, HOMESERVER, TOKEN)
            result = await client.send_message("!abc:host.example", "hi")

    assert result["success"] is True


@pytest.mark.asyncio
async def test_redacts_bearer_in_error() -> None:
    """A 4xx response body must not echo the Authorization Bearer back to caller."""
    import re
    with aioresponses() as mocked:
        mocked.put(
            re.compile(r".*"),
            status=403,
            body='{"errcode": "M_FORBIDDEN", "Authorization": "Bearer ' + TOKEN + '"}',
            repeat=True,
        )

        async with aiohttp.ClientSession() as sess:
            client = MatrixClient(sess, HOMESERVER, TOKEN)
            result = await client.send_message("!abc:host.example", "hi")

    assert result["success"] is False
    assert TOKEN not in result["error"]
