from unittest.mock import patch
import pytest
from szurubooru import api, db, model, errors
from szurubooru.func import users


@pytest.fixture(autouse=True)
def inject_config(config_injector):
    config_injector({
        'privileges': {
            'users:edit:self:name': model.User.RANK_REGULAR,
            'users:edit:self:pass': model.User.RANK_REGULAR,
            'users:edit:self:email': model.User.RANK_REGULAR,
            'users:edit:self:rank': model.User.RANK_MODERATOR,
            'users:edit:self:avatar': model.User.RANK_MODERATOR,
            'users:edit:any:name': model.User.RANK_MODERATOR,
            'users:edit:any:pass': model.User.RANK_MODERATOR,
            'users:edit:any:email': model.User.RANK_MODERATOR,
            'users:edit:any:rank': model.User.RANK_ADMINISTRATOR,
            'users:edit:any:avatar': model.User.RANK_ADMINISTRATOR,
        },
    })


def test_updating_user(context_factory, user_factory):
    user = user_factory(name='u1', rank=model.User.RANK_ADMINISTRATOR)
    auth_user = user_factory(rank=model.User.RANK_ADMINISTRATOR)
    db.session.add(user)
    db.session.flush()

    with patch('szurubooru.func.users.create_user'), \
            patch('szurubooru.func.users.update_user_name'), \
            patch('szurubooru.func.users.update_user_password'), \
            patch('szurubooru.func.users.update_user_email'), \
            patch('szurubooru.func.users.update_user_rank'), \
            patch('szurubooru.func.users.update_user_avatar'), \
            patch('szurubooru.func.users.serialize_user'):
        users.serialize_user.return_value = 'serialized user'

        result = api.user_api.update_user(
            context_factory(
                params={
                    'version': 1,
                    'name': 'chewie',
                    'email': 'asd@asd.asd',
                    'password': 'oks',
                    'rank': 'moderator',
                    'avatarStyle': 'manual',
                },
                files={
                    'avatar': b'...',
                },
                user=auth_user),
            {'user_name': 'u1'})

        assert result == 'serialized user'
        users.create_user.assert_not_called()
        users.update_user_name.assert_called_once_with(user, 'chewie')
        users.update_user_password.assert_called_once_with(user, 'oks')
        users.update_user_email.assert_called_once_with(user, 'asd@asd.asd')
        users.update_user_rank.assert_called_once_with(
            user, 'moderator', auth_user)
        users.update_user_avatar.assert_called_once_with(
            user, 'manual', b'...')
        users.serialize_user.assert_called_once_with(
            user, auth_user, options=[])


@pytest.mark.parametrize(
    'field', ['name', 'email', 'password', 'rank', 'avatarStyle'])
def test_omitting_optional_field(user_factory, context_factory, field):
    user = user_factory(name='u1', rank=model.User.RANK_ADMINISTRATOR)
    db.session.add(user)
    db.session.flush()
    params = {
        'name': 'chewie',
        'email': 'asd@asd.asd',
        'password': 'oks',
        'rank': 'moderator',
        'avatarStyle': 'gravatar',
    }
    del params[field]
    with patch('szurubooru.func.users.create_user'), \
            patch('szurubooru.func.users.update_user_name'), \
            patch('szurubooru.func.users.update_user_password'), \
            patch('szurubooru.func.users.update_user_email'), \
            patch('szurubooru.func.users.update_user_rank'), \
            patch('szurubooru.func.users.update_user_avatar'), \
            patch('szurubooru.func.users.serialize_user'):
        api.user_api.update_user(
            context_factory(
                params={**params, **{'version': 1}},
                files={'avatar': b'...'},
                user=user),
            {'user_name': 'u1'})


def test_trying_to_update_non_existing(user_factory, context_factory):
    user = user_factory(name='u1', rank=model.User.RANK_ADMINISTRATOR)
    db.session.add(user)
    db.session.flush()
    with pytest.raises(users.UserNotFoundError):
        api.user_api.update_user(
            context_factory(user=user), {'user_name': 'u2'})


@pytest.mark.parametrize('params', [
    {'name': 'whatever'},
    {'email': 'whatever'},
    {'rank': 'whatever'},
    {'password': 'whatever'},
    {'avatarStyle': 'whatever'},
])
def test_trying_to_update_field_without_privileges(
        user_factory, context_factory, params):
    user1 = user_factory(name='u1', rank=model.User.RANK_REGULAR)
    user2 = user_factory(name='u2', rank=model.User.RANK_REGULAR)
    db.session.add_all([user1, user2])
    db.session.flush()
    with pytest.raises(errors.AuthError):
        api.user_api.update_user(
            context_factory(
                params={**params, **{'version': 1}},
                user=user1),
            {'user_name': user2.name})
