from __future__ import unicode_literals

import logging

from django.contrib.auth.models import Permission, User
from django.utils import six
from djblets.avatars.services.base import AvatarService
from djblets.avatars.services.gravatar import GravatarService
from djblets.testing.decorators import add_fixtures
from djblets.webapi.errors import DOES_NOT_EXIST
from djblets.webapi.testing.decorators import webapi_test_template
from kgb import SpyAgency

from reviewboard.accounts.backends.base import BaseAuthBackend
from reviewboard.accounts.backends.registry import get_enabled_auth_backends
from reviewboard.avatars import avatar_services
from reviewboard.avatars.testcase import AvatarServicesTestMixin
from reviewboard.site.models import LocalSite
from reviewboard.webapi.resources import resources
from reviewboard.webapi.tests.base import BaseWebAPITestCase
from reviewboard.webapi.tests.mimetypes import (user_item_mimetype,
                                                user_list_mimetype)
from reviewboard.webapi.tests.mixins import BasicTestsMetaclass
from reviewboard.webapi.tests.urls import (get_user_item_url,
                                           get_user_list_url)


class NoProfileAuthBackend(BaseAuthBackend):
    backend_id = 'test-no-profile'
    supports_change_name = False
    supports_change_email = False


class BrokenUpdateProfileAuthBackend(BaseAuthBackend):
    backend_id = 'test-broken-profile'
    supports_change_name = True
    supports_change_email = True

    def update_name(self, user):
        raise Exception('oh no')

    def update_email(self, user):
        raise Exception('oh no')


class NoURLAvatarService(AvatarService):
    """An avatar services that returns no URLs."""

    avatar_service_id = 'no-urls'
    name = 'No URLs For You'

    def get_avatar_urls_uncached(self, user, size):
        """Return no URLs."""
        return {}


class SimpleRenderAvatarService(NoURLAvatarService):
    """An avatar services that has simple, testable rendered output."""

    avatar_service_id = 'simple-renders'
    name = 'Simple Renders'

    def render(self, request, user, size, **kwargs):
        return '<div class="avatar" data-size="%s">%s</div>' % (size,
                                                                user.username)


@six.add_metaclass(BasicTestsMetaclass)
class ResourceListTests(AvatarServicesTestMixin, SpyAgency,
                        BaseWebAPITestCase):
    """Testing the UserResource list API tests."""

    fixtures = ['test_users']
    sample_api_url = 'users/'
    resource = resources.user

    test_http_methods = ('GET',)

    def setup_http_not_allowed_list_test(self, user):
        return get_user_list_url()

    def compare_item(self, item_rsp, obj):
        self.assertEqual(item_rsp['id'], obj.pk)
        self.assertEqual(item_rsp['username'], obj.username)

    #
    # HTTP GET tests
    #

    def setup_basic_get_test(self, user, with_local_site, local_site_name,
                             populate_items):
        if not populate_items:
            items = []
        elif with_local_site:
            local_site = self.get_local_site(name=local_site_name)
            items = list(local_site.users.all())
        else:
            items = list(User.objects.all())

        return (get_user_list_url(local_site_name),
                user_list_mimetype,
                items)

    @webapi_test_template
    def test_get_filter_inactive(self):
        """Testing the GET <URL> API filters out inactive users by default"""
        dopey = User.objects.get(username='dopey')
        dopey.is_active = False
        dopey.save()

        rsp = self.api_get(get_user_list_url(),
                           expected_mimetype=user_list_mimetype)

        self.assertEqual(rsp['stat'], 'ok')
        user_pks = [user['id'] for user in rsp['users']]
        returned_users = set(User.objects.filter(pk__in=user_pks))
        expected_users = set(User.objects.filter(is_active=True))
        self.assertEqual(returned_users, expected_users)

    @webapi_test_template
    def test_get_include_inactive(self):
        """Testing the GET <URL>/?include-inactive=1 API includes inactive
        users
        """
        dopey = User.objects.get(username='dopey')
        dopey.is_active = False
        dopey.save()

        rsp = self.api_get(get_user_list_url(), {'include-inactive': '1'},
                           expected_mimetype=user_list_mimetype)

        self.assertEqual(rsp['stat'], 'ok')
        user_pks = [user['id'] for user in rsp['users']]
        self.assertEqual(set(User.objects.filter(pk__in=user_pks)),
                         set(User.objects.all()))

    @webapi_test_template
    def test_get_include_inactive_true(self):
        """Testing the GET <URL>/?include-inactive=true API includes inactive
        users
        """
        dopey = User.objects.get(username='dopey')
        dopey.is_active = False
        dopey.save()

        rsp = self.api_get(get_user_list_url(), {'include-inactive': 'true'},
                           expected_mimetype=user_list_mimetype)

        self.assertEqual(rsp['stat'], 'ok')
        user_pks = [user['id'] for user in rsp['users']]
        self.assertEqual(set(User.objects.filter(pk__in=user_pks)),
                         set(User.objects.all()))

    def test_get_with_q(self):
        """Testing the GET users/?q= API"""
        rsp = self.api_get(get_user_list_url(), {'q': 'gru'},
                           expected_mimetype=user_list_mimetype)
        self.assertEqual(rsp['stat'], 'ok')
        self.assertEqual(len(rsp['users']), 1)  # grumpy

    @webapi_test_template
    def test_get_with_render_avatars_at(self):
        """Testing the GET <URL> API with ?render-avatars-at=..."""
        avatar_services.register(SimpleRenderAvatarService)
        avatar_services.enable_service(SimpleRenderAvatarService, save=False)
        avatar_services.set_default_service(SimpleRenderAvatarService)

        rsp = self.api_get(
            get_user_list_url(),
            {
                'render-avatars-at': '24,abc,48,,128',
            },
            expected_mimetype=user_list_mimetype)

        self.assertEqual(rsp['stat'], 'ok')
        self.assertEqual(len(rsp['users']), 4)
        self.assertEqual(
            rsp['users'][0]['avatar_html'],
            {
                '24': '<div class="avatar" data-size="24">admin</div>',
                '48': '<div class="avatar" data-size="48">admin</div>',
                '128': '<div class="avatar" data-size="128">admin</div>',
            })

        self.assertEqual(
            rsp['users'][1]['avatar_html'],
            {
                '24': '<div class="avatar" data-size="24">doc</div>',
                '48': '<div class="avatar" data-size="48">doc</div>',
                '128': '<div class="avatar" data-size="128">doc</div>',
            })

        self.assertEqual(
            rsp['users'][2]['avatar_html'],
            {
                '24': '<div class="avatar" data-size="24">dopey</div>',
                '48': '<div class="avatar" data-size="48">dopey</div>',
                '128': '<div class="avatar" data-size="128">dopey</div>',
            })

        self.assertEqual(
            rsp['users'][3]['avatar_html'],
            {
                '24': '<div class="avatar" data-size="24">grumpy</div>',
                '48': '<div class="avatar" data-size="48">grumpy</div>',
                '128': '<div class="avatar" data-size="128">grumpy</div>',
            })

    def test_populate_users_auth_backend(self):
        """Testing the GET users/?q= API with BaseAuthBackend.populate_users
        failure
        """
        class SandboxAuthBackend(BaseAuthBackend):