"""
Datadog trace code for flask_cache
"""

# stdlib
import logging

# project
from .utils import _extract_conn_tags, _resource_from_cache_prefix
from ...constants import ANALYTICS_SAMPLE_RATE_KEY, SPAN_MEASURED_KEY
from ...ext import SpanTypes
from ...settings import config

# 3rd party
from flask.ext.cache import Cache


log = logging.Logger(__name__)

DEFAULT_SERVICE = 'flask-cache'

# standard tags
COMMAND_KEY = 'flask_cache.key'
CACHE_BACKEND = 'flask_cache.backend'
CONTACT_POINTS = 'flask_cache.contact_points'


def get_traced_cache(ddtracer, service=DEFAULT_SERVICE, meta=None):
    """
    Return a traced Cache object that behaves exactly as the ``flask.ext.cache.Cache class``
    """

    class TracedCache(Cache):
        """
        Traced cache backend that monitors any operations done by flask_cache. Observed actions are:
        * get, set, add, delete, clear
        * all ``many_`` operations
        """
        _datadog_tracer = ddtracer
        _datadog_service = service
        _datadog_meta = meta

        def __trace(self, cmd):
            """
            Start a tracing with default attributes and tags
            """
            # create a new span
            s = self._datadog_tracer.trace(cmd, span_type=SpanTypes.CACHE, service=self._datadog_service)
            s.set_tag(SPAN_MEASURED_KEY)
            # set span tags
            s.set_tag(CACHE_BACKEND, self.config.get('CACHE_TYPE'))
            s.set_tags(self._datadog_meta)
            # set analytics sample rate
            s.set_tag(
                ANALYTICS_SAMPLE_RATE_KEY,
                config.flask_cache.get_analytics_sample_rate()
            )
            # add connection meta if there is one
            if getattr(self.cache, '_client', None):
                try:
                    s.set_tags(_extract_conn_tags(self.cache._client))
                except Exception:
                    log.debug('error parsing connection tags', exc_info=True)

            return s

        def get(self, *args, **kwargs):
            """
            Track ``get`` operation
            """
            with self.__trace('flask_cache.cmd') as span:
                span.resource = _resource_from_cache_prefix('GET', self.config)
                if len(args) > 0:
                    span.set_tag(COMMAND_KEY, args[0])
                return super(TracedCache, self).get(*args, **kwargs)

        def set(self, *args, **kwargs):
            """
            Track ``set`` operation
            """
            with self.__trace('flask_cache.cmd') as span:
                span.resource = _resource_from_cache_prefix('SET', self.config)
                if len(args) > 0:
                    span.set_tag(COMMAND_KEY, args[0])
                return super(TracedCache, self).set(*args, **kwargs)

        def add(self, *args, **kwargs):
            """
            Track ``add`` operation
            """
            with self.__trace('flask_cache.cmd') as span:
                span.resource = _resource_from_cache_prefix('ADD', self.config)
                if len(args) > 0:
                    span.set_tag(COMMAND_KEY, args[0])
                return super(TracedCache, self).add(*args, **kwargs)

        def delete(self, *args, **kwargs):
            """
            Track ``delete`` operation
            """
            with self.__trace('flask_cache.cmd') as span:
                span.resource = _resource_from_cache_prefix('DELETE', self.config)
                if len(args) > 0:
                    span.set_tag(COMMAND_KEY, args[0])
                return super(TracedCache, self).delete(*args, **kwargs)

        def delete_many(self, *args, **kwargs):
            """
            Track ``delete_many`` operation
            """
            with self.__trace('flask_cache.cmd') as span:
                span.resource = _resource_from_cache_prefix('DELETE_MANY', self.config)
                span.set_tag(COMMAND_KEY, list(args))
                return super(TracedCache, self).delete_many(*args, **kwargs)

        def clear(self, *args, **kwargs):
            """
            Track ``clear`` operation
            """
            with self.__trace('flask_cache.cmd') as span:
                span.resource = _resource_from_cache_prefix('CLEAR', self.config)
                return super(TracedCache, self).clear(*args, **kwargs)

        def get_many(self, *args, **kwargs):
            """
            Track ``get_many`` operation
            """
            with self.__trace('flask_cache.cmd') as span:
                span.resource = _resource_from_cache_prefix('GET_MANY', self.config)
                span.set_tag(COMMAND_KEY, list(args))
                return super(TracedCache, self).get_many(*args, **kwargs)

        def set_many(self, *args, **kwargs):
            """
            Track ``set_many`` operation
            """
            with self.__trace('flask_cache.cmd') as span:
                span.resource = _resource_from_cache_prefix('SET_MANY', self.config)
                if len(args) > 0:
                    span.set_tag(COMMAND_KEY, list(args[0].keys()))
                return super(TracedCache, self).set_many(*args, **kwargs)

    return TracedCache
