"""Administrative intake via services; protected history is audit-only."""
from django import forms
from django.contrib import admin, messages
from django.core import signing
from django.core.exceptions import ValidationError
from django.http import HttpResponseRedirect

from apps.customers.models import Customer
from apps.devices.models import Device
from . import services
from .engineer_admin import EngineerWorkflowMixin
from .diagnostic_admin import DiagnosisWorkflowMixin
from .repair_admin import RepairWorkflowMixin
from .quality_control_admin import QualityControlWorkflowMixin
from .handover_admin import HandoverWorkflowMixin
from .models import (ServiceCase, ServiceCaseComplaint, ServiceCaseIntakeCondition,
                     ServiceCaseAccessory, ServiceCaseWarrantySnapshot)


class ContextChoice(forms.ModelChoiceField):
    def label_from_instance(self, obj):
        if isinstance(obj, Customer):
            return f"{obj.company} / {obj.customer_number} / {obj.display_name}"
        return f"{obj.company} / {obj.service_center} / {obj.job_number}"


class ServiceCaseForm(forms.ModelForm):
    revision = forms.CharField(required=False, widget=forms.HiddenInput)
    customer = ContextChoice(queryset=Customer.objects.select_related("company"))
    device = forms.ModelChoiceField(queryset=Device.objects.select_related("product_model__brand"))

    class Meta:
        model = ServiceCase
        fields = ("company", "service_center", "customer", "device", "intake_channel", "received_at", "customer_reference", "intake_note")

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if not self.instance._state.adding and "revision" in self.fields:
            self.fields["revision"].required = True
            self.initial["revision"] = signing.dumps([str(self.instance.pk), self.instance.updated_at.isoformat()], salt="service-intake-revision")

    def clean(self):
        data = super().clean()
        if not self.instance._state.adding:
            try:
                supplied = signing.loads(data.get("revision", ""), salt="service-intake-revision")
            except signing.BadSignature as error:
                raise ValidationError("Reload the ServiceCase before editing.") from error
            if supplied != [str(self.instance.pk), self.instance.updated_at.isoformat()]:
                raise ValidationError("ServiceCase changed; reload and review it.")
        return data


class SafeIntakeAdmin(admin.ModelAdmin):
    def has_delete_permission(self, request, obj=None):
        return False

    def changeform_view(self, request, object_id=None, form_url="", extra_context=None):
        try:
            return super().changeform_view(request, object_id, form_url, extra_context)
        except ValidationError:
            self.message_user(request, "Intake operation rejected. Reload and review current state and prerequisites.", messages.ERROR)
            return HttpResponseRedirect(request.path)

    def construct_change_message(self, request, form, formsets, add=False):
        return "Recorded a service intake operation."

    def _copy(self, obj, record):
        for field in obj._meta.concrete_fields:
            setattr(obj, field.attname, getattr(record, field.attname))
        obj._state = record._state


