from __future__ import annotations

from typing import Any

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

from adapter.matrix.agent_registry import AgentRegistry
from adapter.matrix.store import (
    get_user_meta,
    next_platform_chat_id,
    set_room_meta,
    set_user_meta,
)

logger = structlog.get_logger(__name__)


def _default_room_name(chat_id: str) -> str:
    suffix = chat_id[1:] if chat_id.startswith("C") else chat_id
    return f"Чат {suffix}"


def default_agent_notice() -> str:
    return (
        "Внимание: ваш Matrix ID не найден в конфиге агентов. "
        "Пока используется агент по умолчанию. После добавления вас в конфиг "
        "бот переключит существующие комнаты на назначенного агента."
    )


async def _invite_if_possible(client: Any, room_id: str, matrix_user_id: str) -> bool:
    room_invite = getattr(client, "room_invite", None)
    if not callable(room_invite):
        return False
    try:
        await room_invite(room_id, matrix_user_id)
        return True
    except Exception as exc:
        logger.warning(
            "matrix_workspace_reinvite_failed",
            room_id=room_id,
            user=matrix_user_id,
            error=str(exc),
        )
        return False


async def provision_workspace_chat(
    client: Any,
    matrix_user_id: str,
    display_name: str,
    platform,
    store,
    auth_mgr,
    chat_mgr,
    room_name_override: str | None = None,
    registry: AgentRegistry | None = None,
) -> dict:
    user = await platform.get_or_create_user(
        external_id=matrix_user_id,
        platform="matrix",
        display_name=display_name,
    )
    await auth_mgr.confirm(matrix_user_id)

    homeserver = matrix_user_id.split(":")[-1]
    user_meta = await get_user_meta(store, matrix_user_id) or {}
    space_id = user_meta.get("space_id")

    if not space_id:
        space_resp = await client.room_create(
            name=f"Lambda — {display_name}",
            space=True,
            visibility=RoomVisibility.private,
            invite=[matrix_user_id],
        )
        if isinstance(space_resp, RoomCreateError):
            logger.error(
                "space creation failed",
                user=matrix_user_id,
                error=getattr(space_resp, "status_code", None),
            )
            raise RuntimeError("Не удалось создать Space.")
        space_id = space_resp.room_id
        user_meta["space_id"] = space_id
        await set_user_meta(store, matrix_user_id, user_meta)

    next_chat_index = int(user_meta.get("next_chat_index", 1))
    chat_id = f"C{next_chat_index}"
    platform_chat_id = await next_platform_chat_id(store)
    room_name = room_name_override or _default_room_name(chat_id)

    agent_id = None
    agent_assignment = "none"
    if registry is not None:
        assignment = registry.resolve_agent_for_user(matrix_user_id)
        agent_id = assignment.agent_id
        agent_assignment = assignment.source

    chat_resp = await client.room_create(
        name=room_name,
        visibility=RoomVisibility.private,
        is_direct=False,
        invite=[matrix_user_id],
    )
    if isinstance(chat_resp, RoomCreateError):
        logger.error(
            "chat room creation failed",
            user=matrix_user_id,
            error=getattr(chat_resp, "status_code", None),
        )
        raise RuntimeError("Не удалось создать рабочий чат.")
    chat_room_id = chat_resp.room_id

    await client.room_put_state(
        room_id=space_id,
        event_type="m.space.child",
        content={"via": [homeserver]},
        state_key=chat_room_id,
    )

    user_meta["space_id"] = space_id
    user_meta["next_chat_index"] = next_chat_index + 1
    await set_user_meta(store, matrix_user_id, user_meta)

    await set_room_meta(
        store,
        chat_room_id,
        {
            "room_type": "chat",
            "chat_id": chat_id,
            "display_name": room_name,
            "matrix_user_id": matrix_user_id,
            "space_id": space_id,
            "platform_chat_id": platform_chat_id,
            "agent_id": agent_id,
            "agent_assignment": agent_assignment,
        },
    )
    await chat_mgr.get_or_create(
        user_id=matrix_user_id,
        chat_id=chat_id,
        platform="matrix",
        surface_ref=chat_room_id,
        name=room_name,
    )

    return {
        "user": user,
        "space_id": space_id,
        "chat_room_id": chat_room_id,
        "chat_id": chat_id,
        "room_name": room_name,
        "agent_assignment": agent_assignment,
        "agent_id": agent_id,
    }


