import logging

from django import VERSION as django_version
from django.conf import settings
from django.conf.urls import url
from django.contrib.auth.models import Group
from django.core.exceptions import ObjectDoesNotExist
from guardian.shortcuts import assign_perm
from tastypie import fields, http
from tastypie.authorization import Authorization
from tastypie.exceptions import BadRequest
from tastypie.resources import Bundle, ModelResource, ALL
from tastypie.utils import dict_strip_unicode_keys

from ldt.api.ldt.authentication import (SessionAuthentication,
    MultiAuthentication, ApiKeyAuthentication)
from ldt.api.ldt.resources import ContentResource
from ldt.api.ldt.resources.user import UserResource
from ldt.api.ldt.serializers.cinelabserializer import CinelabSerializer
from ldt.ldt_utils.models import Project
from ldt.security import protect_models, unprotect_models
from ldt.security.permissionchecker import check_object_perm_for_user


logger = logging.getLogger(__name__)

class ProjectResource(ModelResource):
    contents = fields.ManyToManyField(ContentResource, 'contents')
    owner = fields.ForeignKey(UserResource, 'owner')
    class Meta:
        allowed_methods = ['get', 'post', 'put']
        authorization = Authorization() # BE CAREFUL WITH THAT, it's unsecure
        authentication = MultiAuthentication(ApiKeyAuthentication(), SessionAuthentication())
        resource_name = 'projects'
        queryset = Project.objects.all()
        serializer = CinelabSerializer()
        filtering = {
            'state' : ALL,
            'ldt_id' : ALL,
            'title' : ALL
        }
        # In the future version :
        # detail_uri_name = 'ldt_id'
    
    
    def get_object_list(self, request):
        return Project.safe_objects.all()
    
    def prepend_urls(self):
        return [
            url(r"^(?P<resource_name>%s)/(?P<ldt_id>[\w\d_.-]+)/$" % self._meta.resource_name, self.wrap_view('dispatch_detail'), name="api_dispatch_detail"),
        ]
    
    def get_resource_uri(self, bundle_or_obj=None, url_name='api_dispatch_list'):
        if bundle_or_obj is None:
            return super(ProjectResource, self).get_resource_uri(bundle_or_obj,url_name)
        kwargs = {
            'resource_name': self._meta.resource_name,
            'api_name': self._meta.api_name
        }
        if isinstance(bundle_or_obj, Bundle):
            kwargs['ldt_id'] = bundle_or_obj.obj.ldt_id
        else:
            kwargs['ldt_id'] = bundle_or_obj.ldt_id
        return self._build_reverse_url("api_dispatch_detail", kwargs=kwargs)
        
    # Create a new project. Used with post_detail and with no ldt_id in the url
    def obj_create(self, bundle, **kwargs):
        request = bundle.request
        if request is None:
            raise BadRequest("Request object must be passed as an argument")
        unprotect_models()
        bundle = super(ProjectResource, self).obj_create(bundle, **kwargs)
        # Assign permission for the owner
        assign_perm('view_project', request.user, bundle.obj)
        assign_perm('change_project', request.user, bundle.obj)
        # Since the project is published by default, we assign permission for the everyone group
        everyone = Group.objects.get(name=settings.PUBLIC_GROUP_NAME)
        assign_perm('ldt_utils.view_project', everyone, bundle.obj)
        protect_models()
        return bundle
    
    
    # Prevent for put_list and delete all objects.
    def obj_delete_list(self, bundle, **kwargs):
        raise BadRequest("PUT with a list of projects is forbidden.")
    
    
    def hydrate_image(self, bundle):
        image_data = bundle.data.get('image', None)
        if image_data and image_data.startswith(settings.MEDIA_URL):
            bundle.data['image'] = image_data[len(settings.MEDIA_URL):]
        return bundle
    
    # Updates an existing project. Used with post_detail and with a ldt_id in the url
    def post_detail(self, request, **kwargs):
        # Inspired by put_detail but we only update an object. We can not create one.
        """
        Since always_return_data is not set to True, 
        the request returns accepted (202) if the project is well updated
        and Not Found (404) if the id in url or datas were incorrect.
        """
        if django_version >= (1, 4):
            body = request.body
        else:
            body = request.raw_post_data
        deserialized = self.deserialize(request, body, format=request.META.get('CONTENT_TYPE', 'application/json'))
        deserialized = self.alter_deserialized_detail_data(request, deserialized)
        bundle = self.build_bundle(data=dict_strip_unicode_keys(deserialized), request=request)
        
        try:
            # We have to check if the user can change the project BEFORE calling obj_update
            proj = Project.objects.get(ldt_id=bundle.data['ldt_id'])
            if check_object_perm_for_user(proj, "change_project", request.user):
                # Even if the user has the guardian right to change the project, 
                # tastypie's save_m2m will raise an error. So we unprotect just for this saving.
                unprotect_models()
                # Here we hack self.obj_update because it bugs : add request object to data and makes self.obj_get bug.
                #updated_bundle = self.obj_update(bundle, request=request, **self.remove_api_resource_names(kwargs))
                try:
                    bundle.obj = self.obj_get(bundle=bundle, **self.remove_api_resource_names(kwargs))
                except ObjectDoesNotExist:
                    return http.HttpNotFound("A model instance matching the provided arguments could not be found.")
                bundle = self.full_hydrate(bundle)
                updated_bundle = self.save(bundle)
                # Save is done, we can reactivate guardian rights
                protect_models()
            else:
                raise BadRequest("User has no right to change the project.")
            
            if not self._meta.always_return_data:
                return http.HttpAccepted()
            else:
                updated_bundle = self.full_dehydrate(updated_bundle)
                updated_bundle = self.alter_detail_data_to_serialize(request, updated_bundle)
                return self.create_response(request, updated_bundle, response_class=http.HttpAccepted)
        except Exception as e:
            return http.HttpBadRequest("Datas are not correct.\nError = " + str(e) + "\n")
        return http.HttpResponse("Done")
    
    