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 render, get_object_or_404, redirect
from django.urls import reverse
from django.utils.translation import gettext_lazy as _

from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm
from compensation.models import CompensationAction, CompensationState
from ema.forms import NewEmaForm, EditEmaForm, NewEmaDocumentForm
from ema.tables import EmaTable
from intervention.forms.modalForms import ShareModalForm
from konova.contexts import BaseContext
from konova.decorators import conservation_office_group_required, shared_access_required
from ema.models import Ema, EmaDocument
from konova.forms import RemoveModalForm, SimpleGeomForm, RecordModalForm, DeadlineRemoveModalForm
from konova.models import Deadline
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.documents import get_document, remove_document
from konova.utils.generators import generate_qr_code
from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID, DATA_UNSHARED, DATA_UNSHARED_EXPLANATION, \
    DOCUMENT_ADDED, COMPENSATION_STATE_REMOVED, COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, \
    COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED
from konova.utils.user_checks import in_group


@login_required
def index_view(request: HttpRequest):
    """ Renders the index view for EMAs

    Args:
        request (HttpRequest): The incoming request

    Returns:

    """
    template = "generic_index.html"
    emas = Ema.objects.filter(
        deleted=None,
    ).order_by(
        "-modified"
    )
    table = EmaTable(
        request,
        queryset=emas
    )
    context = {
        "table": table,
        TAB_TITLE_IDENTIFIER: _("EMAs - Overview"),
    }
    context = BaseContext(request, context).context
    return render(request, template, context)


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

    Args:
        request (HttpRequest): The incoming request

    Returns:

    """
    template = "ema/form/view.html"
    data_form = NewEmaForm(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)
            ema = data_form.save(request.user, geom_form)
            if generated_identifier != ema.identifier:
                messages.info(
                    request,
                    IDENTIFIER_REPLACED.format(
                        generated_identifier,
                        ema.identifier
                    )
                )
            messages.success(request, _("EMA {} added").format(ema.identifier))
            return redirect("ema:detail", id=ema.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 EMA"),
    }
    context = BaseContext(request, context).context
    return render(request, template, context)


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

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

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


@login_required
def detail_view(request: HttpRequest, id: str):
    """ Renders the detail view of an EMA

    Args:
        request (HttpRequest): The incoming request
        id (str): The EMA id

    Returns:

    """
    template = "ema/detail/view.html"
    ema = get_object_or_404(Ema, id=id, deleted=None)

    geom_form = SimpleGeomForm(instance=ema)
    parcels = ema.get_underlying_parcels()
    _user = request.user
    is_data_shared = ema.is_shared_with(_user)

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

    ema.set_status_messages(request)

    context = {
        "obj": ema,
        "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,
        "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": ema.get_LANIS_link(),
        TAB_TITLE_IDENTIFIER: f"{ema.identifier} - {ema.title}",
    }
    context = BaseContext(request, context).context
    return render(request, template, context)


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

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

    Returns:

    """
    ema = get_object_or_404(Ema, id=id)
    template = "modal/modal_generic.html"
    body_template = "log.html"

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


@login_required
@conservation_office_group_required
@shared_access_required(Ema, "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
    ema = get_object_or_404(Ema, id=id)
    # Create forms, initialize with values from db/from POST request
    data_form = EditEmaForm(request.POST or None, instance=ema)
    geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=ema)
    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
            ema = data_form.save(request.user, geom_form)
            messages.success(request, _("EMA {} edited").format(ema.identifier))
            return redirect("ema:detail", id=ema.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(ema.identifier),
    }
    context = BaseContext(request, context).context
    return render(request, template, context)


@login_required
@conservation_office_group_required
@shared_access_required(Ema, "id")
def remove_view(request: HttpRequest, id: str):
    """ Renders a modal view for removing the EMA

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

    Returns:

    """
    ema = get_object_or_404(Ema, id=id)
    form = RemoveModalForm(request.POST or None, instance=ema, request=request)
    return form.process_request(
        request=request,
        msg_success=_("EMA removed"),
        redirect_url=reverse("ema:index"),
    )


