"""Tests for user authorization mechanism."""

from __future__ import annotations

from pathlib import Path

import pytest

import mindroom.authorization
from mindroom import constants
from mindroom.config.auth import AuthorizationConfig
from mindroom.config.main import Config
from mindroom.constants import ORIGINAL_SENDER_KEY, ROUTER_AGENT_NAME, resolve_runtime_paths
from mindroom.matrix.state import MatrixRoom, MatrixState

_BOUND_RUNTIME_PATHS: dict[int, constants.RuntimePaths] = {}


def _bind_runtime_paths(config: Config, path: Path | None = None) -> Config:
    runtime_paths = constants.resolve_runtime_paths(
        config_path=(path or Path("config.yaml")),
        storage_path=Path("mindroom_data"),
        process_env={
            "MATRIX_HOMESERVER": "https://example.com",
            "MINDROOM_NAMESPACE": "",
        },
    )
    bound = Config.validate_with_runtime(config.model_dump(exclude_none=True), runtime_paths)
    _BOUND_RUNTIME_PATHS[id(bound)] = runtime_paths
    return bound


def _runtime_paths_for(config: Config) -> constants.RuntimePaths:
    runtime_paths = _BOUND_RUNTIME_PATHS.get(id(config))
    if runtime_paths is None:
        msg = "Test config is missing bound RuntimePaths"
        raise KeyError(msg)
    return runtime_paths


def is_authorized_sender(
    sender_id: str,
    config: Config,
    room_id: str,
    *,
    room_alias: str | None = None,
) -> bool:
    """Run sender authorization with the test config's bound runtime context."""
    return mindroom.authorization.is_authorized_sender(
        sender_id,
        config,
        room_id,
        _runtime_paths_for(config),
        room_alias=room_alias,
    )


def is_sender_allowed_for_agent_reply(sender_id: str, agent_name: str, config: Config) -> bool:
    """Run reply-permission checks with the test config's bound runtime context."""
    return mindroom.authorization.is_sender_allowed_for_agent_reply(
        sender_id,
        agent_name,
        config,
        _runtime_paths_for(config),
    )


def get_effective_sender_id_for_reply_permissions(
    sender_id: str,
    event_source: dict[str, object] | None,
    config: Config,
) -> str:
    """Resolve effective sender IDs with the test config's bound runtime context."""
    return mindroom.authorization.get_effective_sender_id_for_reply_permissions(
        sender_id,
        event_source,
        config,
        _runtime_paths_for(config),
    )


def _config(**kwargs: object) -> Config:
    return _bind_runtime_paths(Config(**kwargs))


@pytest.fixture
def mock_config_no_restrictions() -> Config:
    """Config with no authorized users (defaults to only internal system user)."""
    return _bind_runtime_paths(
        Config(
            agents={
                "assistant": {
                    "display_name": "Assistant",
                    "role": "Test assistant",
                    "rooms": ["test_room"],
                },
            },
            teams={
                "test_team": {
                    "display_name": "Test Team",
                    "role": "Test team",
                    "agents": ["assistant"],
                    "rooms": ["test_room"],
                },
            },
            mindroom_user={"username": "mindroom_user", "display_name": "MindRoomUser"},
            # No authorization field means default empty authorization
        ),
    )


@pytest.fixture
def mock_config_with_restrictions() -> Config:
    """Config with authorization restrictions."""
    return _bind_runtime_paths(
        Config(
            agents={
                "assistant": {
                    "display_name": "Assistant",
                    "role": "Test assistant",
                    "rooms": ["test_room"],
                },
                "analyst": {
                    "display_name": "Analyst",
                    "role": "Test analyst",
                    "rooms": ["test_room"],
                },
            },
            teams={
                "test_team": {
                    "display_name": "Test Team",
                    "role": "Test team",
                    "agents": ["assistant"],
                    "rooms": ["test_room"],
                },
            },
            mindroom_user={"username": "mindroom_user", "display_name": "MindRoomUser"},
            authorization={
                "global_users": ["@alice:example.com", "@bob:example.com"],
                "room_permissions": {},
                "default_room_access": False,
            },
        ),
    )


