"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 04.12.20

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

from codelist.models import KonovaCode
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID, CODELIST_HANDLER_ID
from compensation.models import Compensation, EcoAccount
from intervention.inputs import GenerateInput
from intervention.models import Intervention, Responsibility, Legal, Handler
from konova.forms import BaseForm, SimpleGeomForm
from konova.utils.message_templates import EDITED_GENERAL_DATA, COMPENSATION_ADDED_TEMPLATE
from user.models import UserActionLogEntry


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"),
        widget=GenerateInput(
            attrs={
                "class": "form-control",
                "url": None,  # Needs to be set in inheriting constructors
            }
        )
    )
    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 CompensationResponsibleFormMixin(forms.Form):
    """ Encapsulates form fields used in different compensation related models like EcoAccount or EMA

    """
    conservation_office = forms.ModelChoiceField(
        label=_("Conservation office"),
        label_suffix="",
        help_text=_("Select the responsible office"),
        queryset=KonovaCode.objects.filter(
            is_archived=False,
            is_leaf=True,
            code_lists__in=[CODELIST_CONSERVATION_OFFICE_ID],
        ),
        widget=autocomplete.ModelSelect2(
            url="codes-conservation-office-autocomplete",
            attrs={
                "data-placeholder": _("Click for selection")
            }
        ),
    )
    conservation_file_number = forms.CharField(
        label=_("Conservation office file number"),
        label_suffix="",
        max_length=255,
        required=False,
        widget=forms.TextInput(
            attrs={
                "placeholder": _("ETS-123/ABC.456"),
                "class": "form-control",
            }
        )
    )

    handler_type = forms.ModelChoiceField(
        label=_("Eco-Account handler type"),
        label_suffix="",
        help_text=_("What type of handler is responsible for the ecoaccount?"),
        required=False,
        queryset=KonovaCode.objects.filter(
            is_archived=False,
            is_leaf=True,
            code_lists__in=[CODELIST_HANDLER_ID],
        ),
        widget=autocomplete.ModelSelect2(
            url="codes-handler-autocomplete",
            attrs={
                "data-placeholder": _("Click for selection"),
            }
        ),
    )
    handler_detail = forms.CharField(
        label=_("Eco-Account handler detail"),
        label_suffix="",
        max_length=255,
        required=False,
        help_text=_("Detail input on the handler"),
        widget=forms.TextInput(
            attrs={
                "placeholder": _("Company Mustermann"),
                "class": "form-control",
            }
        )
    )


class CEFCompensationFormMixin(forms.Form):
    """ A form mixin, providing CEF compensation field

    """
    is_cef = forms.BooleanField(
        label_suffix="",
        label=_("Is CEF"),
        help_text=_("Optionally: Whether this compensation is a CEF compensation?"),
        required=False,
        widget=forms.CheckboxInput()
    )


class CoherenceCompensationFormMixin(forms.Form):
    """ A form mixin, providing coherence compensation field

    """
    is_coherence_keeping = forms.BooleanField(
        label_suffix="",
        label=_("Is coherence keeping"),
        help_text=_("Optionally: Whether this compensation is a coherence keeping compensation?"),
        required=False,
        widget=forms.CheckboxInput()
    )


