import logging

from eventlet.queue import Queue
from nameko.extensions import DependencyProvider
from raven import Client


class SentryReporter(DependencyProvider):
    """ Send exceptions generated by entrypoints to a sentry server.
    """
    _gt = None
    queue = None
    client = None

    def _run(self):

        while True:
            item = self.queue.get()
            if item is None:
                break

            exc_info, message, extra, data = item
            self.client.captureException(
                exc_info, message=message, extra=extra, data=data)

            # these will remain in scope until the next iteration and
            # can potentially be large, so delete to reclaim the memory now
            del exc_info, message, extra, data, item

    def start(self):
        self._gt = self.container.spawn_managed_thread(
            self._run, protected=True)

    def stop(self):
        self.queue.put(None)

        if self._gt is not None:
            self._gt.wait()

    def setup(self):
        sentry_config = self.container.config.get('SENTRY')

        dsn = sentry_config['DSN']
        kwargs = sentry_config.get('CLIENT_CONFIG', {})

        self.queue = Queue()
        self.client = Client(dsn, **kwargs)

    def worker_result(self, worker_ctx, result, exc_info):
        if exc_info is None:
            return

        exc = exc_info[1]
        call_id = worker_ctx.call_id
        parent_call_id = worker_ctx.immediate_parent_call_id

        expected_exceptions = getattr(
            worker_ctx.entrypoint, 'expected_exceptions', tuple())

        level = logging.ERROR
        if expected_exceptions and isinstance(exc, expected_exceptions):
            level = logging.WARNING

        message = (
            'Unhandled exception in call {}: '
            '{} {!r}'.format(call_id, exc_info[0].__name__, str(exc))
        )

        logger = '{}.{}'.format(
            worker_ctx.service_name, worker_ctx.entrypoint.method_name)

        data = {
            'logger': logger,
            'level': level,
            'message': message,
            'tags': {
                'call_id': call_id,
                'parent_call_id': parent_call_id,
            },
        }

        extra = {'exc': exc}

        self.queue.put((exc_info, message, extra, data))
