"""Matrix presence and status message utilities."""

from __future__ import annotations

from typing import TYPE_CHECKING

import nio

from mindroom.constants import ROUTER_AGENT_NAME
from mindroom.logging_config import get_logger

if TYPE_CHECKING:
    from mindroom.config.main import Config

logger = get_logger(__name__)

_VOICE_CALLS_STATUS = "📞 Voice calls"


async def set_presence_status(
    client: nio.AsyncClient,
    status_msg: str,
    presence: str = "online",
) -> None:
    """Set presence status for a Matrix user.

    Args:
        client: The Matrix client
        status_msg: The status message to display
        presence: The presence state (online, offline, unavailable)

    """
    response = await client.set_presence(presence, status_msg)

    if isinstance(response, nio.PresenceSetResponse):
        logger.info(
            "presence_status_set",
            user_id=client.user_id,
            presence=presence,
            status=status_msg,
        )
    else:
        logger.warning(
            "presence_status_set_failed",
            user_id=client.user_id,
            presence=presence,
            status=status_msg,
            error=str(response),
        )


def build_agent_status_message(
    agent_name: str,
    config: Config,
    *,
    voice_calls_available: bool = False,
) -> str:
    """Build status message with model and role information for an agent.

    Args:
        agent_name: Name of the agent
        config: Application configuration
        voice_calls_available: Whether this bot has a usable MatrixRTC call manager

    Returns:
        Status message string, limited to 250 characters

    """
    status_parts = []

    entity_view = config.resolve_entity(agent_name)
    model_name = entity_view.model_name

    # Format model info
    if model_name in config.models:
        model_config = config.models[model_name]
        model_info = f"{model_config.provider}/{model_config.id}"
    else:
        model_info = model_name

    status_parts.append(f"🤖 Model: {model_info}")

    # Add role/purpose for teams and agents
    if agent_name == ROUTER_AGENT_NAME:
        status_parts.append("📍 Routes messages to appropriate agents or teams")
    elif agent_name in config.teams:
        team_config = config.teams[agent_name]
        if team_config.role:
            status_parts.append(f"👥 {team_config.role[:100]}")  # Limit length
        status_parts.append(f"🤝 Team: {', '.join(team_config.agents[:5])}")  # Show first 5 agents
    elif agent_name in config.agents:
        agent_config = config.agents[agent_name]
        if agent_config.role:
            status_parts.append(f"💼 {agent_config.role[:100]}")  # Limit length
        # Add tool count
        effective_tools = entity_view.available_tools
        if effective_tools:
            status_parts.append(f"🔧 {len(effective_tools)} tools available")
        if voice_calls_available:
            status_parts.append(_VOICE_CALLS_STATUS)

    # Join all parts with separators
    return " | ".join(status_parts)


async def is_user_online(
    client: nio.AsyncClient,
    user_id: str,
    room_id: str | None = None,
) -> bool:
    """Check if a Matrix user is currently online.

    Args:
        client: The Matrix client to use for the presence check
        user_id: The Matrix user ID string (e.g., "@user:example.com")
        room_id: Optional room ID whose synced membership cache should be checked first

    Returns:
        True if the user is online or unavailable (active but busy),
        False if offline or presence check fails

    """
    rooms = client.rooms
    cached_rooms = rooms if isinstance(rooms, dict) else {}
    candidate_rooms = [cached_rooms.get(room_id)] if room_id is not None else cached_rooms.values()
    for room in candidate_rooms:
        if room is None or user_id not in room.users:
            continue
        cached_user = room.users[user_id]
        if cached_user.presence not in ("online", "unavailable"):
            continue
        is_online = True
        logger.debug(
            "User presence check from room cache",
            user_id=user_id,
            room_id=room.room_id,
            presence=cached_user.presence,
            is_online=is_online,
            last_active_ago=cached_user.last_active_ago,
        )
        return is_online

    try:
        response = await client.get_presence(user_id)

        # Check if we got an error response
        if isinstance(response, nio.PresenceGetError):
            logger.warning(
                "Presence API error",
                user_id=user_id,
                error=response.message,
            )
            return False

        # Presence states: "online", "unavailable" (busy/idle), "offline"
        # We consider both "online" and "unavailable" as "online" for streaming purposes
        # since "unavailable" usually means the user is idle but still has the client open
        is_online = response.presence in ("online", "unavailable")

        logger.debug(
            "User presence check",
            user_id=user_id,
            presence=response.presence,
            is_online=is_online,
            last_active_ago=response.last_active_ago,
        )

        return is_online  # noqa: TRY300

    except Exception:
        logger.exception(
            "Error checking user presence",
            user_id=user_id,
        )
        # Default to non-streaming on error (safer)
        return False


async def should_use_streaming(
    client: nio.AsyncClient,
    room_id: str,
    requester_user_id: str | None = None,
    *,
    enable_streaming: bool,
) -> bool:
    """Determine if streaming should be used based on user presence.

    This checks if the human user who sent the message is online.
    If they are online, we use streaming (message editing) for real-time updates.
    If they are offline, we send the complete message at once to save API calls.

    Args:
        client: The Matrix client
        room_id: The room where the interaction is happening
        requester_user_id: The user who sent the message (optional)
        enable_streaming: Whether streaming is enabled in config

    Returns:
        True if streaming should be used, False otherwise

    """
    # Check if streaming is globally disabled
    if not enable_streaming:
        return False

    # If no requester specified, we can't check presence, default to streaming
    if not requester_user_id:
        logger.debug("No requester specified, defaulting to streaming")
        return True

    # Check if the requester is online
    is_online = await is_user_online(client, requester_user_id, room_id=room_id)

    logger.info(
        "Streaming decision",
        room_id=room_id,
        requester=requester_user_id,
        is_online=is_online,
        use_streaming=is_online,
    )

    return is_online
