import asyncio
from typing import Any, Awaitable, Callable, Dict

from .. import app_config, util
from ..broker import Broker, BrokerOptions
from ..logger import Logger
from ..request import Request
from ..response import Response

NAME = 'Info'
"""Name of the main class of the module.

Used by the App class
to obtain the correct class via getattr

"""


class InfoOptions(BrokerOptions):
    """Info broker options.

    Attributes:
        get (str): info request name, supported:
            - ngcp.roles
        params (str): params, optional

    """
    get: str
    params: str


class Info(Broker):
    """Info broker class, inherits Broker."""

    def __init__(self, send_req_cb: Callable[[Request],
                                             Awaitable[None]]) -> None:
        """Constructor for Info class.

        Args:
            send_req_cb (callable): callback that a broker can use to trigger
                sending another request. Set by the App class.

        Returns:
            None

        """
        self._logger = Logger(f'broker.{util.short_class_name(self)}')
        super().__init__(send_req_cb)

    async def start(self) -> None:
        """Starts the broker.

        Currently does nothing as there is no
        specific logic related to the broker startup. Reserved for
        future use and consistency with the parent class

        Args:
            None

        Returns
            None

        """
        pass

    async def process_request(self,
                              request: Request,
                              options: Dict[str, Any],
                              callback: Callable[[Request, Response],
                                                 Awaitable[None]]) -> None:
        """Processes the provided request and sends back responses.

        Uses the provided callback function

        Request options:
            parse_dst (str): dst string to parse
                and return nodes list based on the input

        Args:
            request (Request): Request message object
            options (Dict): broker options
            callback (Callable[[Request, Response], Awaitable[None]]):
                callback function to dispatch the responses to

        Returns
            None

        """
        info_options = InfoOptions()
        util.apply_dict(options, info_options)

        response = Response()
        response.status = 'accepted'
        await asyncio.ensure_future(callback(request, response))

        get = getattr(info_options, 'get', '')
        params = getattr(info_options, 'params', '')

        self.log.debug(util.compact_str(
            f'info get={get} params={params}'
        ))

        response = Response()
        response.status = 'done'

        if get == 'ngcp.roles':
            response.data = app_config.ngcp.roles
        elif get == 'ngcp.dst_nodes':
            if request.data and type(request.data) is str:
                parse_dst = str(request.data)
                response.data = list(await util.get_dst_nodes(parse_dst))
            else:
                response.status = 'error'
                response.reason = util.compact_str(
                    'missing or invalid \
                     request.data (expected str)'
                )
        else:
            response.status = 'error'
            response.reason = f'invalid info get={get}'

        await asyncio.ensure_future(callback(request, response))
