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

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

from compensation.forms.forms import NewEcoAccountForm, EditEcoAccountForm
from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm
from compensation.models import EcoAccount, EcoAccountDocument
from compensation.tables import EcoAccountTable
from intervention.forms.modalForms import NewDeductionModalForm
from konova.contexts import BaseContext
from konova.decorators import any_group_check, default_group_required, conservation_office_group_required
from konova.forms import RemoveModalForm, SimpleGeomForm, NewDocumentForm, RecordModalForm
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
from konova.utils.documents import get_document, remove_document
from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID
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"
    user = request.user
    eco_accounts = EcoAccount.objects.filter(
        deleted=None,
    )
    table = EcoAccountTable(
        request=request,
        queryset=eco_accounts
    )
    context = {
        "table": table,
    }
    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-open", id=acc.id)
        else:
            messages.error(request, FORM_INVALID)
    else:
        # For clarification: nothing in this case
        pass
    context = {
        "form": data_form,
        "geom_form": geom_form,
    }
    context = BaseContext(request, context).context
    return render(request, template, context)


@login_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={
            "identifier": identifier
        }
    )


@login_required
@default_group_required
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)
    # 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-open", id=acc.id)
        else:
            messages.error(request, FORM_INVALID)
    else:
        # For clarification: nothing in this case
        pass
    context = {
        "form": data_form,
        "geom_form": geom_form,
    }
    context = BaseContext(request, context).context
    return render(request, template, context)


@login_required
@any_group_check
def open_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, id=id)
    geom_form = SimpleGeomForm(instance=acc)
    _user = request.user
    is_data_shared = acc.is_shared_with(_user)

    # Order states according to surface
    before_states = acc.before_states.all().order_by("-surface")
    after_states = acc.after_states.all().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, available_relative = acc.get_available_rest()

    deductions = acc.deductions.filter(
        intervention__deleted=None,
    )

    context = {
        "obj": acc,
        "geom_form": geom_form,
        "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,
    }
    context = BaseContext(request, context).context
    return render(request, template, context)


@login_required
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)
    form = RemoveModalForm(request.POST or None, instance=acc, user=request.user)
    return form.process_request(
        request=request,
        msg_success=_("Eco-account removed"),
        redirect_url=reverse("compensation:acc-index"),
    )


@login_required
@default_group_required
def deduction_remove_view(request: HttpRequest, id: str, deduction_id: str):
    """ Renders a modal view for removing deductions

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

    Returns:

    """
    acc = get_object_or_404(EcoAccount, id=id)
    try:
        eco_deduction = acc.deductions.get(id=deduction_id)
    except ObjectDoesNotExist:
        raise Http404("Unknown deduction")

    form = RemoveModalForm(request.POST or None, instance=eco_deduction, user=request.user)
    return form.process_request(
        request=request,
        msg_success=_("Deduction removed")
    )


@login_required
@default_group_required
def log_view(request: HttpRequest, id: str):
    """ Renders a log view using modal

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

    Returns:

    """
    comp = get_object_or_404(EcoAccount, id=id)
    template = "modal/modal_generic.html"
    body_template = "log.html"

    context = {
        "modal_body_template": body_template,
        "log": comp.log.all(),
        "modal_title": _("Log"),
    }
    context = BaseContext(request, context).context
    return render(request, template, context)


@login_required
@conservation_office_group_required
def record_view(request: HttpRequest, id:str):
    """ Renders a modal form for recording an eco account

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

    Returns:

    """
    acc = get_object_or_404(EcoAccount, id=id)
    form = RecordModalForm(request.POST or None, instance=acc, user=request.user)
    msg_succ = _("{} unrecorded") if acc.recorded else _("{} recorded")
    msg_succ = msg_succ.format(acc.identifier)
    return form.process_request(
        request,
        msg_succ
    )


@login_required
def state_new_view(request: HttpRequest, id: str):
    """ Renders a form for adding new states for an eco account

    Args:
        request (HttpRequest): The incoming request
        id (str): The account's id to which the new state will be related

    Returns:

    """
    acc = get_object_or_404(EcoAccount, id=id)
    form = NewStateModalForm(request.POST or None, instance=acc, user=request.user)
    return form.process_request(
        request,
        msg_success=_("State added")
    )


@login_required
def action_new_view(request: HttpRequest, id: str):
    """ Renders a form for adding new actions for an eco account

    Args:
        request (HttpRequest): The incoming request
        id (str): The account's id to which the new state will be related

    Returns:

    """
    acc = get_object_or_404(EcoAccount, id=id)
    form = NewActionModalForm(request.POST or None, instance=acc, user=request.user)
    return form.process_request(
        request,
        msg_success=_("Action added")
    )


@login_required
def deadline_new_view(request: HttpRequest, id: str):
    """ Renders a form for adding new states for an eco account

    Args:
        request (HttpRequest): The incoming request
        id (str): The account's id to which the new state will be related

    Returns:

    """
    acc = get_object_or_404(EcoAccount, id=id)
    form = NewDeadlineModalForm(request.POST or None, instance=acc, user=request.user)
    return form.process_request(
        request,
        msg_success=_("Deadline added")
    )


@login_required
def new_document_view(request: HttpRequest, id: str):
    """ Renders a form for uploading new documents

    Args:
        request (HttpRequest): The incoming request
        id (str): The account's id to which the new document will be related
    Returns:

    """
    acc = get_object_or_404(EcoAccount, id=id)
    form = NewDocumentForm(request.POST or None, request.FILES or None, instance=acc, user=request.user)
    return form.process_request(
        request,
        msg_success=_("Document added")
    )


@login_required
def get_document_view(request: HttpRequest, doc_id: str):
    """ Returns the document as downloadable file

    Wraps the generic document fetcher function from konova.utils.

    Args:
        request (HttpRequest): The incoming request
        doc_id (str): The document id

    Returns:

    """
    doc = get_object_or_404(EcoAccountDocument, id=doc_id)
    return get_document(doc)


@login_required
def remove_document_view(request: HttpRequest, doc_id: str):
    """ Removes the document from the database and file system

    Wraps the generic functionality from konova.utils.

    Args:
        request (HttpRequest): The incoming request
        doc_id (str): The document id

    Returns:

    """
    doc = get_object_or_404(EcoAccountDocument, id=doc_id)
    return remove_document(
        request,
        doc
    )


@login_required
@default_group_required
def new_deduction_view(request: HttpRequest, id: str):
    """ Renders a modal form view for creating deductions

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

    Returns:

    """
    acc = get_object_or_404(EcoAccount, id=id)
    form = NewDeductionModalForm(request.POST or None, instance=acc, user=request.user)
    return form.process_request(
        request,
        msg_success=_("Deduction added")
    )