import pytest
from werkzeug.security import check_password_hash

from flaskbb.auth.services import password
from flaskbb.core.exceptions import StopValidation, ValidationError
from flaskbb.core.tokens import Token, TokenActions, TokenError
from flaskbb.user.models import User

pytestmark = pytest.mark.usefixtures('default_settings')


class TestPasswordReset(object):

    def test_raises_token_error_if_not_a_password_reset(
            self, token_serializer
    ):
        service = password.ResetPasswordService(token_serializer, User, [])
        raw_token = token_serializer.dumps(
            Token(user_id=1, operation=TokenActions.ACTIVATE_ACCOUNT)
        )

        with pytest.raises(TokenError) as excinfo:
            service.reset_password(
                raw_token, "some@e.mail", "a great password!"
            )

        assert "invalid" in str(excinfo.value)

    def test_raises_StopValidation_if_verifiers_fail(self, token_serializer):
        token = token_serializer.dumps(
            Token(user_id=1, operation=TokenActions.RESET_PASSWORD)
        )

        def verifier(*a, **k):
            raise ValidationError('attr', 'no')

        service = password.ResetPasswordService(
            token_serializer, User, [verifier]
        )

        with pytest.raises(StopValidation) as excinfo:
            service.reset_password(token, "an@e.mail", "great password!")
        assert ("attr", "no") in excinfo.value.reasons

    def test_sets_user_password_to_provided_if_verifiers_pass(
            self, token_serializer, Fred
    ):
        token = token_serializer.dumps(
            Token(user_id=Fred.id, operation=TokenActions.RESET_PASSWORD)
        )

        service = password.ResetPasswordService(token_serializer, User, [])

        service.reset_password(token, Fred.email, "newpasswordwhodis")
        assert check_password_hash(Fred.password, "newpasswordwhodis")

    # need fred to initiate Users
    def test_initiate_raises_if_user_doesnt_exist(
            self, token_serializer, Fred
    ):
        service = password.ResetPasswordService(token_serializer, User, [])
        with pytest.raises(ValidationError) as excinfo:
            service.initiate_password_reset('lol@doesnt.exist')

        assert excinfo.value.attribute == 'email'
        assert excinfo.value.reason == 'Invalid email'

    def test_calls_send_reset_token_successfully_if_user_exists(
            self, Fred, mocker, token_serializer
    ):
        service = password.ResetPasswordService(token_serializer, User, [])
        mock = mocker.MagicMock()

        with mocker.patch(
                'flaskbb.auth.services.password.send_reset_token.delay', mock):
            service.initiate_password_reset(Fred.email)

        token = token_serializer.dumps(
            Token(user_id=Fred.id, operation=TokenActions.RESET_PASSWORD)
        )
        mock.assert_called_once_with(
            token=token, username=Fred.username, email=Fred.email
        )