"""
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 django.contrib.auth.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_COMPENSATION_FUNDING_ID, CODELIST_CONSERVATION_OFFICE_ID
from compensation.models import Compensation, EcoAccount
from intervention.inputs import GenerateInput
from intervention.models import Intervention, ResponsibilityData
from konova.forms import BaseForm, SimpleGeomForm
from user.models import UserActionLogEntry, UserAction


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",
            }
        )
    )
    fundings = forms.ModelMultipleChoiceField(
        label=_("Fundings"),
        label_suffix="",
        required=False,
        help_text=_("Select fundings for this compensation"),
        queryset=KonovaCode.objects.filter(
            is_archived=False,
            is_leaf=True,
            code_lists__in=[CODELIST_COMPENSATION_FUNDING_ID],
        ),
        widget=autocomplete.ModelSelect2Multiple(
            url="codes-compensation-funding-autocomplete",
            attrs={
                "data-placeholder": _("Click for selection"),
            }
        ),
    )
    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 = forms.CharField(
        label=_("Eco-account handler"),
        label_suffix="",
        max_length=255,
        required=False,
        help_text=_("Who handles the eco-account"),
        widget=forms.TextInput(
            attrs={
                "placeholder": _("Company Mustermann"),
                "class": "form-control",
            }
        )
    )


class NewCompensationForm(AbstractCompensationForm):
    """ 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",
        "fundings",
        "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 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)
            fundings = self.cleaned_data.get("fundings", None)
            intervention = self.cleaned_data.get("intervention", None)
            comment = self.cleaned_data.get("comment", None)

            # Create log entry
            action = UserActionLogEntry.objects.create(
                user=user,
                action=UserAction.CREATED,
            )
            # 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,
                geometry=geometry,
                comment=comment,
            )
            comp.fundings.set(fundings)

            # Add the log entry to the main objects log list
            comp.log.add(action)
        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,
            "fundings": self.instance.fundings.all(),
            "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)
            fundings = self.cleaned_data.get("fundings", None)
            intervention = self.cleaned_data.get("intervention", None)
            comment = self.cleaned_data.get("comment", None)

            # Create log entry
            action = UserActionLogEntry.objects.create(
                user=user,
                action=UserAction.EDITED,
            )

            # 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.comment = comment
            self.instance.modified = action
            self.instance.fundings.set(fundings)
            self.instance.save()

            self.instance.log.add(action)
        return self.instance


class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMixin):
    """ 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"
            }
        )
    )

    field_order = [
        "identifier",
        "title",
        "conservation_office",
        "surface",
        "conservation_file_number",
        "handler",
        "fundings",
        "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)
            fundings = self.cleaned_data.get("fundings", None)
            handler = self.cleaned_data.get("handler", 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)

            # Create log entry
            action = UserActionLogEntry.objects.create(
                user=user,
                action=UserAction.CREATED,
            )
            # Process the geometry form
            geometry = geom_form.save(action)

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

            # Finally create main object
            acc = EcoAccount.objects.create(
                identifier=identifier,
                title=title,
                responsible=responsible,
                deductable_surface=surface,
                created=action,
                geometry=geometry,
                comment=comment,
            )
            acc.fundings.set(fundings)
            acc.users.add(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
        form_data = {
            "identifier": self.instance.identifier,
            "title": self.instance.title,
            "surface": self.instance.deductable_surface,
            "handler": self.instance.responsible.handler,
            "conservation_office": self.instance.responsible.conservation_office,
            "conservation_file_number": self.instance.responsible.conservation_file_number,
            "fundings": self.instance.fundings.all(),
            "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)
            fundings = self.cleaned_data.get("fundings", None)
            handler = self.cleaned_data.get("handler", 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)

            # Create log entry
            action = UserActionLogEntry.objects.create(
                user=user,
                action=UserAction.EDITED,
            )
            # Process the geometry form
            geometry = geom_form.save(action)

            # Update responsible data
            self.instance.responsible.handler = handler
            self.instance.responsible.conservation_office = conservation_office
            self.instance.responsible.conservation_file_number = conservation_file_number
            self.instance.responsible.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.modified = action
            self.instance.save()
            self.instance.fundings.set(fundings)

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