import logging

from django.conf import settings
from django.core.exceptions import ValidationError, ObjectDoesNotExist
from django.core.urlresolvers import reverse
from django.db import models
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
from django.utils.text import slugify

from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import fields

from model_utils.managers import InheritanceManager as ModelUtilsInheritanceManager

import passerelle
import requests

KEYTYPE_CHOICES = (
    ('API', 'API Key'),
    ('SIGN', 'Signature HMAC'),
)

class ApiUser(models.Model):
    username = models.CharField(max_length=128,
            verbose_name=_('Username'))
    fullname = models.CharField(max_length=50,
            verbose_name=_('Full Name'))
    description = models.TextField(blank=True,
            verbose_name=_('Description'))

    keytype = models.CharField(max_length=4, choices=KEYTYPE_CHOICES,
            blank=True, verbose_name=_('Key Type'))
    key = models.CharField(max_length=256, blank=True, verbose_name=_('Key'))

    ipsource = models.GenericIPAddressField(blank=True, null=True, unpack_ipv4=True,
            verbose_name=_('IP Address'))

    def __unicode__(self):
        return u'%s <%s>' % (self.fullname, self.username)

    def clean(self):
        if self.keytype and not self.key:
            raise ValidationError(_('Key can not be empty for type %s.') % self.keytype)


class TemplateVar(models.Model):
    name = models.CharField(max_length=64)
    value = models.CharField(max_length=128)

    def __unicode__(self):
        return u'%s - %s' % (self.name, self.value)


class InheritanceManager(ModelUtilsInheritanceManager):

    def get_slug(self, slug, request=None):
        '''
        Returns a resource by its slug
        Request based access control, if request is present
        '''
        resource = self.get_subclass(slug=slug)
        if request and not resource.is_accessible_by(request):
            raise PermissionDenied
        return resource

    def filter_apiuser(self, apiuser):
        '''
        Returns all resources accessible by apiuser
        '''
        return self.filter(Q(users=None) | Q(users=apiuser))


class BaseResource(models.Model):
    title = models.CharField(max_length=50)
    slug = models.SlugField()
    description = models.TextField()
    users = models.ManyToManyField(ApiUser, blank=True)
    log_level = models.CharField(
        _('Log Level'), max_length=10,
        choices = (
           ('NOTSET','NOTSET'),
           ('DEBUG','DEBUG'),
           ('INFO','INFO'),
           ('WARNING','WARNING'),
           ('ERROR','ERROR'),
           ('CRITICAL','CRITICAL'),
           ('FATAL','FATAL'),
        ),
        default='NOTSET'
    )

    objects = InheritanceManager()

    parameters = None
    manager_view_template_name = None

    class Meta:
        abstract = True

    def __init__(self, *args, **kwargs):
        super(BaseResource, self).__init__(*args, **kwargs)
        self.logger = logging.getLogger('passerelle.resource.%s.%s' % (
            slugify(unicode(self.__class__.__name__)), self.slug)
        )
        self.logger.setLevel(getattr(logging, self.log_level))

    def __unicode__(self):
        return self.title

    def is_accessible_by(self, request):
        if request.user.is_superuser:
            return True
        restricted = self.users.all()
        return  not restricted or request.apiuser in restricted

    @classmethod
    def is_enabled(cls):
        # TODO: once the legacy connectors are ported, the test on
        # get_icon_class won't be necessary anymore.
        if not hasattr(cls, 'get_icon_class'):
            return False
        return getattr(settings, 'PASSERELLE_APP_%s_ENABLED' % cls._meta.app_label.upper(), False)

    @property
    def requests(self):
        return passerelle.utils.LoggedRequest(logger=self.logger)

    @classmethod
    def get_verbose_name(cls):
        return cls._meta.verbose_name

    @classmethod
    def get_connector_slug(cls):
        return cls._meta.app_label.replace('_', '-')

    def get_absolute_url(self):
        return reverse('view-connector',
                kwargs={'connector': self.get_connector_slug(), 'slug': self.slug})

    @classmethod
    def get_add_url(cls):
        return reverse('create-connector', kwargs={'connector': cls.get_connector_slug()})

    def get_edit_url(self):
        return reverse('edit-connector',
                kwargs={'connector': self.get_connector_slug(), 'slug': self.slug})

    def get_delete_url(self):
        return reverse('delete-connector',
                kwargs={'connector': self.get_connector_slug(), 'slug': self.slug})

    def get_description_fields(self):
        return [(x, getattr(self, x.name, None)) for x in self._meta.fields if x.name not in (
            'id', 'title', 'slug', 'description', 'log_level', 'users')]


class AccessRight(models.Model):
    codename = models.CharField(max_length=100, verbose_name='codename')
    resource_type = models.ForeignKey(ContentType)
    resource_pk = models.PositiveIntegerField()
    resource = fields.GenericForeignKey('resource_type', 'resource_pk')
    apiuser = models.ForeignKey(ApiUser)

    class Meta:
        permissions = (
            ('view_accessright', 'Can view access right'),
        )

    def __unicode__(self):
        return '%s (on %s <%s>) (for %s)' % (self.codename,
                self.resource_type, self.resource_pk, self.apiuser)
