# -*- coding: utf-8; -*-

from unittest.mock import MagicMock, patch

from pyramid.httpexceptions import HTTPFound, HTTPForbidden

from wuttaweb.views import auth as mod
from tests.util import WebTestCase


class TestAuthView(WebTestCase):

    def setUp(self):
        self.setup_web()
        self.pyramid_config.include('wuttaweb.views.common')

    def make_view(self):
        return mod.AuthView(self.request)

    def test_includeme(self):
        self.pyramid_config.include('wuttaweb.views.auth')

    def test_login(self):
        model = self.app.model
        auth = self.app.get_auth_handler()
        view = self.make_view()

        # until user exists, will redirect
        self.assertEqual(self.session.query(model.User).count(), 0)
        response = view.login(session=self.session)
        self.assertEqual(response.status_code, 302)

        # make a user
        barney = model.User(username='barney')
        auth.set_user_password(barney, 'testpass')
        self.session.add(barney)
        self.session.commit()

        # now since user exists, form will display
        context = view.login(session=self.session)
        self.assertIn('form', context)

        # redirect if user already logged in
        with patch.object(self.request, 'user', new=barney):
            view = self.make_view()
            response = view.login(session=self.session)
        self.assertEqual(response.status_code, 302)

        # login fails w/ wrong password
        self.request.method = 'POST'
        self.request.POST = {'username': 'barney', 'password': 'WRONG'}
        view = self.make_view()
        context = view.login(session=self.session)
        self.assertIn('form', context)

        # redirect if login succeeds
        self.request.method = 'POST'
        self.request.POST = {'username': 'barney', 'password': 'testpass'}
        view = self.make_view()
        response = view.login(session=self.session)
        self.assertEqual(response.status_code, 302)

    def test_logout(self):
        self.pyramid_config.add_route('login', '/login')
        view = self.make_view()
        self.request.session.delete = MagicMock()
        response = view.logout()
        self.request.session.delete.assert_called_once_with()
        self.assertEqual(response.status_code, 302)

    def test_change_password(self):
        model = self.app.model
        auth = self.app.get_auth_handler()
        barney = model.User(username='barney')
        self.session.add(barney)
        self.session.commit()
        view = self.make_view()

        # unauthenticated user is redirected
        redirect = view.change_password()
        self.assertIsInstance(redirect, HTTPFound)

        # set initial password
        auth.set_user_password(barney, 'foo')
        self.session.commit()

        # forbidden if prevent_edit is set for user
        self.request.user = barney
        barney.prevent_edit = True
        self.assertRaises(HTTPForbidden, view.change_password)

        # okay let's test with edit allowed
        barney.prevent_edit = False

        # view should now return context w/ form
        context = view.change_password()
        self.assertIn('form', context)

        # submit valid form, ensure password is changed
        # (nb. this also would redirect user to home page)
        self.request.method = 'POST'
        self.request.POST = {
            'current_password': 'foo',
            # nb. new_password requires colander mapping structure
            '__start__': 'new_password:mapping',
            'new_password': 'bar',
            'new_password-confirm': 'bar',
            '__end__': 'new_password:mapping',
        }
        redirect = view.change_password()
        self.assertIsInstance(redirect, HTTPFound)
        self.session.commit()
        self.assertFalse(auth.check_user_password(barney, 'foo'))
        self.assertTrue(auth.check_user_password(barney, 'bar'))

        # at this point 'foo' is the password, now let's submit some
        # invalid forms and make sure we get back a context w/ form

        # first try empty data
        self.request.POST = {}
        context = view.change_password()
        self.assertIn('form', context)
        dform = context['form'].get_deform()
        self.assertEqual(dform['current_password'].errormsg, "Required")
        self.assertEqual(dform['new_password'].errormsg, "Required")

        # now try bad current password
        self.request.POST = {
            'current_password': 'blahblah',
            '__start__': 'new_password:mapping',
            'new_password': 'baz',
            'new_password-confirm': 'baz',
            '__end__': 'new_password:mapping',
        }
        context = view.change_password()
        self.assertIn('form', context)
        dform = context['form'].get_deform()
        self.assertEqual(dform['current_password'].errormsg, "Current password is incorrect.")

        # now try bad new password
        self.request.POST = {
            'current_password': 'bar',
            '__start__': 'new_password:mapping',
            'new_password': 'bar',
            'new_password-confirm': 'bar',
            '__end__': 'new_password:mapping',
        }
        context = view.change_password()
        self.assertIn('form', context)
        dform = context['form'].get_deform()
        self.assertEqual(dform['new_password'].errormsg, "New password must be different from old password.")

    def test_become_root(self):
        view = mod.AuthView(self.request)

        # GET not allowed
        self.request.method = 'GET'
        self.assertRaises(HTTPForbidden, view.become_root)

        # non-admin users also not allowed
        self.request.method = 'POST'
        self.request.is_admin = False
        self.assertRaises(HTTPForbidden, view.become_root)

        # but admin users can become root
        self.request.is_admin = True
        self.assertNotIn('is_root', self.request.session)
        redirect = view.become_root()
        self.assertIsInstance(redirect, HTTPFound)
        self.assertTrue(self.request.session['is_root'])

    def test_stop_root(self):
        view = mod.AuthView(self.request)

        # GET not allowed
        self.request.method = 'GET'
        self.assertRaises(HTTPForbidden, view.stop_root)

        # non-admin users also not allowed
        self.request.method = 'POST'
        self.request.is_admin = False
        self.assertRaises(HTTPForbidden, view.stop_root)

        # but admin users can stop being root
        # (nb. there is no check whether user is currently root)
        self.request.is_admin = True
        self.assertNotIn('is_root', self.request.session)
        redirect = view.stop_root()
        self.assertIsInstance(redirect, HTTPFound)
        self.assertFalse(self.request.session['is_root'])
