try:
    from django.http import HttpRequest

    _django_request = HttpRequest
except ImportError:
    _django_request = None

try:
    from flask import request

    _flask_request = request.__class__
except ImportError:
    _flask_request = None

import util


class LogRequest(object):
    headers = None
    method = None
    host = None
    path = None
    query_params = None
    body = None
    gr_trace_id = None
    user_id = None

    def dict(self):
        return {
            'headers': self._validate(self.headers, dict),
            'method': self._validate(self.method, str),
            'host': self._validate(self.host, str),
            'path': self._validate(self.path, str),
            'query_params': self._validate(self.query_params, dict),
            'body': self._validate(self.body, dict),
            'gr_trace_id': self._validate(self.gr_trace_id, str),
            'user_id': self.user_id,
        }

    def _validate(self, property, expected_type):
        try:
            return expected_type(property)
        except (TypeError, ValueError) as e:
            return None


class DjangoRequestAdapter(LogRequest):
    def __init__(self, request):
        """
        :param request: Adaptee is WSGIRequest object
        """
        self.host = request.META.get('HTTP_HOST')
        self.method = request.META.get('REQUEST_METHOD')
        self.headers = {
            k: v for k, v in request.META.iteritems() if k.startswith('HTTP_')
            }
        self.path = request.path
        self.query_params = request.GET.dict()
        self.body = request.POST.dict()
        self.gr_trace_id = (
            self.headers.get('HTTP_X_GR_TRACE_ID')
            or self.headers.get('HTTP_REQUEST_ID')
            or self.headers.get('HTTP_CORRELATION_ID')
            or util.generate_trace_id()
        )
        self.user_id = (
            self.headers.get('HTTP_USER')
            or self.headers.get('HTTP_X_GR_UID')
        )


class FlaskRequestAdapter(LogRequest):
    def __init__(self, request):
        self.host = request.host
        self.method = request.method
        self.headers = dict(request.headers)
        self.path = request.path
        self.query_params = dict(request.args)
        try:
            self.body = request.get_json(silent=True) if request.is_json else request.form
        except Exception as e:
            self.body = None
            pass
        self.gr_trace_id = (
            self.headers.get('X-Gr-Trace-Id')
            or self.headers.get('Request-Id')
            or self.headers.get('CorrelationId')
            or util.generate_trace_id()
        )
        self.user_id = self.headers.get('User') or self.headers.get('X-Gr-Uid')


def get_logrequest(request, adapter_cls=None):
    """
    :param request: django / flask request instance
    :param adapter_cls: Adapter subclass of LogRequest
    :return: LogRequest instance
    """
    if isinstance(request, LogRequest):
        return request
    if adapter_cls:
        return adapter_cls(request)
    if _django_request and isinstance(request, _django_request):
        return DjangoRequestAdapter(request)
    if _flask_request and isinstance(request, _flask_request):
        return FlaskRequestAdapter(request)
    return None
