from __future__ import unicode_literals

from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpResponseRedirect
from django.utils import six
from djblets.webapi.decorators import (webapi_login_required,
                                       webapi_response_errors,
                                       webapi_request_fields)
from djblets.webapi.errors import (DOES_NOT_EXIST, NOT_LOGGED_IN,
                                   PERMISSION_DENIED)

from reviewboard.accounts.models import Profile
from reviewboard.webapi.base import WebAPIResource
from reviewboard.webapi.decorators import (webapi_check_local_site,
                                           webapi_check_login_required)
from reviewboard.webapi.resources import resources


class BaseWatchedObjectResource(WebAPIResource):
    """A base resource for objects watched by a user."""
    watched_resource = None
    uri_object_key = 'watched_obj_id'
    profile_field = None
    star_function = None
    unstar_function = None

    allowed_methods = ('GET', 'POST', 'DELETE')

    @property
    def uri_object_key_regex(self):
        return self.watched_resource.uri_object_key_regex

    def get_queryset(self, request, username, local_site_name=None,
                     *args, **kwargs):
        try:
            local_site = self._get_local_site(local_site_name)
            if local_site:
                user = local_site.users.get(username=username)
                profile = user.get_profile()
            else:
                profile = Profile.objects.get(user__username=username)

            q = self.watched_resource.get_queryset(
                request, local_site_name=local_site_name, *args, **kwargs)
            q = q.filter(starred_by=profile)
            return q
        except Profile.DoesNotExist:
            return self.watched_resource.model.objects.none()

    @webapi_check_login_required
    def get(self, request, watched_obj_id, *args, **kwargs):
        try:
            q = self.get_queryset(request, *args, **kwargs)
            obj = self.get_watched_object(q, watched_obj_id, *args, **kwargs)
        except ObjectDoesNotExist:
            return DOES_NOT_EXIST

        return HttpResponseRedirect(
            self.watched_resource.get_href(obj, request, *args, **kwargs))

    @webapi_check_login_required
    @webapi_response_errors(DOES_NOT_EXIST)
    def get_list(self, request, *args, **kwargs):
        # TODO: Handle pagination and ?counts-only=1
        try:
            objects = [
                self.serialize_object(obj)
                for obj in self.get_queryset(request, is_list=True,
                                             *args, **kwargs)
            ]

            return 200, {
                self.list_result_key: objects,
            }
        except User.DoesNotExist:
            return DOES_NOT_EXIST

    @webapi_check_local_site
    @webapi_login_required
    @webapi_response_errors(DOES_NOT_EXIST, NOT_LOGGED_IN, PERMISSION_DENIED)
    @webapi_request_fields(required={
        'object_id': {
            'type': six.text_type,
            'description': 'The ID of the object to watch.',
        },
    })
    def create(self, request, object_id, *args, **kwargs):
        try:
            obj_kwargs = kwargs.copy()
            obj_kwargs[self.watched_resource.uri_object_key] = object_id
            obj = self.watched_resource.get_object(request, *args,
                                                   **obj_kwargs)
            user = resources.user.get_object(request, *args, **kwargs)
        except ObjectDoesNotExist:
            return DOES_NOT_EXIST

        if not resources.user.has_modify_permissions(request, user,
                                                     *args, **kwargs):
            return self._no_access_error(request.user)

        profile, profile_is_new = \
            Profile.objects.get_or_create(user=request.user)
        star = getattr(profile, self.star_function)
        star(obj)

        return 201, {
            self.item_result_key: obj,
        }

    @webapi_check_local_site
    @webapi_login_required
    def delete(self, request, watched_obj_id, *args, **kwargs):
        try:
            obj_kwargs = kwargs.copy()
            obj_kwargs[self.watched_resource.uri_object_key] = watched_obj_id
            obj = self.watched_resource.get_object(request, *args,
                                                   **obj_kwargs)
            user = resources.user.get_object(request, *args, **kwargs)
        except ObjectDoesNotExist:
            return DOES_NOT_EXIST

        if not resources.user.has_modify_permissions(request, user,
                                                     *args, **kwargs):
            return self._no_access_error(request.user)

        profile, profile_is_new = \
            Profile.objects.get_or_create(user=request.user)

        if not profile_is_new:
            unstar = getattr(profile, self.unstar_function)
            unstar(obj)

        return 204, {}

    def serialize_object(self, obj, *args, **kwargs):
        return {
            'id': obj.pk,
            self.item_result_key: obj,
        }

    def get_watched_object(self, queryset, obj_id, *args, **kwargs):
        return queryset.get(pk=obj_id)
