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

"""
from dal import autocomplete
from django.contrib.auth.models import User
from django.db import transaction
from django import forms
from django.urls import reverse
from django.utils.translation import  gettext_lazy as _

from compensation.models import EcoAccount, EcoAccountDeduction
from intervention.inputs import TextToClipboardInput
from intervention.models import Revocation, RevocationDocument, Intervention
from konova.forms import BaseModalForm
from konova.settings import ZB_GROUP, ETS_GROUP
from konova.utils.general import format_german_float
from konova.utils.messenger import Messenger
from konova.utils.user_checks import in_group
from user.models import UserActionLogEntry, UserAction


class ShareInterventionModalForm(BaseModalForm):
    url = forms.CharField(
        label=_("Share link"),
        label_suffix="",
        help_text=_("Send this link to users who you want to have writing access on the data"),
        required=False,
        widget=TextToClipboardInput(
            attrs={
                "readonly": True,
                "class": "form-control",
            }
        )
    )
    users = forms.MultipleChoiceField(
        label=_("Shared with"),
        label_suffix="",
        required=True,
        help_text=_("Remove check to remove access for this user"),
        widget=forms.CheckboxSelectMultiple(
            attrs={
                "class": "list-unstyled",
            }
        ),
        choices=[]
    )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.form_title = _("Share")
        self.form_caption = _("Share settings for {}").format(self.instance.identifier)
        self.template = "modal/modal_form.html"

        # Make sure an access_token is set
        if self.instance.access_token is None:
            self.instance.generate_access_token()

        self._init_fields()

    def _init_fields(self):
        """ Wraps initializing of fields

        Returns:

        """
        # Initialize share_link field
        url_name = f"{self.instance._meta.app_label}:share"
        self.share_link = self.request.build_absolute_uri(
            reverse(url_name, args=(self.instance.id, self.instance.access_token,))
        )
        self.initialize_form_field(
            "url",
            self.share_link
        )

        # Initialize users field
        # Remove field if user is not in registration or conservation group
        if not in_group(self.request.user, ZB_GROUP) and not in_group(self.request.user, ETS_GROUP):
            del self.fields["users"]
        else:
            users = self.instance.users.all()
            choices = []
            for n in users:
                choices.append(
                    (n.id, n.username)
                )
            self.fields["users"].choices = choices
            u_ids = list(users.values_list("id", flat=True))
            self.initialize_form_field(
                "users",
                u_ids
            )

    def save(self):
        accessing_users = User.objects.filter(
            id__in=self.cleaned_data["users"]
        )
        self.instance.share_with_list(accessing_users)


class NewRevocationModalForm(BaseModalForm):
    date = forms.DateField(
        label=_("Date"),
        label_suffix=_(""),
        help_text=_("Date of revocation"),
        widget=forms.DateInput(
            attrs={
                "type": "date",
                "data-provide": "datepicker",
                "class": "form-control",
            },
            format="%d.%m.%Y"
        )
    )
    file = forms.FileField(
        label=_("Document"),
        label_suffix=_(""),
        required=False,
        help_text=_("Must be smaller than 15 Mb"),
        widget=forms.FileInput(
            attrs={
                "class": "form-control-file"
            }
        )
    )
    comment = forms.CharField(
        required=False,
        max_length=200,
        label=_("Comment"),
        label_suffix=_(""),
        help_text=_("Additional comment, maximum {} letters").format(200),
        widget=forms.Textarea(
            attrs={
                "cols": 30,
                "rows": 5,
                "class": "form-control",
            }
        )
    )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.form_title = _("Add revocation")
        self.form_caption = ""
        self.form_attrs = {
            "enctype": "multipart/form-data",  # important for file upload
        }

    def save(self):
        with transaction.atomic():
            created_action = UserActionLogEntry.objects.create(
                user=self.user,
                action=UserAction.CREATED
            )
            edited_action = UserActionLogEntry.objects.create(
                user=self.user,
                action=UserAction.EDITED
            )
            revocation = Revocation.objects.create(
                date=self.cleaned_data["date"],
                comment=self.cleaned_data["comment"],
                created=created_action,
            )
            self.instance.modified = edited_action
            self.instance.save()
            self.instance.log.add(edited_action)
            self.instance.legal.revocation = revocation
            self.instance.legal.save()

            if self.cleaned_data["file"]:
                RevocationDocument.objects.create(
                    title="revocation_of_{}".format(self.instance.identifier),
                    date_of_creation=self.cleaned_data["date"],
                    comment=self.cleaned_data["comment"],
                    file=self.cleaned_data["file"],
                    instance=revocation
                )
        return revocation


class CheckModalForm(BaseModalForm):
    """ The modal form for running a check on interventions and their compensations

    """
    checked_intervention = forms.BooleanField(
        label=_("Checked intervention data"),
        label_suffix="",
        widget=forms.CheckboxInput(),
        required=True,
    )
    checked_comps = forms.BooleanField(
        label=_("Checked compensations data and payments"),
        label_suffix="",
        widget=forms.CheckboxInput(),
        required=True
    )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.form_title = _("Run check")
        self.form_caption = _("I, {} {}, confirm that all necessary control steps have been performed by myself.").format(self.user.first_name, self.user.last_name)

    def is_valid(self) -> bool:
        """ Perform a validity check based on quality_check() logic

        Returns:
            result (bool)
        """
        super_result = super().is_valid()
        # Perform check
        checker = self.instance.quality_check()
        for msg in checker.messages:
            self.add_error(
                "checked_intervention",
                msg
            )
        comps = self.instance.compensations.all()
        for comp in comps:
            checker = comp.quality_check()
            for msg in checker.messages:
                self.add_error(
                    "checked_comps",
                    f"{comp.identifier}: {msg}"
                )
        return super_result and checker.valid

    def save(self):
        """ Saving logic

        Returns:

        """
        with transaction.atomic():
            self.instance.toggle_checked(self.user)

        # Send message to the SSO server
        messenger = Messenger(
            self.instance.users.all(),
            type="INFO",
        )
        messenger.send_object_checked(
            self.instance.identifier,
            self.user,
        )


class NewDeductionModalForm(BaseModalForm):
    """ Form for creating new deduction

    Can be used for Intervention view as well as for EcoAccount views.

    Parameter 'instance' can be an intervention, as well as an ecoAccount.
    An instance check handles both workflows properly.

    """
    account = forms.ModelChoiceField(
        label=_("Eco-account"),
        label_suffix="",
        help_text=_("Only recorded accounts can be selected for deductions"),
        queryset=EcoAccount.objects.filter(deleted=None),
        widget=autocomplete.ModelSelect2(
            url="accounts-autocomplete",
            attrs={
                "data-placeholder": _("Eco-account"),
                "data-minimum-input-length": 3,
                "readonly": True,
            }
        ),
    )
    surface = forms.DecimalField(
        min_value=0.00,
        decimal_places=2,
        label=_("Surface"),
        label_suffix="",
        help_text=_("in m²"),
        widget=forms.NumberInput(
            attrs={
                "class": "form-control",
                "placeholder": "0,00",
            }
        )
    )
    intervention = forms.ModelChoiceField(
        label=_("Intervention"),
        label_suffix="",
        help_text=_("Only shared interventions can be selected"),
        queryset=Intervention.objects.filter(deleted=None),
        widget=autocomplete.ModelSelect2(
            url="interventions-autocomplete",
            attrs={
                "data-placeholder": _("Intervention"),
                "data-minimum-input-length": 3,
            }
        ),
    )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.form_title = _("New Deduction")
        self.form_caption = _("Enter the information for a new deduction from a chosen eco-account")
        self.is_intervention_initially = False

        # Check for Intervention or EcoAccount
        if isinstance(self.instance, Intervention):
            # Form has been called with a given intervention
            self.initialize_form_field("intervention", self.instance)
            self.disable_form_field("intervention")
            self.is_intervention_initially = True
        elif isinstance(self.instance, EcoAccount):
            # Form has been called with a given account --> make it initial in the form and read-only
            self.initialize_form_field("account", self.instance)
            self.disable_form_field("account")
        else:
            raise NotImplementedError

    def is_valid(self):
        """ Custom validity check

        Makes sure the deduction can not contain more surface than the account still provides

        Returns:
            is_valid (bool)
        """
        super_result = super().is_valid()
        if self.is_intervention_initially:
            acc = self.cleaned_data["account"]
        else:
            acc = self.instance

        if not acc.recorded:
            self.add_error(
                "account",
                _("Eco-account {} is not recorded yet. You can only deduct from recorded accounts.").format(acc.identifier)
            )
            return False

        # Calculate valid surface
        deductable_surface = acc.deductable_surface
        sum_surface_deductions = acc.get_deductions_surface()
        rest_surface = deductable_surface - sum_surface_deductions
        form_surface = float(self.cleaned_data["surface"])
        is_valid_surface = form_surface < rest_surface
        if not is_valid_surface:
            self.add_error(
                "surface",
                _("The account {} has not enough surface for a deduction of {} m². There are only {} m² left").format(
                    acc.identifier,
                    format_german_float(form_surface),
                    format_german_float(rest_surface),
                ),
            )
        return is_valid_surface and super_result

    def save(self):
        with transaction.atomic():
            # Create log entry
            user_action_edit = UserActionLogEntry.objects.create(
                user=self.user,
                action=UserAction.EDITED
            )
            user_action_create = UserActionLogEntry.objects.create(
                user=self.user,
                action=UserAction.CREATED
            )
            self.instance.log.add(user_action_edit)
            self.instance.modified = user_action_edit
            self.instance.save()

            # Create deductions depending on Intervention or EcoAccount as the initial instance
            if self.is_intervention_initially:
                deduction = EcoAccountDeduction.objects.create(
                    intervention=self.instance,
                    account=self.cleaned_data["account"],
                    surface=self.cleaned_data["surface"],
                    created=user_action_create,
                )
            else:
                deduction = EcoAccountDeduction.objects.create(
                    intervention=self.cleaned_data["intervention"],
                    account=self.instance,
                    surface=self.cleaned_data["surface"],
                    created=user_action_create,
                )
        return deduction