@login_required
@conservation_office_group_required
@shared_access_required(Ema, "id")
def record_view(request: HttpRequest, id: str):
    """ Renders a modal view for recording the EMA

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

    Returns:

    """
    ema = get_object_or_404(Ema, id=id)
    msg_succ = _("{} unrecorded") if ema.recorded else _("{} recorded")
    form = RecordModalForm(request.POST or None, instance=ema, request=request)
    return form.process_request(
        request=request,
        msg_success=msg_succ.format("EMA"),
    )


@login_required
@conservation_office_group_required
@shared_access_required(Ema, "id")
def state_new_view(request: HttpRequest, id: str):
    """ Renders a form for adding new states for an EMA

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

    Returns:

    """
    ema = get_object_or_404(Ema, id=id)
    form = NewStateModalForm(request.POST or None, instance=ema, request=request)
    return form.process_request(
        request,
        msg_success=COMPENSATION_STATE_ADDED,
        redirect_url=reverse("ema:detail", args=(id,)) + "#related_data"
    )


@login_required
@conservation_office_group_required
@shared_access_required(Ema, "id")
def action_new_view(request: HttpRequest, id: str):
    """ Renders a form for adding new actions for an EMA

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

    Returns:

    """
    ema = get_object_or_404(Ema, id=id)
    form = NewActionModalForm(request.POST or None, instance=ema, request=request)
    return form.process_request(
        request,
        msg_success=COMPENSATION_ACTION_ADDED,
        redirect_url=reverse("ema:detail", args=(id,)) + "#related_data"
    )


@login_required
@conservation_office_group_required
@shared_access_required(Ema, "id")
def deadline_new_view(request: HttpRequest, id: str):
    """ Renders a form for adding new states for an EMA

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

    Returns:

    """
    ema = get_object_or_404(Ema, id=id)
    form = NewDeadlineModalForm(request.POST or None, instance=ema, request=request)
    return form.process_request(
        request,
        msg_success=DEADLINE_ADDED,
        redirect_url=reverse("ema:detail", args=(id,)) + "#related_data"
    )


@login_required
@conservation_office_group_required
@shared_access_required(Ema, "id")
def document_new_view(request: HttpRequest, id: str):
    """ Renders a form for uploading new documents

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

    """
    ema = get_object_or_404(Ema, id=id)
    form = NewEmaDocumentForm(request.POST or None, request.FILES or None, instance=ema, request=request)
    return form.process_request(
        request,
        msg_success=DOCUMENT_ADDED,
        redirect_url=reverse("ema:detail", args=(id,)) + "#related_data"
    )


@login_required
@conservation_office_group_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(EmaDocument, id=doc_id)
    user = request.user
    instance = doc.instance
    # File download only possible if related instance is shared with user
    if not instance.users.filter(id=user.id):
        messages.info(
            request,
            DATA_UNSHARED
        )
        return redirect("ema:detail", id=instance.id)
    return get_document(doc)


@login_required
@conservation_office_group_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(EmaDocument, id=doc_id)
    return remove_document(
        request,
        doc
    )


@login_required
@conservation_office_group_required
@shared_access_required(Ema, "id")
def state_remove_view(request: HttpRequest, id: str, state_id: str):
    """ Renders a form for removing an EMA state

    Args:
        request (HttpRequest): The incoming request
        id (str): The ema id
        state_id (str): The state's id

    Returns:

    """
    state = get_object_or_404(CompensationState, id=state_id)
    form = RemoveModalForm(request.POST or None, instance=state, request=request)
    return form.process_request(
        request,
        msg_success=COMPENSATION_STATE_REMOVED,
        redirect_url=reverse("ema:detail", args=(id,)) + "#related_data"
    )