def test_no_restrictions_only_allows_internal_user(
    mock_config_no_restrictions: Config,
) -> None:
    """Test that empty authorized_users list only allows internal system user and agents."""
    # Random users should NOT be allowed
    assert not is_authorized_sender("@random_user:example.com", mock_config_no_restrictions, "!test:server")
    assert not is_authorized_sender("@another_user:different.com", mock_config_no_restrictions, "!test:server")

    # Agents should still be allowed
    assert is_authorized_sender(
        mock_config_no_restrictions.get_ids(_runtime_paths_for(mock_config_no_restrictions))["assistant"].full_id,
        mock_config_no_restrictions,
        "!test:server",
    )

    # Internal system user should always be allowed
    assert is_authorized_sender(
        mock_config_no_restrictions.get_mindroom_user_id(_runtime_paths_for(mock_config_no_restrictions)),
        mock_config_no_restrictions,
        "!test:server",
    )


def test_authorized_users_allowed(mock_config_with_restrictions: Config) -> None:
    """Test that users in the authorized_users list are allowed."""
    assert is_authorized_sender("@alice:example.com", mock_config_with_restrictions, "!test:server")
    assert is_authorized_sender("@bob:example.com", mock_config_with_restrictions, "!test:server")


def test_unauthorized_users_blocked(mock_config_with_restrictions: Config) -> None:
    """Test that users NOT in the authorized_users list are blocked."""
    assert not is_authorized_sender("@charlie:example.com", mock_config_with_restrictions, "!test:server")
    assert not is_authorized_sender("@random_user:example.com", mock_config_with_restrictions, "!test:server")


def test_agents_always_allowed(mock_config_with_restrictions: Config) -> None:
    """Test that configured agents are always allowed regardless of authorized_users."""
    # Configured agents should be allowed
    assert is_authorized_sender(
        mock_config_with_restrictions.get_ids(_runtime_paths_for(mock_config_with_restrictions))["assistant"].full_id,
        mock_config_with_restrictions,
        "!test:server",
    )
    assert is_authorized_sender(
        mock_config_with_restrictions.get_ids(_runtime_paths_for(mock_config_with_restrictions))["analyst"].full_id,
        mock_config_with_restrictions,
        "!test:server",
    )

    # Non-configured agent should be blocked
    assert not is_authorized_sender("@mindroom_unknown:example.com", mock_config_with_restrictions, "!test:server")


def test_teams_always_allowed(mock_config_with_restrictions: Config) -> None:
    """Test that configured teams are always allowed regardless of authorized_users."""
    # Configured team should be allowed
    assert is_authorized_sender(
        mock_config_with_restrictions.get_ids(_runtime_paths_for(mock_config_with_restrictions))["test_team"].full_id,
        mock_config_with_restrictions,
        "!test:server",
    )

    # Non-configured team should be blocked
    assert not is_authorized_sender("@mindroom_unknown_team:example.com", mock_config_with_restrictions, "!test:server")


def test_router_always_allowed(mock_config_with_restrictions: Config) -> None:
    """Test that the router agent is always allowed."""
    # Router should always be allowed
    assert is_authorized_sender(
        mock_config_with_restrictions.get_ids(_runtime_paths_for(mock_config_with_restrictions))[
            ROUTER_AGENT_NAME
        ].full_id,
        mock_config_with_restrictions,
        "!test:server",
    )


def test_internal_system_user_always_allowed(
    mock_config_with_restrictions: Config,
) -> None:
    """Test that configured internal user on the current domain is always allowed."""
    runtime_paths = _runtime_paths_for(mock_config_with_restrictions)
    internal_user_id = mock_config_with_restrictions.get_mindroom_user_id(runtime_paths)
    assert internal_user_id is not None

    # Internal system user should always be allowed, even with restrictions
    assert is_authorized_sender(
        internal_user_id,
        mock_config_with_restrictions,
        "!test:server",
    )

    # Same username from a different domain should NOT be allowed
    current_domain = mock_config_with_restrictions.get_domain(runtime_paths)
    wrong_domain_id = internal_user_id.replace(f":{current_domain}", ":different.com")
    assert not is_authorized_sender(wrong_domain_id, mock_config_with_restrictions, "!test:server")


