"""
This module provides tests for :py:mod:`userdbs.views`.

"""
from unittest.mock import MagicMock, patch

from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse

from hostingpackages.models import (
    CustomerHostingPackage,
    CustomerUserDatabaseOption,
    HostingPackageTemplate,
    UserDatabaseOption,
)
from userdbs.models import DB_TYPES, UserDatabase
from userdbs.views import AddUserDatabase, ChangeDatabaseUserPassword

User = get_user_model()

TEST_USER = "test"
TEST_PASSWORD = "secret"
TEST_EMAIL = "test@example.org"


class HostingPackageAwareTestMixin(object):
    # noinspection PyMethodMayBeStatic
    def _setup_hosting_package(self, customer):
        template = HostingPackageTemplate.objects.create(
            name="testpackagetemplate", mailboxcount=10, diskspace=1, diskspace_unit=0
        )
        package = CustomerHostingPackage.objects.create_from_template(
            customer, template, "testpackage"
        )
        with patch("hostingpackages.models.settings") as hmsettings:
            hmsettings.OSUSER_DEFAULT_GROUPS = []
            package.save()
        return package


class CustomerUserDatabaseOptionAwareTestMixin(object):
    def __init__(self, *args, **kwargs):
        super(CustomerUserDatabaseOptionAwareTestMixin, self).__init__(*args, **kwargs)
        self._templates = {}

    def _setup_userdatabaseoption(self, number, dbtype):
        key = "{}_{}".format(dbtype, number)
        if key not in self._templates:
            self._templates[key] = UserDatabaseOption.objects.create(
                number=number, db_type=dbtype
            )
        return self._templates[key]

    def _create_userdatabase_option(self, number=1, dbtype=DB_TYPES.pgsql):
        # noinspection PyUnresolvedReferences
        return CustomerUserDatabaseOption.objects.create(
            template=self._setup_userdatabaseoption(number, dbtype),
            number=number,
            db_type=dbtype,
            hosting_package=self.package,
        )


class AddUserDatabaseTest(
    HostingPackageAwareTestMixin, CustomerUserDatabaseOptionAwareTestMixin, TestCase
):
    def setUp(self):
        self.customer = User.objects.create_user(
            username=TEST_USER, password=TEST_PASSWORD
        )
        self.package = self._setup_hosting_package(self.customer)

    def _get_url(self):
        return reverse("add_userdatabase", kwargs={"package": self.package.id})

    def test_get_anonymous(self):
        response = self.client.get(self._get_url())
        self.assertEqual(response.status_code, 403)

    def test_get_regular_user_nodboption(self):
        self.client.login(username=TEST_USER, password=TEST_PASSWORD)
        response = self.client.get(self._get_url())
        self.assertEqual(response.status_code, 400)

    def test_get_regular_user(self):
        self._create_userdatabase_option()
        self.client.login(username=TEST_USER, password=TEST_PASSWORD)
        response = self.client.get(self._get_url())
        self.assertEqual(response.status_code, 200)

    def test_get_other_regular_user(self):
        User.objects.create_user("test2", password=TEST_PASSWORD)
        self.client.login(username="test2", password=TEST_PASSWORD)
        response = self.client.get(self._get_url())
        self.assertEqual(response.status_code, 403)

    def test_get_staff_user_nodboption(self):
        User.objects.create_superuser("admin", email=TEST_EMAIL, password=TEST_PASSWORD)
        self.client.login(username="admin", password=TEST_PASSWORD)
        response = self.client.get(self._get_url())
        self.assertEqual(response.status_code, 400)

    def test_get_staff_user(self):
        self._create_userdatabase_option()
        User.objects.create_superuser("admin", email=TEST_EMAIL, password=TEST_PASSWORD)
        self.client.login(username="admin", password=TEST_PASSWORD)
        response = self.client.get(self._get_url())
        self.assertEqual(response.status_code, 200)

    def test_get_regular_user_nofree_db(self):
        db_option = self._create_userdatabase_option()
        UserDatabase.objects.create_userdatabase_with_user(
            db_option.db_type, self.package.osuser
        )
        self.client.login(username=TEST_USER, password=TEST_PASSWORD)
        response = self.client.get(self._get_url())
        self.assertEqual(response.status_code, 400)

    def test_get_form_kwargs(self):
        db_option = self._create_userdatabase_option()
        self.client.login(username=TEST_USER, password=TEST_PASSWORD)
        view = AddUserDatabase(
            request=MagicMock(), kwargs={"package": str(self.package.pk)}
        )
        the_kwargs = view.get_form_kwargs()
        self.assertIn("hostingpackage", the_kwargs)
        self.assertEqual(the_kwargs["hostingpackage"], self.package)
        self.assertIn("dbtypes", the_kwargs)
        self.assertEqual(
            the_kwargs["dbtypes"], [(db_option.db_type, DB_TYPES[db_option.db_type])]
        )

    def test_get_template(self):
        self._create_userdatabase_option()
        self.client.login(username=TEST_USER, password=TEST_PASSWORD)
        response = self.client.get(self._get_url())
        self.assertTemplateUsed(response, "userdbs/userdatabase_create.html")

    def test_form_valid_redirect(self):
        db_option = self._create_userdatabase_option()
        self.client.login(username=TEST_USER, password=TEST_PASSWORD)
        response = self.client.post(
            self._get_url(),
            data={
                "db_type": db_option.db_type,
                "password1": TEST_PASSWORD,
                "password2": TEST_PASSWORD,
            },
        )
        self.assertRedirects(response, self.package.get_absolute_url())

    def test_form_valid_message(self):
        db_option = self._create_userdatabase_option()
        self.client.login(username=TEST_USER, password=TEST_PASSWORD)
        response = self.client.post(
            self._get_url(),
            follow=True,
            data={
                "db_type": db_option.db_type,
                "password1": TEST_PASSWORD,
                "password2": TEST_PASSWORD,
            },
        )
        db = UserDatabase.objects.filter(db_user__osuser=self.package.osuser).get()
        messages = list(response.context["messages"])
        self.assertEqual(len(messages), 1)
        self.assertEqual(
            str(messages[0]),
            (
                "Successfully create new {type} database {dbname} for user " "{dbuser}."
            ).format(type=db.db_user.db_type, dbname=db.db_name, dbuser=db.db_user),
        )


