from __future__ import annotations

from typing import Optional, TYPE_CHECKING

from clarabot_nano_common.common.protocol.errors import (
    NANO_REQUEST_ERROR_DENIED,
    NANO_REQUEST_ERROR_TEMPORARY_INTERNAL_FAILURE,
)
from clarabot_nano_common.common.protocol.request import NanoRequestContext

from clarabot_nano.common.gateway.error import GatewayRequestProcessingError
from clarabot_nano.gateway.state.config import RoomGroupConfigNotReady

if TYPE_CHECKING:
    from clarabot_nano.gateway.state.gateway import GatewayState, AdhocTokenData


async def get_room_access_info(
    gws: GatewayState,
    account_id: int,
    room_id: int,
    ip_address: Optional[str],
    adhoc_token_data: Optional[AdhocTokenData] = None,
) -> dict:
    """
    Evaluate if we're serving the room in question.
    Evaluate if the account in question has access to the specified room.
    If anything is not found correct or explicit processing error is raised.
    """
    if (room_apis := gws.extracted_config.rooms.get(room_id)) is None or (
        room_config_state := gws.room_config_store.get(room_id)
    ) is None:
        raise GatewayRequestProcessingError(
            NANO_REQUEST_ERROR_DENIED,
            "Room is not served or has no API set up",
            account_id=account_id,
            ip_address=ip_address,
        )
    try:
        access_flags = await room_config_state.get_flags(account_id, adhoc_access=bool(adhoc_token_data))
    except RoomGroupConfigNotReady:
        if room_config_state.state_is_final():
            raise GatewayRequestProcessingError(
                NANO_REQUEST_ERROR_DENIED,
                "Room config is revoked or failed",
                account_id=account_id,
                ip_address=ip_address,
            )
        raise GatewayRequestProcessingError(
            NANO_REQUEST_ERROR_TEMPORARY_INTERNAL_FAILURE,
            "Room config is not ready",
            account_id=account_id,
            ip_address=ip_address,
        )
    # NOTE: Access flags are resolved for account_id: room allows access if access_flags is not None.
    # NOTE: The "deny_anonymous" policy must be checked here (as well) to enforce it against the requesting
    # account's level of access. Registered users may submit requests using their registered identity while
    # only having the anonymous access level. In this case they have the room's pinned key from an
    # anonymous-share URL presumably.
    # NOTE: The deny-anonymous local-config overrides adhoc access as well even if it is valid.
    if not access_flags or (access_flags[0] == 0 and gws.extracted_config.deny_anonymous):
        raise GatewayRequestProcessingError(
            NANO_REQUEST_ERROR_DENIED, "Access denied", account_id=account_id, ip_address=ip_address
        )
    return {
        NanoRequestContext.ROOM_ID: room_id,  # parsed id for the server
        NanoRequestContext.ROOM_APIS: room_apis,  # served API flags
        NanoRequestContext.ROOM_CONFIG_MEMBER: access_flags[0],
        NanoRequestContext.ROOM_CONFIG_GROUP: access_flags[1],
    }