def test_custom_internal_system_user_always_allowed() -> None:
    """Test that custom configured internal user is always allowed."""
    config = _config(
        agents={
            "assistant": {
                "display_name": "Assistant",
                "role": "Test assistant",
                "rooms": ["test_room"],
            },
        },
        mindroom_user={
            "username": "alice_internal",
            "display_name": "Alice Internal",
        },
        authorization={
            "global_users": [],
            "room_permissions": {},
            "default_room_access": False,
        },
    )
    runtime_paths = _runtime_paths_for(config)
    internal_user_id = config.get_mindroom_user_id(runtime_paths)
    assert internal_user_id is not None
    assert is_authorized_sender(internal_user_id, config, "!test:server")
    assert not is_authorized_sender("@mindroom_user:example.com", config, "!test:server")


def test_mixed_authorization_scenarios(mock_config_with_restrictions: Config) -> None:
    """Test various mixed authorization scenarios."""
    # Authorized users - allowed
    assert is_authorized_sender("@alice:example.com", mock_config_with_restrictions, "!test:server")

    # Unauthorized users - blocked
    assert not is_authorized_sender("@eve:example.com", mock_config_with_restrictions, "!test:server")

    # Agents - allowed
    assert is_authorized_sender(
        mock_config_with_restrictions.get_ids(_runtime_paths_for(mock_config_with_restrictions))["assistant"].full_id,
        mock_config_with_restrictions,
        "!test:server",
    )

    # Teams - allowed
    assert is_authorized_sender(
        mock_config_with_restrictions.get_ids(_runtime_paths_for(mock_config_with_restrictions))["test_team"].full_id,
        mock_config_with_restrictions,
        "!test:server",
    )

    # Router - allowed
    assert is_authorized_sender(
        mock_config_with_restrictions.get_ids(_runtime_paths_for(mock_config_with_restrictions))[
            ROUTER_AGENT_NAME
        ].full_id,
        mock_config_with_restrictions,
        "!test:server",
    )

    # Unknown agent - blocked
    assert not is_authorized_sender("@mindroom_fake_agent:example.com", mock_config_with_restrictions, "!test:server")


@pytest.fixture
def mock_config_with_room_permissions() -> Config:
    """Config with room-specific permissions."""
    return _bind_runtime_paths(
        Config(
            agents={
                "assistant": {
                    "display_name": "Assistant",
                    "role": "Test assistant",
                    "rooms": ["test_room"],
                },
            },
            authorization={
                "global_users": ["@alice:example.com"],  # Alice has global access
                "room_permissions": {
                    "!room1:example.com": ["@bob:example.com", "@charlie:example.com"],
                    "!room2:example.com": ["@charlie:example.com"],
                },
                "default_room_access": False,
            },
        ),
    )


def test_room_specific_permissions(mock_config_with_room_permissions: Config) -> None:
    """Test room-specific permission system."""
    # Alice has global access - allowed everywhere
    assert is_authorized_sender("@alice:example.com", mock_config_with_room_permissions, "!room1:example.com")
    assert is_authorized_sender("@alice:example.com", mock_config_with_room_permissions, "!room2:example.com")
    assert is_authorized_sender("@alice:example.com", mock_config_with_room_permissions, "!room3:example.com")

    # Bob only has access to room1
    assert is_authorized_sender("@bob:example.com", mock_config_with_room_permissions, "!room1:example.com")
    assert not is_authorized_sender("@bob:example.com", mock_config_with_room_permissions, "!room2:example.com")
    assert not is_authorized_sender("@bob:example.com", mock_config_with_room_permissions, "!room3:example.com")

    # Charlie has access to room1 and room2
    assert is_authorized_sender("@charlie:example.com", mock_config_with_room_permissions, "!room1:example.com")
    assert is_authorized_sender("@charlie:example.com", mock_config_with_room_permissions, "!room2:example.com")
    assert not is_authorized_sender("@charlie:example.com", mock_config_with_room_permissions, "!room3:example.com")

    # Dave has no access anywhere
    assert not is_authorized_sender("@dave:example.com", mock_config_with_room_permissions, "!room1:example.com")
    assert not is_authorized_sender("@dave:example.com", mock_config_with_room_permissions, "!room2:example.com")
    assert not is_authorized_sender("@dave:example.com", mock_config_with_room_permissions, "!room3:example.com")


