from __future__ import annotations

from types import SimpleNamespace
from unittest.mock import AsyncMock

from nio.api import RoomVisibility

from adapter.matrix.bot import build_runtime
from adapter.matrix.handlers.auth import handle_invite
from adapter.matrix.store import get_room_meta, get_user_meta, set_room_meta, set_user_meta
from sdk.mock import MockPlatformClient


def _make_client():
    space_resp = SimpleNamespace(room_id="!space:example.org")
    chat_resp = SimpleNamespace(room_id="!chat1:example.org")
    return SimpleNamespace(
        join=AsyncMock(),
        room_create=AsyncMock(side_effect=[space_resp, chat_resp]),
        room_put_state=AsyncMock(),
        room_invite=AsyncMock(),
        room_send=AsyncMock(),
    )


async def test_mat01_invite_creates_space_and_chat1():
    runtime = build_runtime(platform=MockPlatformClient())
    await set_user_meta(runtime.store, "@alice:example.org", {"next_chat_index": 4})
    client = _make_client()
    room = SimpleNamespace(room_id="!dm:example.org", display_name="Alice")
    event = SimpleNamespace(sender="@alice:example.org", membership="invite")

    await handle_invite(
        client,
        room,
        event,
        runtime.platform,
        runtime.store,
        runtime.auth_mgr,
        runtime.chat_mgr,
    )

    first_call = client.room_create.call_args_list[0]
    assert first_call.kwargs.get("space") is True
    assert first_call.kwargs.get("visibility") is RoomVisibility.private
    assert first_call.kwargs.get("invite") == ["@alice:example.org"]
    second_call = client.room_create.call_args_list[1]
    assert second_call.kwargs.get("visibility") is RoomVisibility.private
    assert second_call.kwargs.get("invite") == ["@alice:example.org"]
    assert client.room_create.await_count == 2
    client.room_invite.assert_not_awaited()

    client.room_put_state.assert_awaited_once()
    kwargs = client.room_put_state.call_args.kwargs
    assert kwargs.get("event_type") == "m.space.child"
    assert kwargs.get("state_key") == "!chat1:example.org"
    assert kwargs.get("room_id") == "!space:example.org"

    user_meta = await get_user_meta(runtime.store, "@alice:example.org")
    assert user_meta is not None
    assert user_meta["space_id"] == "!space:example.org"

    room_meta = await get_room_meta(runtime.store, "!chat1:example.org")
    assert room_meta is not None
    assert room_meta["chat_id"] == "C4"
    assert room_meta["space_id"] == "!space:example.org"
    assert room_meta["platform_chat_id"] == "1"
    assert user_meta["next_chat_index"] == 5

    chats = await runtime.chat_mgr.list_active("@alice:example.org")
    assert [chat.chat_id for chat in chats] == ["C4"]
    assert [chat.surface_ref for chat in chats] == ["!chat1:example.org"]


async def test_mat02_invite_idempotent():
    runtime = build_runtime(platform=MockPlatformClient())
    client = _make_client()
    room = SimpleNamespace(room_id="!dm:example.org", display_name="Alice")
    event = SimpleNamespace(sender="@alice:example.org", membership="invite")

    await handle_invite(
        client,
        room,
        event,
        runtime.platform,
        runtime.store,
        runtime.auth_mgr,
        runtime.chat_mgr,
    )
    await handle_invite(
        client,
        room,
        event,
        runtime.platform,
        runtime.store,
        runtime.auth_mgr,
        runtime.chat_mgr,
    )

    assert client.room_create.await_count == 2


async def test_existing_user_invite_reinvites_space_and_active_chats():
    runtime = build_runtime(platform=MockPlatformClient())
    await set_user_meta(
        runtime.store,
        "@alice:example.org",
        {"space_id": "!space:example.org", "next_chat_index": 2},
    )
    await set_room_meta(
        runtime.store,
        "!chat1:example.org",
        {
            "room_type": "chat",
            "chat_id": "C1",
            "display_name": "Чат 1",
            "matrix_user_id": "@alice:example.org",
            "space_id": "!space:example.org",
            "platform_chat_id": "1",
            "agent_id": "agent-1",
        },
    )
    await runtime.chat_mgr.get_or_create(
        user_id="@alice:example.org",
        chat_id="C1",
        platform="matrix",
        surface_ref="!chat1:example.org",
        name="Чат 1",
    )
    client = _make_client()
    room = SimpleNamespace(room_id="!dm:example.org", display_name="Alice")
    event = SimpleNamespace(sender="@alice:example.org", membership="invite")

    await handle_invite(
        client,
        room,
        event,
        runtime.platform,
        runtime.store,
        runtime.auth_mgr,
        runtime.chat_mgr,
    )

    client.room_create.assert_not_awaited()
    client.room_invite.assert_any_await("!space:example.org", "@alice:example.org")
    client.room_invite.assert_any_await("!chat1:example.org", "@alice:example.org")
    client.room_send.assert_awaited()


async def test_mat03_no_hardcoded_c1():
    runtime = build_runtime(platform=MockPlatformClient())
    await set_user_meta(runtime.store, "@alice:example.org", {"next_chat_index": 7})
    client = _make_client()
    room = SimpleNamespace(room_id="!dm:example.org", display_name="Alice")
    event = SimpleNamespace(sender="@alice:example.org", membership="invite")

    await handle_invite(
        client,
        room,
        event,
        runtime.platform,
        runtime.store,
        runtime.auth_mgr,
        runtime.chat_mgr,
    )

    room_meta = await get_room_meta(runtime.store, "!chat1:example.org")
    assert room_meta is not None
    assert room_meta["chat_id"] == "C7"
    assert room_meta["platform_chat_id"] == "1"

    user_meta = await get_user_meta(runtime.store, "@alice:example.org")
    assert user_meta is not None
    assert user_meta["next_chat_index"] == 8
