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

"""
import json
from datetime import timedelta

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

from intervention.forms.intervention import NewInterventionForm, EditInterventionForm
from intervention.forms.modals.revocation import NewRevocationModalForm, EditRevocationModalForm, \
    RemoveRevocationModalForm
from intervention.forms.modals.share import ShareModalForm
from intervention.models import Revocation
from konova.forms import SimpleGeomForm
from konova.settings import DEFAULT_GROUP, ZB_GROUP
from konova.tests.test_views import BaseTestCase
from konova.utils.generators import generate_random_string
from konova.utils.message_templates import REVOCATION_ADDED, REVOCATION_EDITED, REVOCATION_REMOVED
from user.models import UserAction


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

    def test_init(self):
        form = NewInterventionForm()
        self.assertEqual(form.form_title, str(_("New intervention")))
        self.assertEqual(form.action_url, reverse("intervention:new"))
        self.assertEqual(form.cancel_redirect, reverse("intervention:index"))

        initial_identifier = form.fields["identifier"].initial
        self.assertIsNotNone(initial_identifier)
        self.assertIn("EIV-", initial_identifier)

    def test_is_valid(self):
        data = {
            "identifier": generate_random_string(length=15, use_letters_uc=True),
            "title": generate_random_string(length=15, use_letters_uc=True),
        }
        form = NewInterventionForm({})
        self.assertFalse(form.is_valid())
        form = NewInterventionForm(data)
        self.assertTrue(form.is_valid(), msg=form.errors)

    def test_save(self):
        data = {
            "identifier": generate_random_string(length=15, use_letters_uc=True),
            "title": generate_random_string(length=15, use_letters_uc=True),
        }
        test_geom = self.create_dummy_geometry()
        geom_form_data = self.create_geojson(
            test_geom
        )
        geom_form_data = json.loads(geom_form_data)
        geom_form_data = {
            "output": json.dumps(geom_form_data)
        }
        geom_form = SimpleGeomForm(geom_form_data)

        form = NewInterventionForm(data)
        self.assertTrue(form.is_valid())
        self.assertTrue(geom_form.is_valid())
        obj = form.save(self.superuser, geom_form)

        self.assertEqual(obj.identifier, data["identifier"])
        self.assertEqual(obj.title, data["title"])
        self.assertIsNotNone(obj.legal)
        self.assertIsNotNone(obj.responsible)
        self.assertIsNotNone(obj.responsible.handler)
        self.assertEqual(obj.created.action, UserAction.CREATED)
        self.assertEqual(obj.created.user, self.superuser)
        self.assertEqual(obj.created, obj.log.first())
        self.assertEqual(obj.created, obj.modified)

        self.assertIn(self.superuser, obj.shared_users)
        self.assertTrue(test_geom.equals_exact(obj.geometry.geom, 0.000001))


class EditInterventionFormTestCase(NewInterventionFormTestCase):

    def test_init(self):
        today = now().date()
        data = {
            "identifier": self.intervention.identifier,
            "title": generate_random_string(length=5, use_letters_lc=True),
            "comment": generate_random_string(length=5, use_letters_lc=True),
            "registration_date": today,
            "binding_date": today,
            "registration_file_number": generate_random_string(length=5, use_numbers=True),
            "conservation_file_number": generate_random_string(length=5, use_numbers=True),
        }
        test_geom = self.create_dummy_geometry()
        geom_form_data = self.create_geojson(
            test_geom
        )
        geom_form_data = json.loads(geom_form_data)
        geom_form_data = {
            "output": json.dumps(geom_form_data)
        }

        geom_form = SimpleGeomForm(geom_form_data)
        form = EditInterventionForm(data, instance=self.intervention)
        self.assertTrue(geom_form.is_valid())
        self.assertTrue(form.is_valid())

        obj = form.save(self.superuser, geom_form)

        last_log = obj.log.first()
        self.assertEqual(last_log.user, self.superuser)
        self.assertEqual(last_log.action, UserAction.EDITED)
        self.assertEqual(last_log, obj.modified)
        self.assertEqual(obj.identifier, self.intervention.identifier)
        self.assertIsNotNone(obj.legal)
        self.assertIsNotNone(obj.responsible)
        self.assertIsNotNone(obj.responsible.handler)
        self.assertEqual(obj.title, data["title"])
        self.assertEqual(obj.comment, data["comment"])
        self.assert_equal_geometries(test_geom, obj.geometry.geom)

        self.assertEqual(obj.legal.binding_date, today)
        self.assertEqual(obj.legal.registration_date, today)
        self.assertEqual(obj.responsible.registration_file_number, data["registration_file_number"])
        self.assertEqual(obj.responsible.conservation_file_number, data["conservation_file_number"])


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

    def test_init(self):
        self.intervention.access_token = None
        self.intervention.save()

        form = ShareModalForm(
            request=self.request,
            instance=self.intervention
        )
        self.assertIsNotNone(self.intervention.access_token)
        self.assertEqual(form.form_title, str(_("Share")))
        self.assertEqual(form.form_caption, str(_("Share settings for {}").format(
            self.intervention.identifier
        )))
        self.assertEqual(form.template, "modal/modal_form.html")
        self.assertEqual(form.instance, self.intervention)
        self.assertEqual(form.user, self.superuser)

    def test_is_valid_and_save(self):
        # make user default-group-only (special treatment)
        self.superuser.groups.set(
            self.groups.filter(
                name=DEFAULT_GROUP
            )
        )

        self.assertNotIn(self.superuser, self.intervention.shared_users)
        self.assertNotIn(self.team, self.intervention.shared_teams)

        # Add new sharing data
        ## Default-only is able to add new sharing but can not remove existing ones
        data = {
            "users": [self.superuser.id,],
            "teams": [self.team.id,],
        }
        form = ShareModalForm(
            data,
            request=self.request,
            instance=self.intervention,
        )
        self.assertTrue(form.is_valid(), msg=form.errors)
        form.save()
        self.assertIn(self.superuser, self.intervention.shared_users)
        self.assertIn(self.team, self.intervention.shared_teams)

        # Try to remove sharing data das default-only user
        data = {
            "users": [],
            "teams": [],
        }
        form = ShareModalForm(
            data,
            request=self.request,
            instance=self.intervention,
        )
        self.assertFalse(form.is_valid(), msg=form.errors)
        self.assertTrue(form.has_error("teams"))
        self.assertTrue(form.has_error("users"))

        # Add another permission group for user
        self.superuser.groups.add(
            self.groups.get(
                name=ZB_GROUP
            )
        )
        form = ShareModalForm(
            data,
            request=self.request,
            instance=self.intervention,
        )
        self.assertTrue(form.is_valid(), msg=form.errors)
        self.assertIn(self.superuser, self.intervention.shared_users)
        self.assertIn(self.team, self.intervention.shared_teams)

        form.save()

        self.assertNotIn(self.superuser, self.intervention.shared_users)
        self.assertNotIn(self.team, self.intervention.shared_teams)


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

    def test_init(self):
        form = NewRevocationModalForm(
            request=self.request,
            instance=self.intervention
        )
        self.assertEqual(form.instance, self.intervention)
        self.assertEqual(form.user, self.request.user)
        self.assertEqual(form.request, self.request)
        self.assertEqual(form.form_title, str(_("Add revocation")))
        self.assertEqual(form.form_caption, "")
        self.assertEqual(form.form_attrs, {
            "enctype": "multipart/form-data"
        })

    def test_save(self):
        data = {
            "date": now().date(),
            "file": None,
            "comment": generate_random_string(20, use_letters_uc=True)
        }
        form = NewRevocationModalForm(
            data,
            request=self.request,
            instance=self.intervention
        )
        self.assertTrue(form.is_valid(), msg=form.errors)
        obj = form.save()
        self.assertEqual(obj.intervention, self.intervention)
        self.assertEqual(obj.date, data["date"])
        self.assertEqual(obj.legal, self.intervention.legal)
        self.assertEqual(obj.comment, data["comment"])

        last_log = self.intervention.log.first()
        self.assertEqual(last_log.user, self.superuser)
        self.assertEqual(last_log.action, UserAction.EDITED)
        self.assertEqual(last_log.comment, REVOCATION_ADDED)


class EditRevocationModalFormTestCase(NewRevocationModalFormTestCase):
    def setUp(self) -> None:
        super().setUp()
        self.revoc = Revocation.objects.get_or_create(
            date=now().date(),
            comment="TEST",
            legal=self.intervention.legal,
        )[0]

    def test_init(self):
        new_date = now().date() - timedelta(days=10)
        data = {
            "date": new_date,
            "comment": generate_random_string(20, use_letters_lc=True)
        }
        form = EditRevocationModalForm(
            data,
            request=self.request,
            instance=self.intervention,
            revocation=self.revoc
        )
        self.assertTrue(form.is_valid(), msg=form.errors)
        obj = form.save()
        self.assertEqual(obj.date, new_date)
        self.assertEqual(obj.comment, data["comment"])
        self.assertEqual(obj.legal, self.intervention.legal)
        self.assertEqual(obj.intervention, self.intervention)

        last_log = self.intervention.log.first()
        self.assertEqual(last_log.action, UserAction.EDITED)
        self.assertEqual(last_log.user, self.superuser)
        self.assertEqual(last_log.comment, REVOCATION_EDITED)

class RemoveRevocationModalFormTestCase(EditRevocationModalFormTestCase):
    def setUp(self) -> None:
        super().setUp()

    def test_init(self):
        form = RemoveRevocationModalForm(
            request=self.request,
            instance=self.intervention,
            revocation=self.revoc,
        )
        self.assertEqual(form.instance, self.intervention)
        self.assertEqual(form.revocation, self.revoc)
        self.assertEqual(form.request, self.request)
        self.assertEqual(form.user, self.request.user)

    def test_save(self):
        data = {
            "confirm": True,
        }
        form = RemoveRevocationModalForm(
            data,
            request=self.request,
            instance=self.intervention,
            revocation=self.revoc
        )
        self.assertTrue(form.is_valid(), msg=form.errors)
        form.save()

        try:
            self.revoc.refresh_from_db()
            self.fail("Revocation should not exist anymore")
        except ObjectDoesNotExist:
            pass
        last_log = self.intervention.log.first()
        self.assertEqual(last_log.action, UserAction.EDITED)
        self.assertEqual(last_log.user, self.superuser)
        self.assertEqual(last_log.comment, REVOCATION_REMOVED)