def test_room_specific_permissions_support_full_alias() -> None:
    """Room permissions should allow using a full Matrix room alias key."""
    config = _config(
        agents={
            "assistant": {
                "display_name": "Assistant",
                "role": "Test assistant",
                "rooms": ["lobby"],
            },
        },
        authorization={
            "room_permissions": {
                "#lobby:example.com": ["@bob:example.com"],
            },
            "default_room_access": False,
        },
    )

    assert is_authorized_sender(
        "@bob:example.com",
        config,
        "!lobby:example.com",
        room_alias="#lobby:example.com",
    )
    assert not is_authorized_sender(
        "@eve:example.com",
        config,
        "!lobby:example.com",
        room_alias="#lobby:example.com",
    )


def test_room_specific_permissions_support_managed_room_key(
    monkeypatch: pytest.MonkeyPatch,
    tmp_path: Path,
) -> None:
    """Room permissions should allow using a managed room key alias."""
    config = _config_with_runtime_paths(
        tmp_path,
        agents={
            "assistant": {
                "display_name": "Assistant",
                "role": "Test assistant",
                "rooms": ["lobby"],
            },
        },
        authorization={
            "room_permissions": {
                "lobby": ["@bob:example.com"],
            },
            "default_room_access": False,
        },
    )

    state = MatrixState(
        rooms={
            "lobby": MatrixRoom(
                room_id="!lobby:example.com",
                alias="#lobby:example.com",
                name="Lobby",
            ),
        },
    )
    monkeypatch.setattr("mindroom.authorization.MatrixState.load", lambda **_kwargs: state)

    assert is_authorized_sender("@bob:example.com", config, "!lobby:example.com")
    assert not is_authorized_sender("@eve:example.com", config, "!lobby:example.com")


def test_default_room_access() -> None:
    """Test default_room_access setting."""
    config_allow_default = _config(
        agents={
            "assistant": {
                "display_name": "Assistant",
                "role": "Test assistant",
                "rooms": ["test_room"],
            },
        },
        authorization={
            "global_users": ["@alice:example.com"],
            "room_permissions": {
                "!room1:example.com": ["@bob:example.com"],
            },
            "default_room_access": True,  # Allow by default
        },
    )

    # Alice has global access
    assert is_authorized_sender("@alice:example.com", config_allow_default, "!room1:example.com")
    assert is_authorized_sender("@alice:example.com", config_allow_default, "!room2:example.com")

    # Bob has explicit access to room1
    assert is_authorized_sender("@bob:example.com", config_allow_default, "!room1:example.com")

    # For room2 (not in room_permissions), Bob gets default access (True)
    assert is_authorized_sender("@bob:example.com", config_allow_default, "!room2:example.com")

    # Charlie has no explicit permissions but gets default access
    assert not is_authorized_sender(
        "@charlie:example.com",
        config_allow_default,
        "!room1:example.com",
    )  # Explicit empty list
    assert is_authorized_sender("@charlie:example.com", config_allow_default, "!room2:example.com")  # Default access


@pytest.fixture
def mock_config_with_aliases() -> Config:
    """Config with bridge aliases mapping."""
    return _config(
        agents={
            "assistant": {
                "display_name": "Assistant",
                "role": "Test assistant",
                "rooms": ["test_room"],
            },
        },
        authorization={
            "global_users": ["@alice:example.com"],
            "room_permissions": {
                "!room1:example.com": ["@bob:example.com"],
            },
            "default_room_access": False,
            "aliases": {
                "@alice:example.com": ["@telegram_111:example.com", "@signal_111:example.com"],
                "@bob:example.com": ["@telegram_222:example.com"],
            },
        },
    )


