import re

from django.core.urlresolvers import reverse
from django.core.serializers.json import DjangoJSONEncoder
from django.http import HttpResponse
from django.utils.encoding import smart_unicode, force_unicode


try:
    import json
except ImportError:
    from django.utils import simplejson as json


class LookupBase(object):
    @classmethod
    def name(cls):
        app_name = cls.__module__.split('.')[-2].lower()
        class_name = cls.__name__.lower()
        name = u'%s-%s' % (app_name, class_name)
        return name

    @classmethod
    def url(cls):
        return reverse('autocomplete-lookup', args=[cls.name()])

    def get_query(self, request, term):
        return []

    def get_item_label(self, item):
        return smart_unicode(item)

    def get_item_id(self, item):
        return smart_unicode(item)

    def get_item_value(self, item):
        return smart_unicode(item)

    def get_item(self, value):
        return value

    def create_item(self, value):
        raise NotImplemented()

    def format_item(self, item):
        return {
            'id': self.get_item_id(item),
            'value': self.get_item_value(item),
            'label': self.get_item_label(item)
        }

    def results(self, request):
        term = request.GET.get('term', '')
        raw_data = self.get_query(request, term)
        data = []
        for item in raw_data:
            data.append(self.format_item(item))
        content = json.dumps(data, cls=DjangoJSONEncoder, ensure_ascii=False)
        return HttpResponse(content, content_type='application/json')


class LookupAlreadyRegistered(Exception):
    pass


class LookupNotRegistered(Exception):
    pass


class LookupInvalid(Exception):
    pass


class LookupRegistry(object):
    def __init__(self):
        self._registry = {}

    def validate(self, lookup):
        if not issubclass(lookup, LookupBase):
            raise LookupInvalid(u'Registered lookups must inherit from the \
            LookupBase class')

    def register(self, lookup):
        self.validate(lookup)
        name = force_unicode(lookup.name())
        if name in self._registry:
            raise LookupAlreadyRegistered(u'The name %s is already registered' \
                                          % name)
        self._registry[name] = lookup

    def unregister(self, lookup):
        self.validate(lookup)
        name = force_unicode(lookup.name())
        if name not in self._registry:
            raise LookupNotRegistered(u'The name %s is not registered' % name)
        del self._registry[name]

    def get(self, key):
        return self._registry.get(key, None)


class ModelLookup(LookupBase):
    model = None
    filters = {}
    search_field = ''

    def get_query(self, request, term):
        qs = self.get_queryset()
        if term and self.search_field:
            qs = qs.filter(**{self.search_field: term})
        return qs

    def get_queryset(self):
        qs = self.model._default_manager.get_query_set()
        if self.filters:
            qs = qs.filter(**self.filters)
        return qs

    def get_item_id(self, item):
        return item.pk

    def get_item(self, value):
        item = None
        if value:
            try:
                item = self.get_queryset().filter(pk=value)[0]
            except IndexError:
                pass
        return item

    def create_item(self, value):
        data = {}
        if self.search_field:
            field_name = re.sub(r'__\w+$', '', self.search_field)
            if field_name:
                data = {field_name: value}
        return self.model(**data)
