"""authentik Flow administration"""
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.mixins import (
    PermissionRequiredMixin as DjangoPermissionRequiredMixin,
)
from django.contrib.messages.views import SuccessMessageMixin
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.urls import reverse_lazy
from django.utils.translation import gettext as _
from django.views.generic import DetailView, FormView, UpdateView
from guardian.mixins import PermissionRequiredMixin

from authentik.admin.views.utils import DeleteMessageView
from authentik.flows.exceptions import FlowNonApplicableException
from authentik.flows.forms import FlowForm, FlowImportForm
from authentik.flows.models import Flow
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
from authentik.flows.transfer.common import DataclassEncoder
from authentik.flows.transfer.exporter import FlowExporter
from authentik.flows.transfer.importer import FlowImporter
from authentik.flows.views import SESSION_KEY_PLAN, FlowPlanner
from authentik.lib.utils.urls import redirect_with_qs
from authentik.lib.views import CreateAssignPermView, bad_request_message


class FlowCreateView(
    SuccessMessageMixin,
    LoginRequiredMixin,
    DjangoPermissionRequiredMixin,
    CreateAssignPermView,
):
    """Create new Flow"""

    model = Flow
    form_class = FlowForm
    permission_required = "authentik_flows.add_flow"

    template_name = "generic/create.html"
    success_url = reverse_lazy("authentik_core:shell")
    success_message = _("Successfully created Flow")


class FlowUpdateView(
    SuccessMessageMixin,
    LoginRequiredMixin,
    PermissionRequiredMixin,
    UpdateView,
):
    """Update flow"""

    model = Flow
    form_class = FlowForm
    permission_required = "authentik_flows.change_flow"

    template_name = "generic/update.html"
    success_url = reverse_lazy("authentik_core:shell")
    success_message = _("Successfully updated Flow")


class FlowDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessageView):
    """Delete flow"""

    model = Flow
    permission_required = "authentik_flows.delete_flow"

    template_name = "generic/delete.html"
    success_url = reverse_lazy("authentik_core:shell")
    success_message = _("Successfully deleted Flow")


class FlowDebugExecuteView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
    """Debug exectue flow, setting the current user as pending user"""

    model = Flow
    permission_required = "authentik_flows.view_flow"

    # pylint: disable=unused-argument
    def get(self, request: HttpRequest, pk: str) -> HttpResponse:
        """Debug exectue flow, setting the current user as pending user"""
        flow: Flow = self.get_object()
        planner = FlowPlanner(flow)
        planner.use_cache = False
        try:
            plan = planner.plan(self.request, {PLAN_CONTEXT_PENDING_USER: request.user})
            self.request.session[SESSION_KEY_PLAN] = plan
        except FlowNonApplicableException as exc:
            return bad_request_message(
                request,
                _(
                    "Flow not applicable to current user/request: %(messages)s"
                    % {"messages": str(exc)}
                ),
            )
        return redirect_with_qs(
            "authentik_flows:flow-executor-shell",
            self.request.GET,
            flow_slug=flow.slug,
        )


class FlowImportView(LoginRequiredMixin, FormView):
    """Import flow from JSON Export; only allowed for superusers
    as these flows can contain python code"""

    form_class = FlowImportForm
    template_name = "administration/flow/import.html"
    success_url = reverse_lazy("authentik_core:shell")

    def dispatch(self, request, *args, **kwargs):
        if not request.user.is_superuser:
            return self.handle_no_permission()
        return super().dispatch(request, *args, **kwargs)

    def form_valid(self, form: FlowImportForm) -> HttpResponse:
        importer = FlowImporter(form.cleaned_data["flow"].read().decode())
        successful = importer.apply()
        if not successful:
            messages.error(self.request, _("Failed to import flow."))
        else:
            messages.success(self.request, _("Successfully imported flow."))
        return super().form_valid(form)


class FlowExportView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
    """Export Flow"""

    model = Flow
    permission_required = "authentik_flows.export_flow"

    # pylint: disable=unused-argument
    def get(self, request: HttpRequest, pk: str) -> HttpResponse:
        """Debug exectue flow, setting the current user as pending user"""
        flow: Flow = self.get_object()
        exporter = FlowExporter(flow)
        response = JsonResponse(exporter.export(), encoder=DataclassEncoder, safe=False)
        response["Content-Disposition"] = f'attachment; filename="{flow.slug}.akflow"'
        return response