def test_bridge_alias_global_user(mock_config_with_aliases: Config) -> None:
    """Test that a bridge alias of a global user gets global access."""
    # Alice's Telegram alias should have global access
    assert is_authorized_sender("@telegram_111:example.com", mock_config_with_aliases, "!room1:example.com")
    assert is_authorized_sender("@telegram_111:example.com", mock_config_with_aliases, "!any_room:example.com")

    # Alice's Signal alias should also work
    assert is_authorized_sender("@signal_111:example.com", mock_config_with_aliases, "!room1:example.com")


def test_bridge_alias_room_permission(mock_config_with_aliases: Config) -> None:
    """Test that a bridge alias inherits room-specific permissions."""
    # Bob's Telegram alias should have access to room1
    assert is_authorized_sender("@telegram_222:example.com", mock_config_with_aliases, "!room1:example.com")

    # But not to other rooms
    assert not is_authorized_sender("@telegram_222:example.com", mock_config_with_aliases, "!room2:example.com")


def test_unknown_bridge_alias_rejected(mock_config_with_aliases: Config) -> None:
    """Test that an unknown alias is not authorized."""
    assert not is_authorized_sender("@telegram_999:example.com", mock_config_with_aliases, "!room1:example.com")


def test_canonical_user_still_works_with_aliases(mock_config_with_aliases: Config) -> None:
    """Test that the canonical user ID still works when aliases are configured."""
    assert is_authorized_sender("@alice:example.com", mock_config_with_aliases, "!room1:example.com")
    assert is_authorized_sender("@bob:example.com", mock_config_with_aliases, "!room1:example.com")


def test_agent_reply_permissions_with_aliases() -> None:
    """Per-agent reply allowlists should use canonical IDs after alias resolution."""
    config = _config(
        agents={
            "assistant": {
                "display_name": "Assistant",
                "role": "Test assistant",
                "rooms": ["test_room"],
            },
            "analyst": {
                "display_name": "Analyst",
                "role": "Test analyst",
                "rooms": ["test_room"],
            },
        },
        authorization={
            "default_room_access": True,
            "aliases": {
                "@alice:example.com": ["@telegram_111:example.com"],
            },
            "agent_reply_permissions": {
                "assistant": ["@alice:example.com"],
            },
        },
    )

    assert is_sender_allowed_for_agent_reply("@alice:example.com", "assistant", config)
    assert is_sender_allowed_for_agent_reply("@telegram_111:example.com", "assistant", config)
    assert not is_sender_allowed_for_agent_reply("@bob:example.com", "assistant", config)
    assert is_sender_allowed_for_agent_reply("@bob:example.com", "analyst", config)


def test_agent_reply_permissions_do_not_bypass_bot_accounts() -> None:
    """Bridge bot accounts should still respect per-agent reply allowlists."""
    config = _config(
        agents={
            "assistant": {
                "display_name": "Assistant",
                "role": "Test assistant",
                "rooms": ["test_room"],
            },
        },
        bot_accounts=["@bridgebot:example.com"],
        authorization={
            "default_room_access": True,
            "agent_reply_permissions": {
                "assistant": ["@alice:example.com"],
            },
        },
    )

    assert not is_sender_allowed_for_agent_reply("@bridgebot:example.com", "assistant", config)


def test_agent_reply_permissions_do_not_bypass_cross_domain_agent_like_ids() -> None:
    """Only configured internal IDs may bypass reply permissions."""
    config = _config(
        agents={
            "assistant": {
                "display_name": "Assistant",
                "role": "Test assistant",
                "rooms": ["test_room"],
            },
        },
        authorization={
            "default_room_access": True,
            "agent_reply_permissions": {
                "assistant": ["@alice:example.com"],
            },
        },
    )

    assert not is_sender_allowed_for_agent_reply("@mindroom_assistant:evil.com", "assistant", config)


