from __future__ import annotations

from types import SimpleNamespace
from unittest.mock import AsyncMock

from nio.api import RoomVisibility
from nio.responses import RoomCreateError

from adapter.matrix.handlers.chat import (
    make_handle_archive,
    make_handle_new_chat,
    make_handle_rename,
)
from adapter.matrix.store import get_room_meta, set_user_meta
from core.auth import AuthManager
from core.chat import ChatManager
from core.protocol import IncomingCommand, OutgoingMessage
from core.settings import SettingsManager
from core.store import InMemoryStore
from sdk.mock import MockPlatformClient


async def _setup():
    platform = MockPlatformClient()
    store = InMemoryStore()
    chat_mgr = ChatManager(platform, store)
    auth_mgr = AuthManager(platform, store)
    settings_mgr = SettingsManager(platform, store)
    await auth_mgr.confirm("@alice:example.org")
    return platform, store, chat_mgr, auth_mgr, settings_mgr


async def test_mat04_new_chat_calls_room_put_state_with_space_id():
    platform, store, chat_mgr, auth_mgr, settings_mgr = await _setup()
    await set_user_meta(
        store, "@alice:example.org", {"space_id": "!space:ex", "next_chat_index": 2}
    )

    client = SimpleNamespace(
        room_create=AsyncMock(return_value=SimpleNamespace(room_id="!newroom:ex")),
        room_put_state=AsyncMock(),
        room_invite=AsyncMock(),
    )
    handler = make_handle_new_chat(client, store)
    event = IncomingCommand(
        user_id="@alice:example.org",
        platform="matrix",
        chat_id="C1",
        command="new",
        args=["Test"],
    )
    result = await handler(event, auth_mgr, platform, chat_mgr, settings_mgr)

    client.room_create.assert_awaited_once_with(
        name="Test",
        visibility=RoomVisibility.private,
        is_direct=False,
        invite=["@alice:example.org"],
    )
    client.room_put_state.assert_awaited_once()
    client.room_invite.assert_not_awaited()
    kwargs = client.room_put_state.call_args.kwargs
    assert kwargs.get("room_id") == "!space:ex"
    assert kwargs.get("event_type") == "m.space.child"
    assert kwargs.get("state_key") == "!newroom:ex"
    room_meta = await get_room_meta(store, "!newroom:ex")
    assert room_meta is not None
    assert room_meta["platform_chat_id"] == "1"
    assert any(isinstance(item, OutgoingMessage) and "Test" in item.text for item in result)


async def test_mat05_new_chat_without_space_id_returns_error():
    platform, store, chat_mgr, auth_mgr, settings_mgr = await _setup()
    await set_user_meta(store, "@alice:example.org", {"next_chat_index": 1})

    client = SimpleNamespace(
        room_create=AsyncMock(),
        room_put_state=AsyncMock(),
        room_invite=AsyncMock(),
    )
    handler = make_handle_new_chat(client, store)
    event = IncomingCommand(
        user_id="@alice:example.org",
        platform="matrix",
        chat_id="C1",
        command="new",
    )
    result = await handler(event, auth_mgr, platform, chat_mgr, settings_mgr)

    assert len(result) == 1
    assert isinstance(result[0], OutgoingMessage)
    assert "Space" in result[0].text or "ошибка" in result[0].text.lower()
    client.room_create.assert_not_awaited()


async def test_mat10_archive_calls_chat_mgr_archive():
    platform, store, chat_mgr, auth_mgr, settings_mgr = await _setup()

    client = SimpleNamespace(room_leave=AsyncMock())
    handler = make_handle_archive(client, store)
    event = IncomingCommand(
        user_id="@alice:example.org",
        platform="matrix",
        chat_id="C1",
        command="archive",
    )
    await chat_mgr.get_or_create(
        user_id="@alice:example.org",
        chat_id="C1",
        platform="matrix",
        surface_ref="!room:ex",
        name="Test",
    )

    result = await handler(event, auth_mgr, platform, chat_mgr, settings_mgr)

    assert len(result) == 1
    assert "архивирован" in result[0].text
    client.room_leave.assert_awaited_once_with("!room:ex")
    chats = await chat_mgr.list_active("@alice:example.org")
    assert chats == []


async def test_mat11_rename_updates_matrix_room_name_via_state_event():
    platform, store, chat_mgr, auth_mgr, settings_mgr = await _setup()
    await chat_mgr.get_or_create(
        user_id="@alice:example.org",
        chat_id="C1",
        platform="matrix",
        surface_ref="!room:ex",
        name="Old",
    )

    client = SimpleNamespace(room_put_state=AsyncMock())
    handler = make_handle_rename(client, store)
    event = IncomingCommand(
        user_id="@alice:example.org",
        platform="matrix",
        chat_id="C1",
        command="rename",
        args=["New", "Name"],
    )

    result = await handler(event, auth_mgr, platform, chat_mgr, settings_mgr)

    client.room_put_state.assert_awaited_once_with(
        room_id="!room:ex",
        event_type="m.room.name",
        content={"name": "New Name"},
        state_key="",
    )
    assert len(result) == 1
    assert "Переименован" in result[0].text


async def test_mat11b_rename_from_unregistered_room_returns_error_message():
    platform, store, chat_mgr, auth_mgr, settings_mgr = await _setup()

    client = SimpleNamespace(room_put_state=AsyncMock())
    handler = make_handle_rename(client, store)
    event = IncomingCommand(
        user_id="@alice:example.org",
        platform="matrix",
        chat_id="unregistered:!old:example.org",
        command="rename",
        args=["New"],
    )

    result = await handler(event, auth_mgr, platform, chat_mgr, settings_mgr)

    client.room_put_state.assert_not_awaited()
    assert len(result) == 1
    assert "не найден" in result[0].text.lower() or "примите приглашение" in result[0].text.lower()


async def test_mat12_room_create_error_returns_user_message():
    platform, store, chat_mgr, auth_mgr, settings_mgr = await _setup()
    await set_user_meta(
        store, "@alice:example.org", {"space_id": "!space:ex", "next_chat_index": 2}
    )

    client = SimpleNamespace(
        room_create=AsyncMock(
            return_value=RoomCreateError(message="rate limited", status_code="429")
        ),
        room_put_state=AsyncMock(),
        room_invite=AsyncMock(),
    )
    handler = make_handle_new_chat(client, store)
    event = IncomingCommand(
        user_id="@alice:example.org",
        platform="matrix",
        chat_id="C1",
        command="new",
        args=["Fail"],
    )
    result = await handler(event, auth_mgr, platform, chat_mgr, settings_mgr)

    assert len(result) == 1
    assert isinstance(result[0], OutgoingMessage)
    assert "Не удалось" in result[0].text or "не удалось" in result[0].text
    client.room_put_state.assert_not_awaited()