async def restore_workspace_access(
    client: Any,
    matrix_user_id: str,
    display_name: str,
    platform,
    store,
    auth_mgr,
    chat_mgr,
    registry: AgentRegistry | None = None,
) -> dict:
    user_meta = await get_user_meta(store, matrix_user_id) or {}
    space_id = user_meta.get("space_id")
    if not space_id:
        created = await provision_workspace_chat(
            client,
            matrix_user_id,
            display_name,
            platform,
            store,
            auth_mgr,
            chat_mgr,
            room_name_override="Чат 1",
            registry=registry,
        )
        return {**created, "reinvited_rooms": [], "created_new_chat": True}

    await auth_mgr.confirm(matrix_user_id)
    await _invite_if_possible(client, space_id, matrix_user_id)

    chats = await chat_mgr.list_active(matrix_user_id)
    if not chats:
        created = await provision_workspace_chat(
            client,
            matrix_user_id,
            display_name,
            platform,
            store,
            auth_mgr,
            chat_mgr,
            registry=registry,
        )
        return {**created, "reinvited_rooms": [], "created_new_chat": True}

    reinvited_rooms = []
    for chat in chats:
        if chat.surface_ref:
            if await _invite_if_possible(client, chat.surface_ref, matrix_user_id):
                reinvited_rooms.append(chat.surface_ref)

    return {
        "space_id": space_id,
        "reinvited_rooms": reinvited_rooms,
        "created_new_chat": False,
    }


async def handle_invite(
    client: Any,
    room: Any,
    event: Any,
    platform,
    store,
    auth_mgr,
    chat_mgr,
    registry: AgentRegistry | None = None,
) -> None:
    matrix_user_id = getattr(event, "sender", "")
    display_name = getattr(room, "display_name", None) or matrix_user_id

    await client.join(room.room_id)

    existing = await get_user_meta(store, matrix_user_id)
    if existing and existing.get("space_id"):
        restored = await restore_workspace_access(
            client,
            matrix_user_id,
            display_name,
            platform,
            store,
            auth_mgr,
            chat_mgr,
            registry=registry,
        )
        body = "Я отправил повторные приглашения в пространство Lambda и рабочие чаты."
        if restored.get("created_new_chat"):
            body = (
                f"Создал новый рабочий чат {restored['room_name']} "
                f"({restored['chat_id']}) и отправил приглашение."
            )
            if restored.get("agent_assignment") == "default":
                body = f"{body}\n\n{default_agent_notice()}"
        await client.room_send(
            room.room_id,
            "m.room.message",
            {"msgtype": "m.text", "body": body},
        )
        return

    try:
        created = await provision_workspace_chat(
            client,
            matrix_user_id,
            display_name,
            platform,
            store,
            auth_mgr,
            chat_mgr,
            room_name_override="Чат 1",
            registry=registry,
        )
    except RuntimeError as exc:
        logger.error("invite_workspace_provision_failed", user=matrix_user_id, error=str(exc))
        return

    welcome = (
        f"Привет, {created['user'].display_name or matrix_user_id}! Пиши — я здесь.\n\n"
        "Команды: !new · !chats · !rename · !archive · !clear · !help"
    )
    if created.get("agent_assignment") == "default":
        welcome = f"{welcome}\n\n{default_agent_notice()}"
    await client.room_send(
        created["chat_room_id"],
        "m.room.message",
        {"msgtype": "m.text", "body": welcome},
    )
