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

"""
from abc import abstractmethod

from django import forms
from django.utils.translation import gettext_lazy as _

from compensation.models import EcoAccount
from konova.models import BaseObject


class BaseForm(forms.Form):
    """
    Basic form for that holds attributes needed in all other forms
    """
    template = None
    action_url = None
    action_btn_label = _("Save")
    form_title = None
    cancel_redirect = None
    form_caption = None
    instance = None  # The data holding model object
    request = None
    form_attrs = {}  # Holds additional attributes, that can be used in the template
    has_required_fields = False  # Automatically set. Triggers hint rendering in templates
    show_cancel_btn = True
    label_input_ratio = (3, 9)  # used for col-sm-xy in the template. Must sum up to 12. Specify on inheriting forms

    def __init__(self, *args, **kwargs):
        self.instance = kwargs.pop("instance", None)
        super().__init__(*args, **kwargs)
        if self.request is not None:
            self.user = self.request.user
        # Check for required fields
        for _field_name, _field_val in self.fields.items():
            if _field_val.required:
                self.has_required_fields = True
                break

        self.check_for_recorded_instance()
        self.__check_valid_label_input_ratio()

    @abstractmethod
    def save(self):
        # To be implemented in subclasses!
        pass

    def __check_valid_label_input_ratio(self):
        """ Checks whether the configured label-input ratio is valid

        If not valid an AssertionError will be raised.
        The valid sum of label-input ratio is defined by bootstrap's column layout system.

        Returns:

        """
        ratio = self.label_input_ratio[0] + self.label_input_ratio[1]
        if ratio != 12:
            raise AssertionError(f"Label-input ratio on form must sum up to 12! It's {self.label_input_ratio}")

    def disable_form_field(self, field: str):
        """
        Disables a form field for user editing
        """
        self.fields[field].widget.attrs["readonly"] = True
        self.fields[field].disabled = True
        self.fields[field].widget.attrs["title"] = _("Not editable")

    def initialize_form_field(self, field: str, val):
        """
        Initializes a form field with a value
        """
        self.fields[field].initial = val

    def add_placeholder_for_field(self, field: str, val):
        """
        Adds a placeholder to a field after initialization without the need to redefine the form widget

        Args:
            field (str): Field name
            val (str): Placeholder

        Returns:

        """
        self.fields[field].widget.attrs["placeholder"] = val

    def load_initial_data(self, form_data: dict, disabled_fields: list = None):
        """ Initializes form data from instance

        Inserts instance data into form and disables form fields

        Returns:

        """
        if self.instance is None:
            return
        for k, v in form_data.items():
            self.initialize_form_field(k, v)
        if disabled_fields:
            for field in disabled_fields:
                self.disable_form_field(field)

    def add_widget_html_class(self, field: str, cls: str):
        """ Adds a HTML class string to the widget of a field

        Args:
            field (str): The field's name
            cls (str): The new class string

        Returns:

        """
        set_class = self.fields[field].widget.attrs.get("class", "")
        if cls in set_class:
            return
        else:
            set_class += " " + cls
        self.fields[field].widget.attrs["class"] = set_class

    def remove_widget_html_class(self, field: str, cls: str):
        """ Removes a HTML class string from the widget of a field

        Args:
            field (str): The field's name
            cls (str): The new class string

        Returns:

        """
        set_class = self.fields[field].widget.attrs.get("class", "")
        set_class = set_class.replace(cls, "")
        self.fields[field].widget.attrs["class"] = set_class

    def check_for_recorded_instance(self):
        """ Checks if the instance is recorded and runs some special logic if yes

        If the instance is recorded, the form shall not display any possibility to
        edit any data. Instead, the users should get some information about why they can not edit anything.

        There are situations where the form should be rendered regularly,
        e.g deduction forms for (recorded) eco accounts.

        Returns:

        """
        is_none = self.instance is None
        is_other_data_type = not isinstance(self.instance, BaseObject)

        if is_none or is_other_data_type:
            # Do nothing
            return

        if self.instance.is_recorded:
            self.block_form()

    def block_form(self):
        """
        Overwrites template, providing no actions

        Returns:

        """
        self.template = "form/recorded_no_edit.html"