"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: ksp-servicestelle@sgdnord.rlp.de
Created on: 12.09.23

"""
from django.core.exceptions import ObjectDoesNotExist
from django.test import RequestFactory
from django.urls import reverse
from django.utils.translation import gettext_lazy as _

from api.models import APIUserToken
from konova.tests.test_views import BaseTestCase
from user.forms.modals.team import NewTeamModalForm, EditTeamModalForm, RemoveTeamModalForm, LeaveTeamModalForm
from user.forms.user import UserNotificationForm, UserAPITokenForm
from user.models import Team, UserAction, UserNotification


class NewTeamModalFormTestCase(BaseTestCase):

    def setUp(self) -> None:
        super().setUp()
        self.request = RequestFactory().request()
        self.request.user = self.user

    def test_init(self):
        form = NewTeamModalForm(
            request=self.request
        )
        self.assertEqual(form.form_title, str(_("Create new team")))
        self.assertEqual(form.form_caption, str(_("You will become the administrator for this group by default. You do not need to add yourself to the list of members.")))
        self.assertEqual(form.action_url, reverse("user:team-new"))
        self.assertEqual(form.cancel_redirect, reverse("user:team-index"))
        self.assertEqual(form.request, self.request)
        self.assertEqual(form.user, self.request.user)

    def test_is_valid(self):
        invalid_data = {
            "name": self.team.name,
            "description": "Test description",
            "members": [self.superuser.id,],
        }
        form = NewTeamModalForm(
            invalid_data,
            request=self.request
        )
        self.assertFalse(form.is_valid())
        self.assertTrue(form.has_error("name"))

        valid_data = invalid_data
        valid_data["name"] = self.team.name + "_OTHER"

        form = NewTeamModalForm(
            invalid_data,
            request=self.request
        )
        self.assertTrue(form.is_valid())

    def test_save(self):
        valid_data = {
            "name": self.team.name + "_OTHER",
            "description": "Test description",
            "members": [self.superuser.id,],
        }
        form = NewTeamModalForm(
            valid_data,
            request=self.request
        )
        self.assertTrue(form.is_valid())
        obj = form.save()
        self.assertEqual(obj.name, valid_data["name"])
        self.assertEqual(obj.description, valid_data["description"])
        users = obj.users.all()
        admins = obj.admins.all()
        self.assertIn(self.request.user, users)
        self.assertIn(self.request.user, admins)
        self.assertIn(self.superuser, users)
        self.assertNotIn(self.superuser, admins)


class EditTeamModalFormTestCase(NewTeamModalFormTestCase):

    def test_init(self):
        self.team.admins.add(self.superuser)

        form = EditTeamModalForm(request=self.request, instance=self.team)
        self.assertEqual(form.form_title, str(_("Edit team")))
        self.assertEqual(form.action_url, reverse("user:team-edit", args=(self.team.id,)))
        self.assertEqual(form.cancel_redirect, reverse("user:team-index"))

        self.assertEqual(form.fields["name"].initial, self.team.name)
        self.assertEqual(form.fields["description"].initial, self.team.description)
        self.assertEqual(form.fields["members"].initial.count(), 1)
        self.assertIn(self.superuser, form.fields["members"].initial)
        self.assertEqual(form.fields["admins"].initial.count(), 1)
        self.assertIn(self.superuser, form.fields["admins"].initial)

    def test_is_valid(self):
        data = {
            "name": self.team.name,
            "description": self.team.description,
            "members": self.team.users.values_list("id", flat=True),
            "admins": self.team.admins.values_list("id", flat=True),
        }
        form = EditTeamModalForm(
            data,
            request=self.request,
            instance=self.team
        )

        # Error 1: Admin not in user list
        self.team.users.set([self.superuser])
        self.team.admins.set([self.user])
        self.assertFalse(form.is_valid())
        self.assertTrue(form.has_error("admins"))

        # Error 2: Admin list empty
        self.team.admins.set([])
        self.assertFalse(form.is_valid())
        self.assertTrue(form.has_error("admins"))

        # Error 3: Name taken
        other_team = Team.objects.create(
            name=self.team.name
        )
        self.team.admins.set([self.superuser])
        self.assertFalse(form.is_valid())
        self.assertTrue(form.has_error("name"))

    def test_save(self):
        data = {
            "name": self.team.name + "_EDITED",
            "description": self.team.description + "_EDITED",
            "members": [self.user.id, self.superuser.id,],
            "admins": [self.user.id,],
        }
        form = EditTeamModalForm(
            data,
            request=self.request,
            instance=self.team
        )
        self.assertTrue(form.is_valid(), msg=form.errors)
        obj = form.save()
        self.assertEqual(obj.name, data["name"])
        self.assertEqual(obj.description, data["description"])
        self.assertIn(self.user, obj.users.all())
        self.assertIn(self.superuser, obj.users.all())
        self.assertIn(self.user, obj.admins.all())
        self.assertEqual(obj.admins.count(), 1)
        self.assertEqual(obj.users.count(), 2)


class RemoveTeamModalFormTestCase(BaseTestCase):
    def setUp(self) -> None:
        super().setUp()
        self.request = RequestFactory().request()
        self.request.user = self.user

    def test_init(self):
        form = RemoveTeamModalForm(
            request=self.request,
            instance=self.team
        )
        self.assertEqual(form.form_caption, str(_("ATTENTION!\n\nRemoving the team means all members will lose their access to data, based on this team! \n\nAre you sure to remove this team?")))
        self.assertEqual(form.user, self.request.user)
        self.assertEqual(form.request, self.request)

    def test_save(self):
        data = {
            "confirm": True
        }
        form = RemoveTeamModalForm(
            data,
            request=self.request,
            instance=self.team
        )
        self.assertTrue(form.is_valid(), msg=form.errors)
        form.save()
        self.team.refresh_from_db()

        self.assertIsNotNone(self.team.deleted)
        self.assertEqual(self.team.deleted.user, self.request.user)
        self.assertEqual(self.team.deleted.action, UserAction.DELETED)


class LeaveTeamModalFormTestCase(BaseTestCase):
    def setUp(self) -> None:
        super().setUp()
        self.request = RequestFactory().request()
        self.request.user = self.user

    def test_init(self):
        form = LeaveTeamModalForm(
            request=self.request,
            instance=self.team
        )
        self.assertEqual(form.form_title, str(_("Leave team")))

    def test_save(self):
        self.team.users.add(self.user)
        data = {
            "confirm": True,
        }
        form = LeaveTeamModalForm(
            data,
            request=self.request,
            instance=self.team
        )
        self.assertTrue(form.is_valid(), msg=form.errors)
        self.assertIn(self.request.user, self.team.users.all())
        form.save()
        self.assertNotIn(self.request.user, self.team.users.all())


class UserNotificationFormTestCase(BaseTestCase):
    def setUp(self) -> None:
        super().setUp()

        if not UserNotification.objects.all().exists():
            self.notifications = UserNotification.objects.bulk_create(
                [
                    UserNotification(id="notification_1", name="notification_1", is_active=True),
                    UserNotification(id="notification_2", name="notification_2", is_active=True),
                    UserNotification(id="notification_3", name="notification_3", is_active=True),
                    UserNotification(id="notification_4", name="notification_4", is_active=True),
                ]
            )

    def test_init(self):
        form = UserNotificationForm(
            user=self.user
        )
        self.assertEqual(form.form_title, str(_("Edit notifications")))
        self.assertEqual(form.form_caption, "")
        self.assertEqual(form.action_url, reverse("user:notifications"))
        self.assertEqual(form.cancel_redirect, reverse("user:index"))

    def test_save(self):
        selected_notification = UserNotification.objects.first()
        data = {
            "notifications": [selected_notification.id,]
        }
        form = UserNotificationForm(
            data=data,
            user=self.user
        )
        self.assertTrue(form.is_valid(), msg=form.errors)
        self.assertEqual(self.user.notifications.count(), 0)
        form.save()
        self.assertEqual(self.user.notifications.count(), 1)
        self.assertIn(selected_notification, self.user.notifications.all())


class UserAPITokenFormTestCase(BaseTestCase):
    def test_init(self):
        form = UserAPITokenForm(
            instance=self.user
        )
        self.assertEqual(form.form_title, str(_("Create new token")))
        self.assertEqual(form.form_caption, str(_("A new token needs to be validated by an administrator!")))
        self.assertEqual(form.action_url, reverse("user:api-token"))
        self.assertEqual(form.cancel_redirect, reverse("user:index"))

        self.assertIsNone(form.fields["token"].initial)
        self.assertTrue(form.fields["token"].widget.attrs["readonly"])

    def test_save(self):
        data = {
            "token": APIUserToken().token
        }
        form = UserAPITokenForm(
            data,
            instance=self.user
        )
        self.assertTrue(form.is_valid(), msg=form.errors)
        self.assertIsNone(self.user.api_token)
        token = form.save()
        self.assertEqual(self.user.api_token, token)
        new_token = form.save()
        self.assertEqual(self.user.api_token, new_token)
        try:
            token.refresh_from_db()
            self.fail("Token should be deleted and not be fetchable anymore")
        except ObjectDoesNotExist:
            pass