@login_required
@conservation_office_group_required
@shared_access_required(Ema, "id")
def action_remove_view(request: HttpRequest, id: str, action_id: str):
    """ Renders a form for removing an EMA action

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

    Returns:

    """
    action = get_object_or_404(CompensationAction, id=action_id)
    form = RemoveModalForm(request.POST or None, instance=action, request=request)
    return form.process_request(
        request,
        msg_success=COMPENSATION_ACTION_REMOVED,
        redirect_url=reverse("ema:detail", args=(id,)) + "#related_data"
    )


def report_view(request:HttpRequest, id: str):
    """ Renders the public report view

    Args:
        request (HttpRequest): The incoming request
        id (str): The id of the intervention

    Returns:

    """
    # Reuse the compensation report template since EMAs are structurally identical
    template = "ema/report/report.html"
    ema = get_object_or_404(Ema, id=id)

    tab_title = _("Report {}").format(ema.identifier)
    # If intervention is not recorded (yet or currently) we need to render another template without any data
    if not ema.is_ready_for_publish():
        template = "report/unavailable.html"
        context = {
            TAB_TITLE_IDENTIFIER: tab_title,
        }
        context = BaseContext(request, context).context
        return render(request, template, context)

    # Prepare data for map viewer
    geom_form = SimpleGeomForm(
        instance=ema,
    )
    parcels = ema.get_underlying_parcels()
    qrcode_img = generate_qr_code(
        request.build_absolute_uri(reverse("ema:report", args=(id,))),
        10
    )
    qrcode_img_lanis = generate_qr_code(
        ema.get_LANIS_link(),
        7
    )
    # Order states by surface
    before_states = ema.before_states.all().order_by("-surface").prefetch_related("biotope_type")
    after_states = ema.after_states.all().order_by("-surface").prefetch_related("biotope_type")
    actions = ema.actions.all().prefetch_related("action_type")

    context = {
        "obj": ema,
        "qrcode": qrcode_img,
        "qrcode_lanis": qrcode_img_lanis,
        "has_access": False,  # disables action buttons during rendering
        "before_states": before_states,
        "after_states": after_states,
        "geom_form": geom_form,
        "parcels": parcels,
        "actions": actions,
        TAB_TITLE_IDENTIFIER: tab_title,
    }
    context = BaseContext(request, context).context
    return render(request, template, context)


@login_required
def share_view(request: HttpRequest, id: str, token: str):
    """ Performs sharing of an ema

    If token given in url is not valid, the user will be redirected to the dashboard

    Args:
        request (HttpRequest): The incoming request
        id (str): EMA's id
        token (str): Access token for EMA

    Returns:

    """
    user = request.user
    obj = get_object_or_404(Ema, id=id)
    # Check tokens
    if obj.access_token == token:
        # Send different messages in case user has already been added to list of sharing users
        if obj.is_shared_with(user):
            messages.info(
                request,
                _("{} has already been shared with you").format(obj.identifier)
            )
        else:
            messages.success(
                request,
                _("{} has been shared with you").format(obj.identifier)
            )
            obj.share_with(user)
        return redirect("ema:detail", id=id)
    else:
        messages.error(
            request,
            _("Share link invalid"),
            extra_tags="danger",
        )
        return redirect("home")


@login_required
@conservation_office_group_required
@shared_access_required(Ema, "id")
def create_share_view(request: HttpRequest, id: str):
    """ Renders sharing form for an Ema

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

    Returns:

    """
    obj = get_object_or_404(Ema, id=id)
    form = ShareModalForm(request.POST or None, instance=obj, request=request)
    return form.process_request(
        request,
        msg_success=_("Share settings updated")
    )


@login_required
@conservation_office_group_required
@shared_access_required(Ema, "id")
def deadline_remove_view(request: HttpRequest, id: str, deadline_id: str):
    """ Renders a form for removing deadlines from a compensation

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

    Returns:

    """
    ema = get_object_or_404(Ema, id=id)
    deadline = get_object_or_404(Deadline, id=deadline_id)
    form = DeadlineRemoveModalForm(request.POST or None, instance=ema, deadline=deadline, request=request)
    return form.process_request(
        request,
        msg_success=DEADLINE_REMOVED,
        redirect_url=reverse("ema:detail", args=(id,)) + "#related_data"
    )