class ChangeDatabaseUserPasswordTest(
    HostingPackageAwareTestMixin, CustomerUserDatabaseOptionAwareTestMixin, TestCase
):
    def setUp(self):
        self.customer = User.objects.create_user(
            username=TEST_USER, password=TEST_PASSWORD
        )
        self.package = self._setup_hosting_package(self.customer)
        template = self._create_userdatabase_option()
        database = UserDatabase.objects.create_userdatabase_with_user(
            template.db_type, self.package.osuser
        )
        self.dbuser = database.db_user

    def _get_url(self, dbuser):
        return reverse(
            "change_dbuser_password",
            kwargs={"package": self.package.id, "slug": dbuser.name},
        )

    def test_get_anonymous(self):
        response = self.client.get(self._get_url(self.dbuser))
        self.assertEqual(response.status_code, 403)

    def test_get_regular_user(self):
        self.client.login(username=TEST_USER, password=TEST_PASSWORD)
        response = self.client.get(self._get_url(self.dbuser))
        self.assertEqual(response.status_code, 200)

    def test_get_other_regular_user(self):
        User.objects.create_user("test2", password=TEST_PASSWORD)
        self.client.login(username="test2", password=TEST_PASSWORD)
        response = self.client.get(self._get_url(self.dbuser))
        self.assertEqual(response.status_code, 403)

    def test_get_staff_user(self):
        User.objects.create_superuser("admin", email=TEST_EMAIL, password=TEST_PASSWORD)
        self.client.login(username="admin", password=TEST_PASSWORD)
        response = self.client.get(self._get_url(self.dbuser))
        self.assertEqual(response.status_code, 200)

    def test_get_template(self):
        self.client.login(username=TEST_USER, password=TEST_PASSWORD)
        response = self.client.get(self._get_url(self.dbuser))
        self.assertTemplateUsed(response, "userdbs/databaseuser_setpassword.html")

    def test_get_form_kwargs(self):
        self.client.login(username=TEST_USER, password=TEST_PASSWORD)
        view = ChangeDatabaseUserPassword(
            request=MagicMock(),
            kwargs={"package": str(self.package.pk), "slug": self.dbuser.name},
        )
        the_kwargs = view.get_form_kwargs()
        self.assertIn("hostingpackage", the_kwargs)
        self.assertEqual(the_kwargs["hostingpackage"], self.package)

    def test_get_context_data(self):
        self.client.login(username=TEST_USER, password=TEST_PASSWORD)
        response = self.client.get(self._get_url(self.dbuser))
        self.assertIn("dbuser", response.context)
        self.assertEqual(response.context["dbuser"], self.dbuser)
        self.assertIn("hostingpackage", response.context)
        self.assertEqual(response.context["hostingpackage"], self.package)
        self.assertIn("customer", response.context)
        self.assertEqual(response.context["customer"], self.customer)

    def test_form_valid_redirect(self):
        self.client.login(username=TEST_USER, password=TEST_PASSWORD)
        response = self.client.post(
            self._get_url(self.dbuser),
            data={"password1": TEST_PASSWORD, "password2": TEST_PASSWORD},
        )
        self.assertRedirects(response, self.package.get_absolute_url())

    def test_form_valid_message(self):
        self.client.login(username=TEST_USER, password=TEST_PASSWORD)
        response = self.client.post(
            self._get_url(self.dbuser),
            follow=True,
            data={"password1": TEST_PASSWORD, "password2": TEST_PASSWORD},
        )
        messages = list(response.context["messages"])
        self.assertEqual(len(messages), 1)
        self.assertEqual(
            str(messages[0]),
            "Successfully changed password of database user {dbuser}.".format(
                dbuser=self.dbuser.name
            ),
        )