def test_agent_reply_permissions_wildcard_entity_applies_to_all() -> None:
    """A '*' entity key should act as default allowlist for all entities."""
    config = _config(
        agents={
            "assistant": {
                "display_name": "Assistant",
                "role": "Test assistant",
                "rooms": ["test_room"],
            },
            "analyst": {
                "display_name": "Analyst",
                "role": "Test analyst",
                "rooms": ["test_room"],
            },
        },
        authorization={
            "default_room_access": True,
            "agent_reply_permissions": {
                "*": ["@alice:example.com"],
            },
        },
    )

    assert is_sender_allowed_for_agent_reply("@alice:example.com", "assistant", config)
    assert is_sender_allowed_for_agent_reply("@alice:example.com", "analyst", config)
    assert not is_sender_allowed_for_agent_reply("@bob:example.com", "assistant", config)
    assert not is_sender_allowed_for_agent_reply("@bob:example.com", "analyst", config)


def test_agent_reply_permissions_entity_override_beats_wildcard() -> None:
    """An explicit entity entry should override the '*' entity default."""
    config = _config(
        agents={
            "assistant": {
                "display_name": "Assistant",
                "role": "Test assistant",
                "rooms": ["test_room"],
            },
            "analyst": {
                "display_name": "Analyst",
                "role": "Test analyst",
                "rooms": ["test_room"],
            },
        },
        authorization={
            "default_room_access": True,
            "agent_reply_permissions": {
                "*": ["@alice:example.com"],
                "analyst": ["@bob:example.com"],
            },
        },
    )

    assert is_sender_allowed_for_agent_reply("@alice:example.com", "assistant", config)
    assert not is_sender_allowed_for_agent_reply("@alice:example.com", "analyst", config)
    assert is_sender_allowed_for_agent_reply("@bob:example.com", "analyst", config)


def test_agent_reply_permissions_wildcard_user_allows_everyone() -> None:
    """A '*' user entry should disable sender restriction for that entity."""
    config = _config(
        agents={
            "assistant": {
                "display_name": "Assistant",
                "role": "Test assistant",
                "rooms": ["test_room"],
            },
            "analyst": {
                "display_name": "Analyst",
                "role": "Test analyst",
                "rooms": ["test_room"],
            },
        },
        authorization={
            "default_room_access": True,
            "agent_reply_permissions": {
                "*": ["@alice:example.com"],
                "analyst": ["*"],
            },
        },
    )

    assert not is_sender_allowed_for_agent_reply("@bob:example.com", "assistant", config)
    assert is_sender_allowed_for_agent_reply("@bob:example.com", "analyst", config)


def test_agent_reply_permissions_support_domain_pattern() -> None:
    """Allowlist entries should support glob patterns like '*:example.com'."""
    config = _config(
        agents={
            "assistant": {
                "display_name": "Assistant",
                "role": "Test assistant",
                "rooms": ["test_room"],
            },
        },
        authorization={
            "default_room_access": True,
            "agent_reply_permissions": {
                "assistant": ["*:example.com"],
            },
        },
    )

    assert is_sender_allowed_for_agent_reply("@alice:example.com", "assistant", config)
    assert not is_sender_allowed_for_agent_reply("@alice:other.com", "assistant", config)


def test_agent_reply_permissions_domain_pattern_after_alias_resolution() -> None:
    """Domain patterns should match after aliases resolve to canonical IDs."""
    config = _config(
        agents={
            "assistant": {
                "display_name": "Assistant",
                "role": "Test assistant",
                "rooms": ["test_room"],
            },
        },
        authorization={
            "default_room_access": True,
            "aliases": {
                "@alice:example.com": ["@telegram_111:example.com"],
            },
            "agent_reply_permissions": {
                "assistant": ["*:example.com"],
            },
        },
    )

    assert is_sender_allowed_for_agent_reply("@telegram_111:example.com", "assistant", config)


def test_agent_reply_permissions_reject_unknown_entity() -> None:
    """Unknown keys in agent_reply_permissions should fail config validation."""
    with pytest.raises(ValueError, match=r"authorization\.agent_reply_permissions contains unknown entities"):
        Config(
            agents={
                "assistant": {
                    "display_name": "Assistant",
                    "role": "Test assistant",
                    "rooms": ["test_room"],
                },
            },
            authorization={
                "default_room_access": True,
                "agent_reply_permissions": {
                    "missing_agent": ["@alice:example.com"],
                },
            },
        )


