"""Explicit, scoped Admin release/handover/closure; no editable evidence forms."""
from django import forms
from django.contrib import admin, messages
from django.core import signing
from django.core.exceptions import PermissionDenied, ValidationError
from django.db.models import Q
from django.http import Http404, HttpResponseRedirect
from django.template.response import TemplateResponse
from django.urls import path, reverse
from django.utils.html import format_html

from . import handover_queries as queries, handover_services as services
from .quality_control_queries import latest_completed_quality_control
from .models import ServiceCase, ServiceCaseDeliveryRelease, ServiceCaseHandover, ServiceCaseHandoverAccessory, ServiceCaseClosure, NO_CREDENTIALS
from .handover_models import PRIVACY
from .repair_admin import RepairHistoryAdmin

SALT = "service-handover-revision"


def visible_cases(actor):
    return ServiceCase.objects.filter(Q(pk__in=queries.authorized_delivery_cases(actor=actor).values("pk")) |
        Q(pk__in=queries.authorized_delivery_cases(actor=actor, permission=queries.CLOSURE_PERMISSION).values("pk")))


class HandoverForm(forms.Form):
    operation = forms.ChoiceField(choices=[("ready", "Mark ready for delivery"), ("handover", "Record physical handover"), ("close", "Close delivered case")])
    recipient_type = forms.ChoiceField(choices=[("", "---------"), *ServiceCaseHandover.Recipient.choices], required=False)
    recipient_name = forms.CharField(max_length=200, required=False, help_text="For Customer collection, leave blank to confirm the displayed current Customer name.")
    recipient_mobile = forms.CharField(max_length=32, required=False, help_text="For Customer collection, leave blank to use the displayed current Customer mobile.")
    recipient_relationship = forms.CharField(max_length=200, required=False)
    verification_method = forms.ChoiceField(choices=[("", "---------"), *ServiceCaseHandover.Verification.choices], required=False)
    verification_reference = forms.CharField(max_length=120, required=False, help_text=PRIVACY)
    recipient_identity_type = forms.ChoiceField(choices=[("", "---------"), *ServiceCaseHandover.Identity.choices], required=False)
    recipient_identity_reference = forms.CharField(max_length=64, required=False, help_text=PRIVACY)
    device_confirmed = forms.BooleanField(required=False, label="I confirmed this physical Device and its displayed identifiers")
    verification_confirmed = forms.BooleanField(required=False, label="I verified the recipient using the selected method")
    physical_handover_confirmed = forms.BooleanField(required=False, label="The recipient accepted physical custody of this Device")
    note = forms.CharField(max_length=2000, required=False, widget=forms.Textarea, help_text=NO_CREDENTIALS)
    revision = forms.CharField(widget=forms.HiddenInput)

    def __init__(self, *args, case, **kwargs):
        super().__init__(*args, **kwargs)
        self.case = case
        self.customer = case.customer
        self.device = case.device
        self.device_identity = queries.device_identity_fingerprint(self.device)
        self.accessories = list(case.accessories.order_by("pk"))
        operation = {"QC_PASSED": "ready", "READY_FOR_DELIVERY": "handover", "DELIVERED": "close"}.get(case.status)
        self.fields["operation"].choices = [choice for choice in self.fields["operation"].choices if choice[0] == operation]
        self.initial["operation"] = operation
        for accessory in self.accessories:
            if operation == "handover":
                self.fields[f"returned_{accessory.pk.hex}"] = forms.IntegerField(min_value=0, max_value=accessory.quantity,
                    label=f"Returned {accessory.get_accessory_type_display()} / {accessory.description} (received {accessory.quantity})")
                self.fields[f"condition_{accessory.pk.hex}"] = forms.CharField(max_length=2000, required=False,
                    label=f"Condition / shortage explanation for {accessory.get_accessory_type_display()}", help_text=NO_CREDENTIALS)
        self.initial["revision"] = signing.dumps(self.facts(), salt=SALT)

    def facts(self):
        return [str(self.case.pk), self.case.updated_at.isoformat(), str(self.device.pk), self.device_identity,
            self.customer.updated_at.isoformat(), [[str(a.pk), a.updated_at.isoformat(), a.quantity, a.description] for a in self.accessories]]

    def clean(self):
        data = super().clean()
        try:
            facts = signing.loads(data.get("revision", ""), salt=SALT)
        except signing.BadSignature as error:
            raise ValidationError("Reload the handover form.") from error
        if facts != self.facts():
            raise ValidationError("Delivery details changed; reload and review them.")
        required = {"ready": ("device_confirmed",), "handover": ("device_confirmed", "verification_confirmed", "physical_handover_confirmed", "recipient_type", "verification_method")}
        for name in required.get(data.get("operation"), ()):
            if not data.get(name):
                self.add_error(name, "Explicit confirmation is required.")
        return data


