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

"""
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.db.models import Sum
from django.http import HttpRequest, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.translation import gettext_lazy as _

from compensation.forms.eco_account import EditEcoAccountForm, NewEcoAccountForm, RemoveEcoAccountModalForm
from compensation.models import EcoAccount
from compensation.tables.eco_account import EcoAccountTable
from konova.contexts import BaseContext
from konova.decorators import shared_access_required, default_group_required, any_group_check, login_required_modal
from konova.forms import SimpleGeomForm
from konova.settings import ETS_GROUP, DEFAULT_GROUP, ZB_GROUP
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.message_templates import CANCEL_ACC_RECORDED_OR_DEDUCTED, RECORDED_BLOCKS_EDIT, FORM_INVALID, \
    IDENTIFIER_REPLACED, DO_NOT_FORGET_TO_SHARE
from konova.utils.user_checks import in_group


@login_required
@any_group_check
def index_view(request: HttpRequest):
    """
    Renders the index view for eco accounts

    Args:
        request (HttpRequest): The incoming request

    Returns:
        A rendered view
    """
    template = "generic_index.html"
    eco_accounts = EcoAccount.objects.filter(
        deleted=None,
    ).order_by(
        "-modified__timestamp"
    )
    table = EcoAccountTable(
        request=request,
        queryset=eco_accounts
    )
    context = {
        "table": table,
        TAB_TITLE_IDENTIFIER: _("Eco-account - Overview"),
    }
    context = BaseContext(request, context).context
    return render(request, template, context)


@login_required
@default_group_required
def new_view(request: HttpRequest):
    """
    Renders a view for a new eco account creation

    Args:
        request (HttpRequest): The incoming request

    Returns:

    """
    template = "compensation/form/view.html"
    data_form = NewEcoAccountForm(request.POST or None)
    geom_form = SimpleGeomForm(request.POST or None, read_only=False)
    if request.method == "POST":
        if data_form.is_valid() and geom_form.is_valid():
            generated_identifier = data_form.cleaned_data.get("identifier", None)
            acc = data_form.save(request.user, geom_form)
            if generated_identifier != acc.identifier:
                messages.info(
                    request,
                    IDENTIFIER_REPLACED.format(
                        generated_identifier,
                        acc.identifier
                    )
                )
            messages.success(request, _("Eco-Account {} added").format(acc.identifier))
            return redirect("compensation:acc:detail", id=acc.id)
        else:
            messages.error(request, FORM_INVALID, extra_tags="danger",)
    else:
        # For clarification: nothing in this case
        pass
    context = {
        "form": data_form,
        "geom_form": geom_form,
        TAB_TITLE_IDENTIFIER: _("New Eco-Account"),
    }
    context = BaseContext(request, context).context
    return render(request, template, context)


@login_required
@default_group_required
def new_id_view(request: HttpRequest):
    """ JSON endpoint

    Provides fetching of free identifiers for e.g. AJAX calls

    """
    tmp = EcoAccount()
    identifier = tmp.generate_new_identifier()
    while EcoAccount.objects.filter(identifier=identifier).exists():
        identifier = tmp.generate_new_identifier()
    return JsonResponse(
        data={
            "gen_data": identifier
        }
    )


@login_required
@default_group_required
@shared_access_required(EcoAccount, "id")
def edit_view(request: HttpRequest, id: str):
    """
    Renders a view for editing compensations

    Args:
        request (HttpRequest): The incoming request

    Returns:

    """
    template = "compensation/form/view.html"
    # Get object from db
    acc = get_object_or_404(EcoAccount, id=id)
    if acc.is_recorded:
        messages.info(
            request,
            RECORDED_BLOCKS_EDIT
        )
        return redirect("compensation:acc:detail", id=id)

    # Create forms, initialize with values from db/from POST request
    data_form = EditEcoAccountForm(request.POST or None, instance=acc)
    geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=acc)
    if request.method == "POST":
        if data_form.is_valid() and geom_form.is_valid():
            # The data form takes the geom form for processing, as well as the performing user
            acc = data_form.save(request.user, geom_form)
            messages.success(request, _("Eco-Account {} edited").format(acc.identifier))
            return redirect("compensation:acc:detail", id=acc.id)
        else:
            messages.error(request, FORM_INVALID, extra_tags="danger",)
    else:
        # For clarification: nothing in this case
        pass
    context = {
        "form": data_form,
        "geom_form": geom_form,
        TAB_TITLE_IDENTIFIER: _("Edit {}").format(acc.identifier),
    }
    context = BaseContext(request, context).context
    return render(request, template, context)


@login_required
@any_group_check
def detail_view(request: HttpRequest, id: str):
    """ Renders a detail view for a compensation

    Args:
        request (HttpRequest): The incoming request
        id (str): The compensation's id

    Returns:

    """
    template = "compensation/detail/eco_account/view.html"
    acc = get_object_or_404(
        EcoAccount.objects.prefetch_related(
            "deadlines",
        ).select_related(
            'geometry',
            'responsible',
        ),
        id=id,
        deleted=None,
    )
    geom_form = SimpleGeomForm(instance=acc)
    parcels = acc.get_underlying_parcels()
    _user = request.user
    is_data_shared = acc.is_shared_with(_user)

    # Order states according to surface
    before_states = acc.before_states.order_by("-surface")
    after_states = acc.after_states.order_by("-surface")

    # Precalculate logical errors between before- and after-states
    # Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
    sum_before_states = before_states.aggregate(Sum("surface"))["surface__sum"] or 0
    sum_after_states = after_states.aggregate(Sum("surface"))["surface__sum"] or 0
    diff_states = abs(sum_before_states - sum_after_states)
    # Calculate rest of available surface for deductions
    available_total = acc.deductable_rest
    available_relative = acc.get_deductable_rest_relative()

    # Prefetch related data to decrease the amount of db connections
    deductions = acc.deductions.filter(
        intervention__deleted=None,
    )
    actions = acc.actions.all()

    request = acc.set_status_messages(request)

    requesting_user_is_only_shared_user = acc.is_only_shared_with(_user)
    if requesting_user_is_only_shared_user:
        messages.info(
            request,
            DO_NOT_FORGET_TO_SHARE
        )

    context = {
        "obj": acc,
        "geom_form": geom_form,
        "parcels": parcels,
        "has_access": is_data_shared,
        "before_states": before_states,
        "after_states": after_states,
        "sum_before_states": sum_before_states,
        "sum_after_states": sum_after_states,
        "diff_states": diff_states,
        "available": available_relative,
        "available_total": available_total,
        "is_default_member": in_group(_user, DEFAULT_GROUP),
        "is_zb_member": in_group(_user, ZB_GROUP),
        "is_ets_member": in_group(_user, ETS_GROUP),
        "LANIS_LINK": acc.get_LANIS_link(),
        "deductions": deductions,
        "actions": actions,
        TAB_TITLE_IDENTIFIER: f"{acc.identifier} - {acc.title}",
        "has_finished_deadlines": acc.get_finished_deadlines().exists(),
    }
    context = BaseContext(request, context).context
    return render(request, template, context)


@login_required_modal
@login_required
@default_group_required
@shared_access_required(EcoAccount, "id")
def remove_view(request: HttpRequest, id: str):
    """ Renders a modal view for removing the eco account

    Args:
        request (HttpRequest): The incoming request
        id (str): The account's id

    Returns:

    """
    acc = get_object_or_404(EcoAccount, id=id)

    # If the eco account has already been recorded OR there are already deductions, it can not be deleted by a regular
    # default group user
    if acc.recorded is not None or acc.deductions.exists():
        user = request.user
        if not in_group(user, ETS_GROUP):
            messages.info(request, CANCEL_ACC_RECORDED_OR_DEDUCTED)
            return redirect("compensation:acc:detail", id=id)

    form = RemoveEcoAccountModalForm(request.POST or None, instance=acc, request=request)
    return form.process_request(
        request=request,
        msg_success=_("Eco-account removed"),
        redirect_url=reverse("compensation:acc:index"),
    )

