"""A Sentry Client Tool."""
import sentry_sdk
from collections import namedtuple
from time import monotonic
from enum import Enum

SentryLog = namedtuple("SentryLog", ("type", "tags", "frequency", "description"))


class LogType(Enum):
    ERROR = "err"
    MESSAGE = "msg"


class SentryClient:
    """A client to log errors or messages to Sentry.
    
    Attribute:
        logs: A map that records the time of last sending of SentryLog to Sentry.
    """

    def __init__(self, sentry_key, server_name: str):
        """Initializes the instance and initialize sentry_sdk based on server_name.

        Args:
            sentry_key: The Sentry DSN of the sentry project.
            server_name: A string representing the server name.
        """
        sentry_sdk.init(dsn=sentry_key,
                        traces_sample_rate=1.0,
                        attach_stacktrace=True,
                        include_local_variables=True,
                        server_name=server_name)
        self.logs: dict = {}

    def _add_log(self, log_type: LogType, frequency: int, description, tags: dict = {}):
        """Insert an error or a message to Sentry.

        Insert a SentryLog to Sentry when first time encountering or reached
        its frequency.

        Args:
            log_type:
                The type of SentryLog, either 'err' or 'msg'.
            frequency:
                The frequency (time interval) of SentryLog being sent to Sentry for
                a second time from last sending.
            description:
                The actual error or message to be sent to Sentry.
            tags:
                A dictionary of tag to tag value associated to the log.
        """
        assert isinstance(frequency, int)
        log = SentryLog(log_type, str(tags), frequency, str(description))
        log_time = int(monotonic())
        last_log_time = self.logs.get(log)
        if not last_log_time or log_time - last_log_time >= frequency:
            with sentry_sdk.push_scope() as scope:
                for tag, tag_value in tags.items():
                    scope.set_tag(tag, tag_value)
                if log_type == LogType.ERROR:
                    sentry_sdk.capture_exception(description)
                elif log_type == LogType.MESSAGE:
                    sentry_sdk.capture_message(description)
            if frequency > 0:
                self.logs[log] = log_time

    def error(self, description, frequency: int = 0, tags: dict = {}):
        """Insert an error to Sentry.

        See _add_log for more details.
        """
        assert isinstance(frequency, int)
        self._add_log(LogType.ERROR, frequency, description, tags)

    def warning(self, description: str, frequency: int = 0, tags: dict = {}):
        """Insert a warning message to Sentry.

        See _add_log for more details.
        """
        assert isinstance(frequency, int)
        self._add_log(LogType.MESSAGE, frequency, description, tags)
