__all__ = [
    'BaseMetric',
    'Balance',
    'bind_client',
    'NewMessageThreads',
    'NewUserBonuses',
    'NewUserSkills',
]
import datetime
import toloka.async_client.client
import toloka.client
import typing


def bind_client(metrics: typing.List['BaseMetric'], toloka_client: typing.Union[toloka.client.TolokaClient, toloka.async_client.client.AsyncTolokaClient]) -> None:
    """Sets/updates toloka_client for all metrics in list.

    Examples:
        How to bind same client for all metrics:
        >>> import toloka.client as toloka
        >>> from toloka.metrics import AssignmentsInPool, Balance, bind_client, MetricCollector
        >>>
        >>> toloka_client = toloka.TolokaClient(auth_token, 'PRODUCTION')
        >>>
        >>> collector = MetricCollector(
        >>>     [
        >>>         Balance(),
        >>>         AssignmentsInPool(pool_id),
        >>>     ],
        >>> )
        >>> bind_client(collector.metrics, toloka_client)
        ...

        How to bind several clients:
        >>> metrics_1 = bind_client([Balance(), AssignmentsInPool(pool_id_1)], toloka_client_1)
        >>> metrics_2 = bind_client([Balance(), AssignmentsInPool(pool_id_2)], toloka_client_2)
        >>> collector = MetricCollector(metrics_1 + metrics_2)
        ...
    """
    ...


class BaseMetric:
    """Base class for all metrics.

    Stores TolokaClient instance for this metric.
    """

    def set_client(self, toloka_client: typing.Union[toloka.client.TolokaClient, toloka.async_client.client.AsyncTolokaClient]):
        """Sets both TolokaClient and AsyncTolokaClient for the object.

        New instance of AsyncTolokaClient is created is case of TolokaClient being passed.
        """
        ...

    def get_lines(self) -> typing.Dict[str, typing.List[typing.Tuple[typing.Any, typing.Any]]]:
        """Gather and return metrics

        All metrics returned in the same format: named list, contain pairs of: datetime of some event, metric value.
        Could not return some metrics in dict on iteration or return it with empty list:
        means that is nothing being gathered on this step. This is not zero value!

        Return example:
        {
            'rejected_assignments_in_pool': [(datetime.datetime(2021, 8, 12, 10, 4, 44, 895232), 0)],
            'submitted_assignments_in_pool': [(datetime.datetime(2021, 8, 12, 10, 4, 45, 321904), 75)],
            'accepted_assignments_in_pool': [(datetime.datetime(2021, 8, 12, 10, 4, 45, 951156), 75)],
            'accepted_events_in_pool': [(datetime.datetime(2021, 8, 11, 15, 13, 3, 65000), 1), ... ],
            'rejected_events_in_pool': [],
            # no toloka_requester_balance on this iteration
        }
        """
        ...

    def get_line_names(self) -> typing.List[str]:
        """Returns a list of metric names that can be generated by this class instance.
        For example, if a class can generate 5 metrics, but some instance generate only 3 of them,
        this method will return a list with exactly 3 strings. If you call 'get_metrics' on this instance,
        it could return from 0 to 3 metrics.
        """
        ...

    def __init__(
        self,
        *,
        toloka_client: toloka.client.TolokaClient = None,
        atoloka_client: toloka.async_client.client.AsyncTolokaClient = None,
        timeout: datetime.timedelta = ...
    ) -> None:
        """Method generated by attrs for class BaseMetric.
        """
        ...

    toloka_client: toloka.client.TolokaClient
    atoloka_client: toloka.async_client.client.AsyncTolokaClient
    timeout: datetime.timedelta


class Balance(BaseMetric):
    """Traking your Toloka balance.

    Returns only one metric: don't spend and don't reserved money on your acount.

    Args:
        balance_name: Metric name. Default 'toloka_requester_balance'.

    Raises:
        ValueError: If all metric names are set to None.

    Example:
        How to collect this metrics:
        >>> def print_metric(metric_dict):
        >>>     print(metric_dict)
        >>>
        >>> collector = MetricCollector([Balance(toloka_client=toloka_client)], print_metric)
        >>> asyncio.run(collector.run())
        {
            toloka_requester_balance: [(datetime.datetime(2021, 8, 30, 10, 30, 59, 628239), Decimal('123.4500'))],
        }
    """

    def get_line_names(self) -> typing.List[str]:
        """Returns a list of metric names that can be generated by this class instance.
        """
        ...

    def __init__(
        self,
        balance_name: typing.Optional[str] = None,
        *,
        toloka_client: toloka.client.TolokaClient = None,
        atoloka_client: toloka.async_client.client.AsyncTolokaClient = None,
        timeout: datetime.timedelta = ...
    ) -> None:
        """Method generated by attrs for class Balance.
        """
        ...

    toloka_client: toloka.client.TolokaClient
    atoloka_client: toloka.async_client.client.AsyncTolokaClient
    timeout: datetime.timedelta
    balance_name: typing.Optional[str]


