from contextlib import contextmanager
from dogapi import dog_stats_api
import json
import logging
import requests
from django.conf import settings
from time import time
from uuid import uuid4
from django.utils.translation import get_language

log = logging.getLogger(__name__)


def strip_none(dic):
    return dict([(k, v) for k, v in dic.iteritems() if v is not None])


def strip_blank(dic):
    def _is_blank(v):
        return isinstance(v, str) and len(v.strip()) == 0
    return dict([(k, v) for k, v in dic.iteritems() if not _is_blank(v)])


def extract(dic, keys):
    if isinstance(keys, str):
        return strip_none({keys: dic.get(keys)})
    else:
        return strip_none({k: dic.get(k) for k in keys})


def merge_dict(dic1, dic2):
    return dict(dic1.items() + dic2.items())


@contextmanager
def request_timer(request_id, method, url):
    start = time()
    yield
    end = time()
    duration = end - start
    dog_stats_api.histogram('comment_client.request.time', duration, end)
    log.info(
        "comment_client_request_log: request_id={request_id}, method={method}, "
        "url={url}, duration={duration}".format(
            request_id=request_id,
            method=method,
            url=url,
            duration=duration
        )
    )


def perform_request(method, url, data_or_params=None, *args, **kwargs):
    if data_or_params is None:
        data_or_params = {}
    headers = {
        'X-Edx-Api-Key': getattr(settings, "COMMENTS_SERVICE_KEY", None),
        'Accept-Language': get_language(),
    }
    request_id = uuid4()
    request_id_dict = {'request_id': request_id}

    if method in ['post', 'put', 'patch']:
        data = data_or_params
        params = request_id_dict
    else:
        data = None
        params = merge_dict(data_or_params, request_id_dict)
    with request_timer(request_id, method, url):
        response = requests.request(
            method,
            url,
            data=data,
            params=params,
            headers=headers,
            timeout=5
        )

    if 200 < response.status_code < 500:
        raise CommentClientRequestError(response.text, response.status_code)
    # Heroku returns a 503 when an application is in maintenance mode
    elif response.status_code == 503:
        raise CommentClientMaintenanceError(response.text)
    elif response.status_code == 500:
        raise CommentClient500Error(response.text)
    else:
        if kwargs.get("raw", False):
            return response.text
        else:
            return json.loads(response.text)


class CommentClientError(Exception):
    def __init__(self, msg):
        self.message = msg

    def __str__(self):
        return repr(self.message)


class CommentClientRequestError(CommentClientError):
    def __init__(self, msg, status_code=400):
        super(CommentClientRequestError, self).__init__(msg)
        self.status_code = status_code


class CommentClient500Error(CommentClientError):
    pass


class CommentClientMaintenanceError(CommentClientError):
    pass