class HandoverWorkflowMixin:
    def get_urls(self):
        return [path("<path:object_id>/handover/", self.admin_site.admin_view(self.handover_view),
            name="service_servicecase_handover")] + super().get_urls()

    @admin.display(description="Delivery and closure")
    def handover_workflow(self, obj):
        return format_html('<a href="{}">Delivery preparation, handover and closure</a>', reverse("admin:service_servicecase_handover", args=[obj.pk]))

    def handover_view(self, request, object_id):
        case = self.get_object(request, object_id)
        if case is None:
            raise Http404
        if not self.has_view_permission(request, case) or not visible_cases(request.user).filter(pk=case.pk).exists():
            raise PermissionDenied
        can_change = request.user.has_perm("service.change_servicecase") and case.status in ("QC_PASSED", "READY_FOR_DELIVERY", "DELIVERED")
        if request.method == "POST" and not can_change:
            raise PermissionDenied
        form = HandoverForm(request.POST if request.method == "POST" else None, case=case)
        if request.method == "POST" and form.is_valid():
            data = form.cleaned_data
            try:
                args = dict(service_case=case, actor=request.user, note=data["note"], expected_updated_at=case.updated_at)
                if data["operation"] == "close":
                    services.close_service_case(**args)
                else:
                    args.update(expected_device=form.device, expected_device_identity=form.device_identity)
                    if data["operation"] == "ready":
                        services.mark_service_case_ready_for_delivery(**args)
                    else:
                        services.handover_service_case(**args, expected_customer_updated_at=form.customer.updated_at,
                            accessories=[dict(intake_accessory=a, returned_quantity=data[f"returned_{a.pk.hex}"], condition_note=data[f"condition_{a.pk.hex}"])
                                for a in form.accessories],
                            **{name: data[name] for name in ("recipient_type", "recipient_name", "recipient_mobile", "recipient_relationship",
                                "recipient_identity_type", "recipient_identity_reference", "verification_method", "verification_reference")})
            except ValidationError as error:
                form.add_error(None, ValidationError(error.messages))
            else:
                self.log_change(request, case, "Recorded a delivery/closure workflow operation.")
                self.message_user(request, "Delivery workflow updated.", messages.SUCCESS)
                return HttpResponseRedirect(request.path)
        handover = queries.get_service_case_handover(case)
        return TemplateResponse(request, "admin/service/handover.html", {**self.admin_site.each_context(request), "opts": self.model._meta,
            "title": "Delivery preparation, handover and closure", "case": case, "form": form, "can_change": can_change,
            "qc": latest_completed_quality_control(case), "release": queries.get_service_case_delivery_release(case),
            "handover": handover, "returned": queries.handover_accessories(handover), "closure": queries.get_service_case_closure(case),
            "identifiers": case.device.identifiers.filter(is_active=True)})


class HandoverHistoryAdmin(RepairHistoryAdmin):
    def get_queryset(self, request):
        key = "handover__service_case_id__in" if self.model is ServiceCaseHandoverAccessory else "service_case_id__in"
        return super().get_queryset(request).filter(**{key: visible_cases(request.user).values("pk")})


@admin.register(ServiceCaseDeliveryRelease)
class DeliveryReleaseAdmin(HandoverHistoryAdmin):
    list_display = ("service_case", "quality_control", "readied_by", "readied_at")
    list_select_related = ("service_case", "quality_control", "readied_by")


@admin.register(ServiceCaseHandover)
class HandoverAdmin(HandoverHistoryAdmin):
    list_display = ("service_case", "recipient_type", "handed_over_by", "delivered_at")
    list_select_related = ("service_case", "handed_over_by")


@admin.register(ServiceCaseHandoverAccessory)
class HandoverAccessoryAdmin(HandoverHistoryAdmin):
    list_display = ("handover", "intake_accessory", "returned_quantity")
    list_select_related = ("handover", "intake_accessory")


@admin.register(ServiceCaseClosure)
class ClosureAdmin(HandoverHistoryAdmin):
    list_display = ("service_case", "closed_by", "closed_at")
    list_select_related = ("service_case", "closed_by")