class NewUserBonuses(BaseMetric):
    """Tracking the new user bonuses - count or money amount.

    Args:
        count_name: Metric name for a count of new bonuses.
        money_name: Metric name for amount of money in new bonuses.
        join_events: Count all events in one point.  Default False.

    Example:
        How to collect this metrics:
        >>> def print_metric(metric_dict):
        >>>     print(metric_dict)
        >>>
        >>> collector = MetricCollector([NewUserBonuses(toloka_client=toloka_client)], print_metric)
        >>> asyncio.run(collector.run())
        {
            'bonus_count': [(datetime.datetime(2021, 11, 18, 8, 29, 9, 734373), 0)],
            'bonus_money': [(datetime.datetime(2021, 11, 18, 8, 29, 9, 734377), Decimal('0'))]
        }
    """

    def get_line_names(self) -> typing.List[str]:
        """Returns a list of metric names that can be generated by this class instance.
        """
        ...

    def __init__(
        self,
        count_name: typing.Optional[str] = None,
        money_name: typing.Optional[str] = None,
        join_events: bool = False,
        *,
        toloka_client: toloka.client.TolokaClient = None,
        atoloka_client: toloka.async_client.client.AsyncTolokaClient = None,
        timeout: datetime.timedelta = ...
    ) -> None:
        """Method generated by attrs for class NewUserBonuses.
        """
        ...

    toloka_client: toloka.client.TolokaClient
    atoloka_client: toloka.async_client.client.AsyncTolokaClient
    timeout: datetime.timedelta
    _count_name: typing.Optional[str]
    _money_name: typing.Optional[str]
    _join_events: bool


class NewUserSkills(BaseMetric):
    """Tracking the new user skill values

    Args:
        skill_id: Which skill we will be tracking.
        count_name: Metric name for a count of new skill assignments. When skill changes it counts to.
        value_name: Metric name for exact values of new skill level for each skill assignment. It could be useful to track mean value or some medians.
        join_events: Count all events in one point.  Default False. "Values" never join.

    Example:
        How to collect this metrics:
        >>> def print_metric(metric_dict):
        >>>     print(metric_dict)
        >>>
        >>> collector = MetricCollector
        >>> (
        >>>     [
        >>>         NewUserSkills(
        >>>             toloka_client=toloka_client,
        >>>             count_name='count',
        >>>             value_name='values',
        >>>         )
        >>>     ],
        >>>     print_metric
        >>> )
        >>> asyncio.run(collector.run())
        {
            'count': [(datetime.datetime(2021, 11, 18, 8, 31, 54, 11000), 1)],
            'values':  [(datetime.datetime(2021, 11, 18, 8, 31, 54, 11000), Decimal('50.000000000000'))],
        }
    """

    def get_line_names(self) -> typing.List[str]:
        """Returns a list of metric names that can be generated by this class instance.
        """
        ...

    def __init__(
        self,
        skill_id: str,
        count_name: typing.Optional[str] = None,
        value_name: typing.Optional[str] = None,
        join_events: bool = False,
        *,
        toloka_client: toloka.client.TolokaClient = None,
        atoloka_client: toloka.async_client.client.AsyncTolokaClient = None,
        timeout: datetime.timedelta = ...
    ) -> None:
        """Method generated by attrs for class NewUserSkills.
        """
        ...

    toloka_client: toloka.client.TolokaClient
    atoloka_client: toloka.async_client.client.AsyncTolokaClient
    timeout: datetime.timedelta
    _skill_id: str
    _count_name: typing.Optional[str]
    _value_name: typing.Optional[str]
    _join_events: bool


class NewMessageThreads(BaseMetric):
    """Tracking the new messages

    Could count messages in projects or pools. If you want to track messages count in several projects/pools, don't get several
    NewMessageThreads instance. You can gather all in one instance.

    Args:
        count_name: Metric name for a count of new messages.
        projects_name: Dictionary that allows count messages on exact projects. {project_id: line_name}
        pools_name: Dictionary that allows count messages on exact pools. {pool_id: line_name}
        join_events: Count all events in one point. Default False. "Values" never join.

    Example:
        How to collect this metrics:
        >>> def print_metric(metric_dict):
        >>>     print(metric_dict)
        >>>
        >>> collector = MetricCollector
        >>> (
        >>>     [
        >>>         NewMessageThreads(
        >>>             toloka_client=toloka_client,
        >>>             count_name='messages_count'
        >>>             pools_name={'123': 'my_train_pool', '456': 'my_working_pool'},
        >>>             projects_name={'01': 'pedestrian_proj', '02': 'checking_proj'},
        >>>             join_events=True,
        >>>         )
        >>>     ],
        >>>     print_metric
        >>> )
        >>> asyncio.run(collector.run())
        {
            # all messages in all projects and pools
            'messages_count': [(datetime.datetime(2021, 11, 19, 9, 40, 15, 970000), 10)],
            # messages on this exact pool
            'my_train_pool': [(datetime.datetime(2021, 11, 19, 12, 42, 50, 554830), 4)],
            # with 'join_events=True' it will be zero if no messages
            'my_working_pool': [(datetime.datetime(2021, 11, 19, 12, 42, 50, 554830), 0)],
            'pedestrian_proj': [(datetime.datetime(2021, 11, 19, 12, 42, 50, 554830), 1)],
            # total count != sum of other counts, because could exist different pools and projects
            'checking_proj': [(datetime.datetime(2021, 11, 19, 12, 42, 50, 554830), 1)],
        }
    """

    def get_line_names(self) -> typing.List[str]:
        """Returns a list of metric names that can be generated by this class instance.
        """
        ...

    def __init__(
        self,
        count_name: typing.Optional[str] = None,
        projects_name: typing.Dict[str, str] = ...,
        pools_name: typing.Dict[str, str] = ...,
        join_events: bool = False,
        *,
        toloka_client: toloka.client.TolokaClient = None,
        atoloka_client: toloka.async_client.client.AsyncTolokaClient = None,
        timeout: datetime.timedelta = ...
    ) -> None:
        """Method generated by attrs for class NewMessageThreads.
        """
        ...

    toloka_client: toloka.client.TolokaClient
    atoloka_client: toloka.async_client.client.AsyncTolokaClient
    timeout: datetime.timedelta
    _count_name: typing.Optional[str]
    _projects_name: typing.Dict[str, str]
    _pools_name: typing.Dict[str, str]
    _join_events: bool