class DeleteUserDatabaseTest(
    HostingPackageAwareTestMixin, CustomerUserDatabaseOptionAwareTestMixin, TestCase
):
    def setUp(self):
        self.customer = User.objects.create_user(
            username=TEST_USER, password=TEST_PASSWORD
        )
        self.package = self._setup_hosting_package(self.customer)
        template = self._create_userdatabase_option()
        self.database = UserDatabase.objects.create_userdatabase_with_user(
            template.db_type, self.package.osuser
        )

    def _get_url(self, userdatabase):
        return reverse(
            "delete_userdatabase",
            kwargs={"package": self.package.id, "slug": userdatabase.db_name},
        )

    def test_get_anonymous(self):
        response = self.client.get(self._get_url(self.database))
        self.assertEqual(response.status_code, 403)

    def test_get_regular_user(self):
        self.client.login(username=TEST_USER, password=TEST_PASSWORD)
        response = self.client.get(self._get_url(self.database))
        self.assertEqual(response.status_code, 200)

    def test_get_other_regular_user(self):
        User.objects.create_user("test2", password=TEST_PASSWORD)
        self.client.login(username="test2", password=TEST_PASSWORD)
        response = self.client.get(self._get_url(self.database))
        self.assertEqual(response.status_code, 403)

    def test_get_staff_user(self):
        User.objects.create_superuser("admin", email=TEST_EMAIL, password=TEST_PASSWORD)
        self.client.login(username="admin", password=TEST_PASSWORD)
        response = self.client.get(self._get_url(self.database))
        self.assertEqual(response.status_code, 200)

    def test_get_template(self):
        self.client.login(username=TEST_USER, password=TEST_PASSWORD)
        response = self.client.get(self._get_url(self.database))
        self.assertTemplateUsed(response, "userdbs/userdatabase_confirm_delete.html")

    def test_get_context_data(self):
        self.client.login(username=TEST_USER, password=TEST_PASSWORD)
        response = self.client.get(self._get_url(self.database))
        self.assertIn("database", response.context)
        self.assertEqual(response.context["database"], self.database)
        self.assertIn("hostingpackage", response.context)
        self.assertEqual(response.context["hostingpackage"], self.package)
        self.assertIn("customer", response.context)
        self.assertEqual(response.context["customer"], self.customer)

    def test_form_valid_redirect(self):
        self.client.login(username=TEST_USER, password=TEST_PASSWORD)
        response = self.client.post(self._get_url(self.database))
        self.assertRedirects(response, self.package.get_absolute_url())

    def test_form_valid_message(self):
        self.client.login(username=TEST_USER, password=TEST_PASSWORD)
        response = self.client.post(self._get_url(self.database), follow=True)
        messages = list(response.context["messages"])
        self.assertEqual(len(messages), 1)
        self.assertEqual(str(messages[0]), "Database deleted.")
