from django.contrib import admin, messages
from django.core.exceptions import ValidationError

from .models import Customer
from . import services


@admin.register(Customer)
class CustomerAdmin(admin.ModelAdmin):
    list_display = ("customer_number", "customer_type", "display_name", "company",
                    "primary_mobile", "primary_email", "is_active", "created_at")
    list_filter = ("company", "customer_type", "is_active")
    search_fields = ("customer_number", "full_name", "organization_name", "primary_mobile", "primary_email")
    ordering = ("customer_number", "id")
    list_select_related = ("company",)
    readonly_fields = ("id", "customer_number", "is_active", "created_at", "updated_at")
    actions = ("deactivate_selected", "reactivate_selected")

    def get_readonly_fields(self, request, obj=None):
        return self.readonly_fields + (("company", "customer_type") if obj else ())

    def has_delete_permission(self, request, obj=None):
        return False

    def save_model(self, request, obj, form, change):
        if change:
            # Patch only submitted changes onto freshly locked state. Omitted and
            # unchanged fields must not overwrite another request's changes.
            names = {"full_name", "organization_name", "primary_mobile", "primary_email"}
            current = services.update_customer(customer=obj, **{
                name: form.cleaned_data[name] for name in names.intersection(form.changed_data)
            })
        else:
            current = services.create_customer(company=obj.company, customer_type=obj.customer_type,
                full_name=obj.full_name, organization_name=obj.organization_name,
                primary_mobile=obj.primary_mobile, primary_email=obj.primary_email)
        for field in obj._meta.concrete_fields:
            setattr(obj, field.attname, getattr(current, field.attname))
        obj._state = current._state

    def _lifecycle(self, request, queryset, operation):
        for customer in queryset.order_by("company_id", "pk"):
            try:
                operation(customer=customer)
            except ValidationError:
                self.message_user(request, "Customer lifecycle change rejected; check company status.", messages.ERROR)

    @admin.action(description="Deactivate selected customers", permissions=["change"])
    def deactivate_selected(self, request, queryset):
        self._lifecycle(request, queryset, services.deactivate_customer)

    @admin.action(description="Reactivate selected customers", permissions=["change"])
    def reactivate_selected(self, request, queryset):
        self._lifecycle(request, queryset, services.reactivate_customer)


class CustomerDetailAdmin(admin.ModelAdmin):
    readonly_fields = ("id", "is_primary", "is_active", "created_at", "updated_at")
    list_select_related = ("customer", "customer__company")
    ordering = ("created_at", "id")
    actions = ("make_primary", "deactivate_selected", "reactivate_selected")

    def get_readonly_fields(self, request, obj=None):
        fields = self.readonly_fields + (("customer",) if obj else ())
        if self.model is CustomerContact:
            fields += ("normalized_value",) + (("contact_type",) if obj else ())
        return fields

    def has_delete_permission(self, request, obj=None):
        return False

    def changeform_view(self, request, object_id=None, form_url="", extra_context=None):
        from django.http import HttpResponseRedirect
        try:
            return super().changeform_view(request, object_id, form_url, extra_context)
        except ValidationError:
            # ModelAdmin's surrounding atomic block has rolled back before this
            # handler. A post-validation concurrent conflict must not partly save.
            self.message_user(request, "The record changed or is unavailable. Reload and retry.", messages.ERROR)
            return HttpResponseRedirect(request.path)

    def save_model(self, request, obj, form, change):
        if self.model is CustomerAddress:
            names = {"address_type", "label", "address_line_1", "address_line_2", "area",
                     "city", "district", "postal_code", "country_code"}
            if change:
                current = services.update_customer_address(address=obj, **{
                    name: form.cleaned_data[name] for name in names.intersection(form.changed_data)
                })
            else:
                current = services.create_customer_address(customer=obj.customer, **{
                    name: form.cleaned_data[name] for name in names
                })
        elif change:
            current = services.update_customer_contact(contact=obj, **{
                name: form.cleaned_data[name] for name in {"label", "value"}.intersection(form.changed_data)
            })
        else:
            current = services.create_customer_contact(customer=obj.customer,
                contact_type=obj.contact_type, value=obj.value, label=obj.label)
        for field in obj._meta.concrete_fields:
            setattr(obj, field.attname, getattr(current, field.attname))
        obj._state = current._state

    def _operation(self, request, queryset, verb):
        kind = "address" if self.model is CustomerAddress else "contact"
        operation = getattr(services, f"{verb}_customer_{kind}")
        for record in queryset.order_by("customer_id", "pk"):
            try:
                operation(**{kind: record})
            except ValidationError:
                self.message_user(request, "Change rejected; check current customer/company and record state.", messages.ERROR)

    @admin.action(description="Make selected record primary", permissions=["change"])
    def make_primary(self, request, queryset):
        if queryset.count() != 1:
            self.message_user(request, "Select exactly one record to make primary.", messages.ERROR)
            return
        self._operation(request, queryset, "set_primary")

    @admin.action(description="Deactivate selected records", permissions=["change"])
    def deactivate_selected(self, request, queryset):
        self._operation(request, queryset, "deactivate")

    @admin.action(description="Reactivate selected records", permissions=["change"])
    def reactivate_selected(self, request, queryset):
        self._operation(request, queryset, "reactivate")


from .models import CustomerAddress, CustomerContact  # noqa: E402


@admin.register(CustomerAddress)
class CustomerAddressAdmin(CustomerDetailAdmin):
    list_display = ("customer", "address_type", "label", "city", "country_code", "is_primary", "is_active", "created_at")
    list_filter = ("customer__company", "address_type", "country_code", "is_primary", "is_active")
    search_fields = ("customer__customer_number", "customer__full_name", "customer__organization_name",
                     "label", "address_line_1", "city", "postal_code")


@admin.register(CustomerContact)
class CustomerContactAdmin(CustomerDetailAdmin):
    list_display = ("customer", "contact_type", "label", "value", "is_primary", "is_active", "created_at")
    list_filter = ("customer__company", "contact_type", "is_primary", "is_active")
    search_fields = ("customer__customer_number", "customer__full_name", "customer__organization_name",
                     "label", "value", "normalized_value")
