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

"""
from dal import autocomplete
from django import forms
from django.db import transaction
from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext_lazy as _

from compensation.forms.mixins import CEFCompensationFormMixin, CoherenceCompensationFormMixin, PikCompensationFormMixin
from compensation.models import Compensation
from intervention.inputs import GenerateInput
from intervention.models import Intervention
from konova.forms import BaseForm, SimpleGeomForm
from konova.utils.message_templates import COMPENSATION_ADDED_TEMPLATE, EDITED_GENERAL_DATA
from user.models import UserActionLogEntry, User


class AbstractCompensationForm(BaseForm):
    """ Abstract form for compensations

    Holds all important form fields, which are used in compensation and eco account forms

    """
    identifier = forms.CharField(
        label=_("Identifier"),
        label_suffix="",
        max_length=255,
        help_text=_("Generated automatically - not editable"),
        widget=GenerateInput(
            attrs={
                "class": "form-control",
                "url": None,  # Needs to be set in inheriting constructors
                "readonly": True,
            }
        )
    )
    title = forms.CharField(
        label=_("Title"),
        label_suffix="",
        help_text=_("An explanatory name"),
        max_length=255,
        widget=forms.TextInput(
            attrs={
                "placeholder": _("Compensation XY; Location ABC"),
                "class": "form-control",
            }
        )
    )
    comment = forms.CharField(
        label_suffix="",
        label=_("Comment"),
        required=False,
        help_text=_("Additional comment"),
        widget=forms.Textarea(
            attrs={
                "rows": 5,
                "class": "form-control"
            }
        )
    )

    class Meta:
        abstract = True


class NewCompensationForm(AbstractCompensationForm,
                          CEFCompensationFormMixin,
                          CoherenceCompensationFormMixin,
                          PikCompensationFormMixin):
    """ Form for creating new compensations.

    Can be initialized with an intervention id for preselecting the related intervention.
        form = NewCompensationForm(request.POST or None, intervention_id=intervention_id)
        ...
    The intervention id will not be resolved into the intervention ORM object but instead will be used to initialize
    the related form field.

    """
    intervention = forms.ModelChoiceField(
        label=_("compensates intervention"),
        label_suffix="",
        help_text=_("Select the intervention for which this compensation compensates"),
        queryset=Intervention.objects.filter(
            deleted=None,
        ),
        widget=autocomplete.ModelSelect2(
            url="intervention:autocomplete",
            attrs={
                "data-placeholder": _("Click for selection"),
                "data-minimum-input-length": 3,
            }
        ),
    )

    # Define a field order for a nicer layout instead of running with the inheritance result
    field_order = [
        "identifier",
        "title",
        "intervention",
        "is_pik",
        "is_cef",
        "is_coherence_keeping",
        "comment",
    ]

    def __init__(self, *args, **kwargs):
        intervention_id = kwargs.pop("intervention_id", None)
        super().__init__(*args, **kwargs)
        self.form_title = _("New compensation")

        # If the compensation shall directly be initialized from an intervention, we need to fill in the intervention id
        # and disable the form field.
        # Furthermore the action_url needs to be set accordingly.
        if intervention_id is not None:
            self.initialize_form_field("intervention", intervention_id)
            self.disable_form_field("intervention")
            self.action_url = reverse("compensation:new", args=(intervention_id,))
            self.cancel_redirect = reverse("intervention:detail", args=(intervention_id,))
        else:
            self.action_url = reverse("compensation:new")
            self.cancel_redirect = reverse("compensation:index")

        tmp = Compensation()
        identifier = tmp.generate_new_identifier()
        self.initialize_form_field("identifier", identifier)
        self.fields["identifier"].widget.attrs["url"] = reverse_lazy("compensation:new-id")

    def __create_comp(self, user):
        """ Creates the compensation from form data

        Args:
            user (User): The performing user

        Returns:
            comp (Compensation): The compensation object
        """
        # Fetch data from cleaned POST values
        identifier = self.cleaned_data.get("identifier", None)
        title = self.cleaned_data.get("title", None)
        intervention = self.cleaned_data.get("intervention", None)
        is_cef = self.cleaned_data.get("is_cef", None)
        is_coherence_keeping = self.cleaned_data.get("is_coherence_keeping", None)
        is_pik = self.cleaned_data.get("is_pik", None)
        comment = self.cleaned_data.get("comment", None)

        # Create log entry
        action = UserActionLogEntry.get_created_action(user)

        # Finally create main object
        comp = Compensation.objects.create(
            identifier=identifier,
            title=title,
            intervention=intervention,
            created=action,
            modified=action,
            is_cef=is_cef,
            is_coherence_keeping=is_coherence_keeping,
            is_pik=is_pik,
            comment=comment,
        )

        # Add the log entry to the main objects log list
        comp.log.add(action)
        return comp, action

    def save(self, user: User, geom_form: SimpleGeomForm):
        with transaction.atomic():
            comp, action = self.__create_comp(user)
            comp.intervention.mark_as_edited(user, edit_comment=COMPENSATION_ADDED_TEMPLATE.format(comp.identifier))

        # Process the geometry form
        geometry = geom_form.save(action)
        comp.geometry = geometry
        comp.save()

        return comp


class EditCompensationForm(NewCompensationForm):
    """ Form for editing compensations

    """
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.form_title = _("Edit compensation")
        self.action_url = reverse("compensation:edit", args=(self.instance.id,))
        self.cancel_redirect = reverse("compensation:detail", args=(self.instance.id,))

        # Initialize form data
        form_data = {
            "identifier": self.instance.identifier,
            "title": self.instance.title,
            "intervention": self.instance.intervention,
            "is_cef": self.instance.is_cef,
            "is_coherence_keeping": self.instance.is_coherence_keeping,
            "is_pik": self.instance.is_pik,
            "comment": self.instance.comment,
        }
        disabled_fields = []
        self.load_initial_data(
            form_data,
            disabled_fields
        )

    def save(self, user: User, geom_form: SimpleGeomForm):
        with transaction.atomic():
            # Create log entry
            action = UserActionLogEntry.get_edited_action(user)

            # Fetch data from cleaned POST values
            title = self.cleaned_data.get("title", None)
            intervention = self.cleaned_data.get("intervention", None)
            is_cef = self.cleaned_data.get("is_cef", None)
            is_coherence_keeping = self.cleaned_data.get("is_coherence_keeping", None)
            is_pik = self.cleaned_data.get("is_pik", None)
            comment = self.cleaned_data.get("comment", None)

            self.instance.title = title
            self.instance.intervention = intervention
            self.instance.is_cef = is_cef
            self.instance.is_coherence_keeping = is_coherence_keeping
            self.instance.comment = comment
            self.instance.is_pik = is_pik
            self.instance.modified = action
            self.instance.save()

            self.instance.log.add(action)
            intervention.mark_as_edited(user, self.request, EDITED_GENERAL_DATA)

        # Process the geometry form (NOT ATOMIC TRANSACTION DUE TO CELERY!)
        geometry = geom_form.save(action)
        self.instance.geometry = geometry
        self.instance.save()

        return self.instance