import typing as t

from atproto_client import models
from atproto_client.models.utils import get_model_as_dict, get_or_create

from atproto_firehose.client import AsyncFirehoseClient, FirehoseClient

if t.TYPE_CHECKING:
    from atproto_firehose.models import MessageFrame

_REPOS_BASE_WEBSOCKET_URI = 'wss://bsky.network/xrpc'
_LABELS_BASE_WEBSOCKET_URI = 'wss://mod.bsky.app/xrpc'

_REPOS_DEFAULT_RECV_TIMEOUT = 30.0
_LABELS_DEFAULT_RECV_TIMEOUT = 300.0  # 5 minutes

# TODO(MarshalX): Everything here could be autogenerated from the lexicon.
_SUBSCRIBE_REPOS_MESSAGE_TYPE_TO_MODEL = {
    '#commit': models.ComAtprotoSyncSubscribeRepos.Commit,
    '#info': models.ComAtprotoSyncSubscribeRepos.Info,
    '#identity': models.ComAtprotoSyncSubscribeRepos.Identity,  # DEPRECATED
    '#account': models.ComAtprotoSyncSubscribeRepos.Account,
    '#sync': models.ComAtprotoSyncSubscribeRepos.Sync,
}
_SUBSCRIBE_LABELS_MESSAGE_TYPE_TO_MODEL = {
    '#labels': models.ComAtprotoLabelSubscribeLabels.Labels,
    '#info': models.ComAtprotoLabelSubscribeLabels.Info,
}

#: Subscribe Repos Message
SubscribeReposMessage = t.Union[
    models.ComAtprotoSyncSubscribeRepos.Commit,
    models.ComAtprotoSyncSubscribeRepos.Info,
    models.ComAtprotoSyncSubscribeRepos.Identity,
    models.ComAtprotoSyncSubscribeRepos.Account,
    models.ComAtprotoSyncSubscribeRepos.Sync,
]
#: Subscribe Labels Message
SubscribeLabelsMessage = t.Union[
    models.ComAtprotoLabelSubscribeLabels.Labels,
    models.ComAtprotoLabelSubscribeLabels.Info,
]


def parse_subscribe_repos_message(message: 'MessageFrame') -> SubscribeReposMessage:
    """Parse Firehose repositories message to the corresponding model.

    Note:
        Use `decode_inner_cbor` only when required to increase performance.

    Args:
        message: Message frame.

    Returns:
        :obj:`SubscribeReposMessage`: Corresponding message model.
    """
    model_class = _SUBSCRIBE_REPOS_MESSAGE_TYPE_TO_MODEL[message.type]
    return get_or_create(message.body, model_class)


def parse_subscribe_labels_message(message: 'MessageFrame') -> SubscribeLabelsMessage:
    """Parse Firehose labels message to the corresponding model.

    Args:
        message: Message frame.

    Returns:
        :obj:`SubscribeLabelsMessage`: Corresponding message model.
    """
    model_class = _SUBSCRIBE_LABELS_MESSAGE_TYPE_TO_MODEL[message.type]
    return get_or_create(message.body, model_class)


class FirehoseSubscribeReposClient(FirehoseClient):
    """Firehose subscribe repos client.

    Args:
        params: Parameters model.
        base_uri: Base websocket URI. Example: `wss://bsky.social/xrpc`.
        recv_timeout: Reconnect to the server after this many seconds of inactivity.
            Default is 30 seconds.
    """

    def __init__(
        self,
        params: t.Optional[t.Union[dict, 'models.ComAtprotoSyncSubscribeRepos.Params']] = None,
        base_uri: t.Optional[str] = _REPOS_BASE_WEBSOCKET_URI,
        recv_timeout: t.Optional[float] = _REPOS_DEFAULT_RECV_TIMEOUT,
    ) -> None:
        params_model = get_or_create(params, models.ComAtprotoSyncSubscribeRepos.Params)

        params_dict = None
        if params_model:
            params_dict = get_model_as_dict(params_model)

        super().__init__(
            method='com.atproto.sync.subscribeRepos', base_uri=base_uri, params=params_dict, recv_timeout=recv_timeout
        )


class AsyncFirehoseSubscribeReposClient(AsyncFirehoseClient):
    """Async firehose subscribe repos client.

    Args:
        params: Parameters model.
        base_uri: Base websocket URI. Example: `wss://bsky.social/xrpc`.
        recv_timeout: Reconnect to the server after this many seconds of inactivity.
            Default is 30 seconds.
    """

    def __init__(
        self,
        params: t.Optional[t.Union[dict, 'models.ComAtprotoSyncSubscribeRepos.Params']] = None,
        base_uri: t.Optional[str] = _REPOS_BASE_WEBSOCKET_URI,
        recv_timeout: t.Optional[float] = _REPOS_DEFAULT_RECV_TIMEOUT,
    ) -> None:
        params_model = get_or_create(params, models.ComAtprotoSyncSubscribeRepos.Params)

        params_dict = None
        if params_model:
            params_dict = get_model_as_dict(params_model)

        super().__init__(
            method='com.atproto.sync.subscribeRepos', base_uri=base_uri, params=params_dict, recv_timeout=recv_timeout
        )


class FirehoseSubscribeLabelsClient(FirehoseClient):
    """Firehose subscribe labels client.

    Args:
        params: Parameters model.
        base_uri: Base websocket URI. Example: `wss://bsky.social/xrpc`.
        recv_timeout: Reconnect to the server after this many seconds of inactivity.
            Default is 300 seconds (5 minutes).
    """

    def __init__(
        self,
        params: t.Optional[t.Union[dict, 'models.ComAtprotoLabelSubscribeLabels.Params']] = None,
        base_uri: t.Optional[str] = _LABELS_BASE_WEBSOCKET_URI,
        recv_timeout: t.Optional[float] = _LABELS_DEFAULT_RECV_TIMEOUT,
    ) -> None:
        params_model = get_or_create(params, models.ComAtprotoLabelSubscribeLabels.Params)

        params_dict = None
        if params_model:
            params_dict = get_model_as_dict(params_model)

        super().__init__(
            method='com.atproto.label.subscribeLabels', base_uri=base_uri, params=params_dict, recv_timeout=recv_timeout
        )


class AsyncFirehoseSubscribeLabelsClient(AsyncFirehoseClient):
    """Async firehose subscribe labels client.

    Args:
        params: Parameters model.
        base_uri: Base websocket URI. Example: `wss://bsky.social/xrpc`.
        recv_timeout: Reconnect to the server after this many seconds of inactivity.
            Default is 300 seconds (5 minutes).
    """

    def __init__(
        self,
        params: t.Optional[t.Union[dict, 'models.ComAtprotoLabelSubscribeLabels.Params']] = None,
        base_uri: t.Optional[str] = _LABELS_BASE_WEBSOCKET_URI,
        recv_timeout: t.Optional[float] = _LABELS_DEFAULT_RECV_TIMEOUT,
    ) -> None:
        params_model = get_or_create(params, models.ComAtprotoLabelSubscribeLabels.Params)

        params_dict = None
        if params_model:
            params_dict = get_model_as_dict(params_model)

        super().__init__(
            method='com.atproto.label.subscribeLabels', base_uri=base_uri, params=params_dict, recv_timeout=recv_timeout
        )
