import re

from rest_framework.request import Request

from .utils import flatten


class AchieveryAPIRequest(Request):

    def __init__(self, *args, **kwargs):
        super(AchieveryAPIRequest, self).__init__(*args, **kwargs)

        self.PARAMS = RequestParamsParser(self.QUERY_PARAMS)


def validate_many(values):
    if not values:
        return()

    return list(flatten(map(validate_one, values)))


def validate_one(value):
    if not value:
        return ''

    if ',' in value:
        return validate_many(value.split(','))

    try:
        return int(value)
    except ValueError:
        pass

    for b in True, False:
        if str(b).lower() == value.lower():
            return b

    if 'null' == value.lower():
        return None

    return value


class RequestParam(object):

    def __init__(self, name, default=None):
        self.name = name
        self.default = default


class AnyRequestParam(RequestParam):

    def __get__(self, instance, owner):
        """
        @type instance: RequestParamsParser
        """
        return {
            key: validate_many(values)
            for key, values in instance.source_params.lists()
            if re.match(self.name, key)
        }


class IntegerParam(RequestParam):
    def __get__(self, instance, owner):
        try:
            result = int(instance.source_params.get(self.name))
        except TypeError:
            result = self.default
        return result


class StringListParam(RequestParam):
    def __get__(self, instance, owner):
        raw = instance.source_params.getlist(self.name)
        validated = validate_many(raw)
        return list(filter(bool, validated))


class StringParam(RequestParam):
    def __get__(self, instance, owner):
        raw = instance.source_params.get(self.name)
        validated = validate_one(raw)
        return validated


class BoolParam(RequestParam):
    def __get__(self, instance, owner):
        raw = instance.source_params.get(self.name, 'true')
        return bool(validate_one(raw))


class RequestParamsParser(object):
    limit = IntegerParam('_limit', default=50)
    page = IntegerParam('_page', default=1)
    fields = StringListParam('_fields')
    sort = StringListParam('_sort')
    filter = AnyRequestParam(r'[^_].*')
    search = StringParam('_search')
    localize = BoolParam('_localize')

    def __init__(self, params):
        self.source_params = params