@admin.register(ServiceCase)
class ServiceCaseAdmin(HandoverWorkflowMixin, QualityControlWorkflowMixin, RepairWorkflowMixin, DiagnosisWorkflowMixin, EngineerWorkflowMixin, SafeIntakeAdmin):
    form = ServiceCaseForm
    list_display = ("job_number", "company", "service_center", "customer", "device", "received_at", "status", "assigned_engineer")
    list_filter = ("company", "service_center", "status", "intake_channel")
    list_select_related = ("company", "service_center", "customer", "device__product_model__brand")
    search_fields = ("job_number", "customer__customer_number", "customer_reference", "=device__identifiers__identity_key",
                     "engineer_assignments__engineer__username", "engineer_assignments__engineer__email",
                     "engineer_assignments__engineer__first_name", "engineer_assignments__engineer__last_name")
    actions = ("cancel_selected",)

    def has_change_permission(self, request, obj=None):
        return super().has_change_permission(request, obj) and (obj is None or obj.status in ("RECEIVED", "ASSIGNED", "DIAGNOSING", "DIAGNOSED", "REPAIRING"))

    def get_readonly_fields(self, request, obj=None):
        fields = ("id", "job_number", "status", "created_by", "created_at", "updated_at", "cancelled_at", "cancellation_reason", "diagnosis_workflow", "repair_workflow", "quality_control_workflow", "handover_workflow")
        return fields + (("company", "service_center", "customer", "device", "received_at", "intake_channel", "assigned_engineer", "engineer_workflow", "assignment_history") if obj else ()) + (("customer_reference", "intake_note") if obj and obj.status != "RECEIVED" else ())

    def get_fields(self, request, obj=None):
        fields = ("company", "service_center", "job_number", "customer", "device", "intake_channel", "received_at", "status",
                  "customer_reference", "intake_note", "created_by", "cancelled_at", "cancellation_reason", "id", "created_at", "updated_at")
        return fields + (("assigned_engineer", "engineer_workflow", "assignment_history", "diagnosis_workflow", "repair_workflow", "quality_control_workflow", "handover_workflow") if obj else ()) + (("revision",) if obj and obj.status == "RECEIVED" else ())

    def save_model(self, request, obj, form, change):
        if change:
            record = services.update_service_case_intake(service_case=obj, expected_updated_at=obj.updated_at,
                **{name: form.cleaned_data[name] for name in ("customer_reference", "intake_note") if name in form.changed_data})
        else:
            record = services.create_service_case(created_by=request.user,
                **{name: form.cleaned_data[name] for name in ServiceCaseForm.Meta.fields})
        self._copy(obj, record)

    @admin.action(description="Cancel selected open cases (retain all history)", permissions=["change"])
    def cancel_selected(self, request, queryset):
        for case in queryset.order_by("pk"):
            try:
                services.cancel_service_case(service_case=case, expected_updated_at=case.updated_at, cancelled_by=request.user)
            except ValidationError:
                self.message_user(request, "Case changed; reload before cancellation.", messages.ERROR)


class ChildAdmin(SafeIntakeAdmin):
    list_select_related = ("service_case",)
    list_filter = ("service_case__company", "service_case__service_center")
    search_fields = ("service_case__job_number",)
    actions = None

    def has_change_permission(self, request, obj=None):
        return super().has_change_permission(request, obj) and obj is None

    def get_readonly_fields(self, request, obj=None):
        return tuple(field.name for field in self.model._meta.fields) if obj else ("id", "created_at", "updated_at")

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "service_case":
            return ContextChoice(queryset=ServiceCase.objects.select_related("company", "service_center").filter(status="RECEIVED"))
        return super().formfield_for_foreignkey(db_field, request, **kwargs)


@admin.register(ServiceCaseComplaint)
class ComplaintAdmin(ChildAdmin):
    list_display = ("service_case", "complaint_symptom", "removed_at", "created_at")
    list_select_related = ("service_case", "complaint_symptom")
    actions = ("remove_selected",)

    def get_readonly_fields(self, request, obj=None):
        return super().get_readonly_fields(request, obj) + (("removed_at",) if not obj else ())

    def save_model(self, request, obj, form, change):
        self._copy(obj, services.add_service_case_complaint(service_case=obj.service_case,
            complaint_symptom=obj.complaint_symptom, customer_description=obj.customer_description))

    @admin.action(description="Remove selected complaints from intake (retain history)", permissions=["change"])
    def remove_selected(self, request, queryset):
        for row in queryset.order_by("service_case_id", "pk"):
            try:
                services.remove_service_case_complaint(service_case=row.service_case, complaint=row)
            except ValidationError:
                self.message_user(request, "Complaint removal rejected; review case state.", messages.ERROR)


@admin.register(ServiceCaseIntakeCondition)
class ConditionAdmin(ChildAdmin):
    list_display = ("service_case", "condition_type", "created_at")

    def save_model(self, request, obj, form, change):
        self._copy(obj, services.add_service_case_intake_condition(service_case=obj.service_case,
            condition_type=obj.condition_type, description=obj.description))


@admin.register(ServiceCaseAccessory)
class AccessoryAdmin(ChildAdmin):
    list_display = ("service_case", "accessory_type", "quantity", "created_at")

    def save_model(self, request, obj, form, change):
        self._copy(obj, services.add_service_case_accessory(service_case=obj.service_case,
            accessory_type=obj.accessory_type, description=obj.description, quantity=obj.quantity))


@admin.register(ServiceCaseWarrantySnapshot)
class SnapshotAdmin(ChildAdmin):
    list_display = ("service_case", "recorded_coverage", "coverage_source", "coverage_start_date", "coverage_end_date", "captured_at")

    def has_add_permission(self, request):
        return False

    def has_change_permission(self, request, obj=None):
        return False
