from unittest.mock import Mock

from django import forms
from django.contrib.admin import AdminSite
from django.contrib.auth import get_user_model
from django.test import TestCase
from django.test.utils import override_settings
from django.urls import reverse
from django.utils.html import format_html
from django.utils.translation import gettext as _

from managemails.admin import (
    PASSWORD_MISMATCH_ERROR,
    ActivationChangeMixin,
    MailboxAdmin,
    MailboxChangeForm,
    MailboxCreationForm,
    ReadOnlyPasswordHashField,
    ReadOnlyPasswordHashWidget,
)
from managemails.models import Mailbox
from osusers.models import User

Customer = get_user_model()


class ReadOnlyPasswordHashWidgetTest(TestCase):
    def test_render(self):
        widget = ReadOnlyPasswordHashWidget()
        rendered = widget.render("password", "secret", {"class": "test"})
        self.assertEqual(
            rendered,
            format_html(
                '<div class="test">{0}</div>',
                format_html("<strong>{0}</strong>: secret ", _("Hash")),
            ),
        )


class ReadOnlyPasswordHashFieldTest(TestCase):
    def test___init__(self):
        field = ReadOnlyPasswordHashField()
        self.assertFalse(field.required)

    def test_bound_data(self):
        field = ReadOnlyPasswordHashField()
        self.assertEqual(field.bound_data("new", "old"), "old")

    def test__has_changed(self):
        field = ReadOnlyPasswordHashField()
        self.assertFalse(field.has_changed("new", "old"))


class CustomerTestCase(TestCase):
    def setUp(self):
        super(CustomerTestCase, self).setUp()
        self.customer = Customer.objects.create(username="test")


class MailboxCreationFormTest(CustomerTestCase):
    def test_clean_password2_same(self):
        form = MailboxCreationForm()
        form.cleaned_data = {"password1": "secret", "password2": "secret"}
        self.assertEqual(form.clean_password2(), "secret")

    def test_clean_password2_empty(self):
        form = MailboxCreationForm()
        form.cleaned_data = {}
        self.assertIsNone(form.clean_password2())

    def test_clean_password2_mismatch(self):
        form = MailboxCreationForm()
        form.cleaned_data = {"password1": "secretx", "password2": "secrety"}
        with self.assertRaises(forms.ValidationError) as cm:
            form.clean_password2()
        self.assertEqual(cm.exception.message, PASSWORD_MISMATCH_ERROR)

    @override_settings(
        CELERY_ALWAYS_EAGER=True, CELERY_CACHE_BACKEND="memory", BROKER_BACKEND="memory"
    )
    def test_save_commit(self):
        user = User.objects.create_user(customer=self.customer)
        form = MailboxCreationForm(
            data={"osuser": user.uid, "password1": "secret", "password2": "secret"}
        )
        mailbox = form.save()
        self.assertIsNotNone(mailbox)
        self.assertEqual(len(Mailbox.objects.filter(osuser=user)), 1)

    @override_settings(
        CELERY_ALWAYS_EAGER=True, CELERY_CACHE_BACKEND="memory", BROKER_BACKEND="memory"
    )
    def test_save_no_commit(self):
        user = User.objects.create_user(customer=self.customer)
        form = MailboxCreationForm(
            data={"osuser": user.uid, "password1": "secret", "password2": "secret"}
        )
        mailbox = form.save(commit=False)
        self.assertIsNotNone(mailbox)
        self.assertEqual(len(Mailbox.objects.filter(osuser=user)), 0)


class MailboxChangeFormTest(CustomerTestCase):
    @override_settings(
        CELERY_ALWAYS_EAGER=True, CELERY_CACHE_BACKEND="memory", BROKER_BACKEND="memory"
    )
    def test_clean_password(self):
        mailbox = Mailbox(
            username="test", osuser=User.objects.create_user(customer=self.customer)
        )
        mailbox.set_password("test")
        mailbox.save()
        form = MailboxChangeForm(instance=mailbox, data={"password": "blub"})
        self.assertEqual(form.clean_password(), mailbox.password)


class ActivationChangeMixinTest(TestCase):
    def test_activate(self):
        querysetmock = Mock()
        activationchange = ActivationChangeMixin()
        activationchange.activate(Mock(), querysetmock)
        querysetmock.update.assert_called_with(active=True)

    def test_deactivate(self):
        querysetmock = Mock()
        activationchange = ActivationChangeMixin()
        activationchange.deactivate(Mock(), querysetmock)
        querysetmock.update.assert_called_with(active=False)


class MailBoxAdminTest(CustomerTestCase):
    def setUp(self):
        super(MailBoxAdminTest, self).setUp()
        site = AdminSite()
        self.mbadmin = MailboxAdmin(Mailbox, site)

    def test_get_fieldsets_without_object(self):
        self.assertEqual(self.mbadmin.get_fieldsets(Mock()), self.mbadmin.add_fieldsets)

    @override_settings(
        CELERY_ALWAYS_EAGER=True, CELERY_CACHE_BACKEND="memory", BROKER_BACKEND="memory"
    )
    def test_get_fieldsets_with_object(self):
        mailbox = Mailbox(
            username="test", osuser=User.objects.create_user(customer=self.customer)
        )
        mailbox.set_password("test")
        mailbox.save()
        self.assertEqual(
            self.mbadmin.get_fieldsets(Mock(), mailbox), self.mbadmin.fieldsets
        )

    def test_get_form_without_object(self):
        form = self.mbadmin.get_form(Mock)
        self.assertEqual(form.Meta.fields, ["osuser", "password1", "password2"])

    @override_settings(
        CELERY_ALWAYS_EAGER=True, CELERY_CACHE_BACKEND="memory", BROKER_BACKEND="memory"
    )
    def test_get_form_with_object(self):
        mailbox = Mailbox(
            username="test", osuser=User.objects.create_user(customer=self.customer)
        )
        mailbox.set_password("test")
        mailbox.save()
        form = self.mbadmin.get_form(Mock, mailbox)
        self.assertEqual(form.Meta.fields, ["osuser", "username", "password", "active"])

    def test_admin_for_mailbox(self):
        admin_url = reverse("admin:managemails_mailaddress_changelist")
        self.assertIsNotNone(admin_url)


class MailAddressAdminTest(TestCase):
    def test_admin_for_mailaddress(self):
        admin_url = reverse("admin:managemails_mailaddress_changelist")
        self.assertIsNotNone(admin_url)