class PikCompensationFormMixin(forms.Form):
    """ A form mixin, providing PIK compensation field

    """
    is_pik = forms.BooleanField(
        label_suffix="",
        label=_("Is PIK"),
        help_text=_("Optionally: Whether this compensation is a compensation integrated in production?"),
        required=False,
        widget=forms.CheckboxInput()
    )


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="interventions-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, geom_form) -> Compensation:
        """ Creates the compensation from form data

        Args:
            user (User): The performing user
            geom_form (SimpleGeomForm): The geometry form

        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)
        # Process the geometry form
        geometry = geom_form.save(action)

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

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

    def save(self, user: User, geom_form: SimpleGeomForm):
        with transaction.atomic():
            comp = self.__create_comp(user, geom_form)
            comp.intervention.mark_as_edited(user, edit_comment=COMPENSATION_ADDED_TEMPLATE.format(comp.identifier))
        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():
            # 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_edited_action(user)

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

            # Finally create main object
            self.instance.identifier = identifier
            self.instance.title = title
            self.instance.intervention = intervention
            self.instance.geometry = geometry
            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)
        return self.instance


class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMixin, PikCompensationFormMixin):
    """ Form for creating eco accounts

    Inherits from basic AbstractCompensationForm and further form fields from CompensationResponsibleFormMixin

    """
    surface = forms.DecimalField(
        min_value=0.00,
        decimal_places=2,
        label=_("Available Surface"),
        label_suffix="",
        required=False,
        help_text=_("The amount that can be used for deductions"),
        widget=forms.NumberInput(
            attrs={
                "class": "form-control",
                "placeholder": "0,00"
            }
        )
    )
    registration_date = forms.DateField(
        label=_("Agreement date"),
        label_suffix="",
        help_text=_("When did the parties agree on this?"),
        required=False,
        widget=forms.DateInput(
            attrs={
                "type": "date",
                "class": "form-control",
            },
            format="%d.%m.%Y"
        )
    )

    field_order = [
        "identifier",
        "title",
        "conservation_office",
        "registration_date",
        "surface",
        "conservation_file_number",
        "is_pik",
        "handler_type",
        "handler_detail",
        "comment",
    ]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.form_title = _("New Eco-Account")

        self.action_url = reverse("compensation:acc:new")
        self.cancel_redirect = reverse("compensation:acc:index")

        tmp = EcoAccount()
        identifier = tmp.generate_new_identifier()
        self.initialize_form_field("identifier", identifier)
        self.fields["identifier"].widget.attrs["url"] = reverse_lazy("compensation:acc:new-id")
        self.fields["title"].widget.attrs["placeholder"] = _("Eco-Account XY; Location ABC")

    def save(self, user: User, geom_form: SimpleGeomForm):
        with transaction.atomic():
            # Fetch data from cleaned POST values
            identifier = self.cleaned_data.get("identifier", None)
            title = self.cleaned_data.get("title", None)
            registration_date = self.cleaned_data.get("registration_date", None)
            handler_type = self.cleaned_data.get("handler_type", None)
            handler_detail = self.cleaned_data.get("handler_detail", None)
            surface = self.cleaned_data.get("surface", None)
            conservation_office = self.cleaned_data.get("conservation_office", None)
            conservation_file_number = self.cleaned_data.get("conservation_file_number", 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)
            # Process the geometry form
            geometry = geom_form.save(action)

            handler = Handler.objects.create(
                type=handler_type,
                detail=handler_detail,
            )

            responsible = Responsibility.objects.create(
                handler=handler,
                conservation_file_number=conservation_file_number,
                conservation_office=conservation_office,
            )

            legal = Legal.objects.create(
                registration_date=registration_date
            )

            # Finally create main object
            acc = EcoAccount.objects.create(
                identifier=identifier,
                title=title,
                responsible=responsible,
                deductable_surface=surface,
                created=action,
                geometry=geometry,
                comment=comment,
                is_pik=is_pik,
                legal=legal
            )
            acc.share_with_user(user)

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


class EditEcoAccountForm(NewEcoAccountForm):
    """ Form for editing eco accounts

    """

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.form_title = _("Edit Eco-Account")

        self.action_url = reverse("compensation:acc:edit", args=(self.instance.id,))
        self.cancel_redirect = reverse("compensation:acc:detail", args=(self.instance.id,))

        # Initialize form data
        reg_date = self.instance.legal.registration_date
        if reg_date is not None:
            reg_date = reg_date.isoformat()

        form_data = {
            "identifier": self.instance.identifier,
            "title": self.instance.title,
            "surface": self.instance.deductable_surface,
            "handler_type": self.instance.responsible.handler.type,
            "handler_detail": self.instance.responsible.handler.detail,
            "registration_date": reg_date,
            "conservation_office": self.instance.responsible.conservation_office,
            "conservation_file_number": self.instance.responsible.conservation_file_number,
            "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():
            # Fetch data from cleaned POST values
            identifier = self.cleaned_data.get("identifier", None)
            title = self.cleaned_data.get("title", None)
            registration_date = self.cleaned_data.get("registration_date", None)
            handler_type = self.cleaned_data.get("handler_type", None)
            handler_detail = self.cleaned_data.get("handler_detail", None)
            surface = self.cleaned_data.get("surface", None)
            conservation_office = self.cleaned_data.get("conservation_office", None)
            conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
            comment = self.cleaned_data.get("comment", None)
            is_pik = self.cleaned_data.get("is_pik", None)

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

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

            # Update responsible data
            self.instance.responsible.handler.type = handler_type
            self.instance.responsible.handler.detail = handler_detail
            self.instance.responsible.handler.save()
            self.instance.responsible.conservation_office = conservation_office
            self.instance.responsible.conservation_file_number = conservation_file_number
            self.instance.responsible.save()

            # Update legal data
            self.instance.legal.registration_date = registration_date
            self.instance.legal.save()

            # Update main oject data
            self.instance.identifier = identifier
            self.instance.title = title
            self.instance.deductable_surface = surface
            self.instance.geometry = geometry
            self.instance.comment = comment
            self.instance.is_pik = is_pik
            self.instance.modified = action
            self.instance.save()

            # Add the log entry to the main objects log list
            self.instance.log.add(action)
        return self.instance
