from django import forms
from django.contrib import admin, messages
from django.core.exceptions import ValidationError

from apps.catalog.models import ProductCategory
from . import services
from .models import ComplaintSymptom, ServiceCategory, FaultDiagnosis, RootCause, RepairAction


class TaxonomyAdmin(admin.ModelAdmin):
    list_display = ("code", "name", "is_active")
    search_fields = ("code", "name", "description")
    list_filter = ("is_active",)
    ordering = ("code",)
    readonly_fields = ("id", "created_at", "updated_at")
    actions = ("deactivate_selected", "reactivate_selected")

    def save_model(self, request, obj, form, change):
        if change:
            # Persist only editable model fields. A stale form must not overwrite
            # readonly lifecycle/applicability flags changed by another operation.
            editable = {field.name for field in obj._meta.concrete_fields if field.editable}
            obj.save(update_fields=editable.intersection(form.fields))
        else:
            super().save_model(request, obj, form, change)

    def get_readonly_fields(self, request, obj=None):
        return (*self.readonly_fields, "is_active") if obj else self.readonly_fields

    def _lifecycle(self, request, queryset, active):
        operations = {
            ServiceCategory: (services.deactivate_service_category, services.reactivate_service_category, "service_category"),
            ComplaintSymptom: (services.deactivate_complaint, services.reactivate_complaint, "complaint"),
            FaultDiagnosis: (services.deactivate_fault_diagnosis, services.reactivate_fault_diagnosis, "diagnosis"),
            RootCause: (services.deactivate_root_cause, services.reactivate_root_cause, "root_cause"),
            RepairAction: (services.deactivate_repair_action, services.reactivate_repair_action, "repair_action"),
        }
        deactivate, reactivate, argument = operations[self.model]
        operation = reactivate if active else deactivate
        for obj in queryset.order_by("pk"):
            try:
                operation(**{argument: obj})
            except ValidationError as error:
                self.message_user(request, "; ".join(error.messages), level=messages.ERROR)
        self.message_user(request, "Lifecycle operation completed; applicability configuration retained.")

    @admin.action(description="Deactivate selected records", permissions=["change"])
    def deactivate_selected(self, request, queryset):
        self._lifecycle(request, queryset, False)

    @admin.action(description="Reactivate selected records", permissions=["change"])
    def reactivate_selected(self, request, queryset):
        self._lifecycle(request, queryset, True)


@admin.register(ServiceCategory)
class ServiceCategoryAdmin(TaxonomyAdmin):
    pass


class ApplicabilityForm(forms.ModelForm):
    contradiction_message = "Global records cannot have explicit category mappings."
    # These configuration inputs are deliberately not model fields. The service
    # changes mode and mappings together after the ordinary record save.
    global_applicability = forms.BooleanField(required=False, label="Applies to all product categories")
    product_categories = forms.ModelMultipleChoiceField(queryset=ProductCategory.objects.all(), required=False,
        help_text="Restricted with no selected categories applies nowhere. Inactive categories may be configured but are not selectable for intake.")

    class Meta:
        exclude = ("applies_to_all_product_categories",)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if not self.instance._state.adding:
            self.initial["global_applicability"] = self.instance.applies_to_all_product_categories
            self.initial["product_categories"] = list(self.instance.category_mappings.values_list("product_category_id", flat=True))

    def clean(self):
        cleaned = super().clean()
        if cleaned.get("global_applicability") and cleaned.get("product_categories"):
            raise forms.ValidationError(self.contradiction_message)
        return cleaned


class ApplicabilityAdmin(TaxonomyAdmin):
    list_display = ("code", "name", "is_active", "applies_to_all_product_categories")
    list_filter = ("is_active", "applies_to_all_product_categories")

    def save_related(self, request, form, formsets, change):
        operations = {
            ComplaintSymptom: (services.set_complaint_applicability, "complaint"),
            FaultDiagnosis: (services.set_fault_diagnosis_applicability, "diagnosis"),
            RootCause: (services.set_root_cause_applicability, "root_cause"),
            RepairAction: (services.set_repair_action_applicability, "repair_action"),
        }
        operation, argument = operations[self.model]
        operation(**{argument: form.instance},
            applies_to_all_product_categories=form.cleaned_data["global_applicability"],
            product_categories=form.cleaned_data["product_categories"])
        super().save_related(request, form, formsets, change)


class ComplaintSymptomForm(ApplicabilityForm):
    contradiction_message = "Global complaints cannot have explicit category mappings."

    class Meta(ApplicabilityForm.Meta):
        model = ComplaintSymptom


class FaultDiagnosisForm(ApplicabilityForm):
    class Meta(ApplicabilityForm.Meta):
        model = FaultDiagnosis


class RootCauseForm(ApplicabilityForm):
    class Meta(ApplicabilityForm.Meta):
        model = RootCause


class RepairActionForm(ApplicabilityForm):
    class Meta(ApplicabilityForm.Meta):
        model = RepairAction


@admin.register(ComplaintSymptom)
class ComplaintSymptomAdmin(ApplicabilityAdmin):
    form = ComplaintSymptomForm


@admin.register(FaultDiagnosis)
class FaultDiagnosisAdmin(ApplicabilityAdmin):
    form = FaultDiagnosisForm


@admin.register(RootCause)
class RootCauseAdmin(ApplicabilityAdmin):
    form = RootCauseForm


@admin.register(RepairAction)
class RepairActionAdmin(ApplicabilityAdmin):
    form = RepairActionForm