def test_effective_sender_uses_voice_original_sender_for_router_messages() -> None:
    """Router transcriptions should use embedded original sender for permission checks."""
    config = _config(
        agents={
            "assistant": {
                "display_name": "Assistant",
                "role": "Test assistant",
                "rooms": ["test_room"],
            },
        },
        authorization={"default_room_access": True},
    )

    event_source = {
        "content": {
            "body": "🎤 help me",
            ORIGINAL_SENDER_KEY: "@alice:example.com",
        },
    }

    assert get_effective_sender_id_for_reply_permissions(
        config.get_ids(_runtime_paths_for(config))[ROUTER_AGENT_NAME].full_id,
        event_source,
        config,
    ) == ("@alice:example.com")


def test_effective_sender_ignores_voice_original_sender_for_non_internal_messages() -> None:
    """Only trusted internal MindRoom senders may override requester identity."""
    config = _config(
        agents={
            "assistant": {
                "display_name": "Assistant",
                "role": "Test assistant",
                "rooms": ["test_room"],
            },
        },
        authorization={"default_room_access": True},
    )

    event_source = {
        "content": {
            "body": "spoof attempt",
            ORIGINAL_SENDER_KEY: "@alice:example.com",
        },
    }

    assert get_effective_sender_id_for_reply_permissions("@bob:example.com", event_source, config) == "@bob:example.com"


def test_effective_sender_uses_original_sender_for_internal_agent_messages() -> None:
    """Internal agent relays should respect embedded original sender metadata."""
    config = _config(
        agents={
            "assistant": {
                "display_name": "Assistant",
                "role": "Test assistant",
                "rooms": ["test_room"],
            },
            "helper": {
                "display_name": "Helper",
                "role": "Test helper",
                "rooms": ["test_room"],
            },
        },
        authorization={"default_room_access": True},
    )

    event_source = {
        "content": {
            "body": "automated task",
            ORIGINAL_SENDER_KEY: "@alice:example.com",
        },
    }

    assert get_effective_sender_id_for_reply_permissions(
        config.get_ids(_runtime_paths_for(config))["assistant"].full_id,
        event_source,
        config,
    ) == ("@alice:example.com")


def test_effective_sender_does_not_trust_cross_domain_router_like_ids() -> None:
    """Router sender override must require exact configured router ID."""
    config = _config(
        agents={
            "assistant": {
                "display_name": "Assistant",
                "role": "Test assistant",
                "rooms": ["test_room"],
            },
        },
        authorization={"default_room_access": True},
    )

    spoofed_router = "@mindroom_router:evil.com"
    event_source = {
        "content": {
            "body": "spoof attempt",
            ORIGINAL_SENDER_KEY: "@alice:example.com",
        },
    }

    assert get_effective_sender_id_for_reply_permissions(spoofed_router, event_source, config) == spoofed_router


def test_resolve_alias_method() -> None:
    """Test the resolve_alias helper directly."""
    auth = AuthorizationConfig(
        aliases={
            "@alice:example.com": ["@telegram_111:example.com"],
        },
    )
    assert auth.resolve_alias("@telegram_111:example.com") == "@alice:example.com"
    assert auth.resolve_alias("@alice:example.com") == "@alice:example.com"
    assert auth.resolve_alias("@unknown:example.com") == "@unknown:example.com"


def test_duplicate_bridge_alias_rejected() -> None:
    """Test that aliases cannot be mapped to multiple canonical users."""
    with pytest.raises(ValueError, match="Duplicate bridge aliases are not allowed"):
        AuthorizationConfig(
            aliases={
                "@alice:example.com": ["@telegram_111:example.com"],
                "@bob:example.com": ["@telegram_111:example.com"],
            },
        )


def _config_with_runtime_paths(tmp_path: Path, **config_data: object) -> Config:
    config_path = tmp_path / "config.yaml"
    storage_path = tmp_path / "mindroom_data"
    runtime_paths = resolve_runtime_paths(config_path=config_path, storage_path=storage_path, process_env={})
    config = _config(**config_data)
    config._runtime_paths = runtime_paths
    return config
