"""Immutable release, custody-transfer, accessory and closure evidence."""
from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.validators import MaxLengthValidator
from django.db import models
from django.utils import timezone

from apps.customers.models import normalize_mobile
from .models import IntakeRecord, NO_CREDENTIALS
from .repair_models import _aware_past

PRIVACY = "Minimal audit reference only. No full identity-document numbers, images, passwords, PINs or OTPs."


class DeliveryEvidence(IntakeRecord):
    class Meta(IntakeRecord.Meta):
        abstract = True

    def clean(self):
        super().clean()
        previous = type(self).objects.filter(pk=self.pk).first()
        if previous and any(getattr(previous, f.attname) != getattr(self, f.attname)
                            for f in self._meta.concrete_fields if f.name not in ("updated_at",)):
            raise ValidationError("Delivery and closure evidence is immutable.")


class ServiceCaseDeliveryRelease(DeliveryEvidence):
    service_case = models.OneToOneField("service.ServiceCase", on_delete=models.PROTECT, related_name="delivery_release")
    quality_control = models.ForeignKey("service.ServiceQualityControl", on_delete=models.PROTECT, related_name="delivery_releases")
    device = models.ForeignKey("devices.Device", on_delete=models.PROTECT, related_name="service_delivery_releases")
    device_identity = models.CharField(max_length=64)
    readied_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, related_name="service_delivery_releases")
    readied_at = models.DateTimeField(default=timezone.now)
    note = models.TextField(blank=True, max_length=2000, validators=[MaxLengthValidator(2000)], help_text=NO_CREDENTIALS)

    def clean(self):
        super().clean()
        from .models import ServiceCase, ServiceQualityControl
        if not ServiceCase.objects.filter(pk=self.service_case_id, device_id=self.device_id).exists() or not ServiceQualityControl.objects.filter(
            pk=self.quality_control_id, service_case_id=self.service_case_id, status="COMPLETED", outcome="PASSED").exists():
            raise ValidationError("Release requires this case's Device and completed passed QC.")
        _aware_past(self.readied_at)


class ServiceCaseHandover(DeliveryEvidence):
    class Recipient(models.TextChoices):
        CUSTOMER = "CUSTOMER", "Customer"
        AUTHORIZED_REPRESENTATIVE = "AUTHORIZED_REPRESENTATIVE", "Authorized representative"

    class Verification(models.TextChoices):
        CUSTOMER_RECORD = "CUSTOMER_RECORD", "Customer record confirmed"
        AUTHORIZED_REPRESENTATIVE = "AUTHORIZED_REPRESENTATIVE", "Representative authorization checked"
        MANUAL_DOCUMENT_CHECK = "MANUAL_DOCUMENT_CHECK", "Manual document check"

    class Identity(models.TextChoices):
        NATIONAL_ID = "NATIONAL_ID", "National ID checked"
        PASSPORT = "PASSPORT", "Passport checked"
        OTHER_DOCUMENT = "OTHER_DOCUMENT", "Other document checked"

    service_case = models.OneToOneField("service.ServiceCase", on_delete=models.PROTECT, related_name="handover")
    delivery_release = models.OneToOneField(ServiceCaseDeliveryRelease, on_delete=models.PROTECT, related_name="handover")
    recipient_type = models.CharField(max_length=25, choices=Recipient.choices)
    recipient_customer = models.ForeignKey("customers.Customer", null=True, blank=True, on_delete=models.PROTECT, related_name="service_handovers")
    recipient_name = models.CharField(max_length=200)
    recipient_mobile = models.CharField(max_length=32, blank=True)
    recipient_relationship = models.CharField(max_length=200, blank=True)
    recipient_identity_type = models.CharField(max_length=16, choices=Identity.choices, blank=True)
    recipient_identity_reference = models.CharField(max_length=64, blank=True, help_text=PRIVACY)
    verification_method = models.CharField(max_length=25, choices=Verification.choices)
    verification_reference = models.CharField(max_length=120, blank=True, help_text=PRIVACY)
    handed_over_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, related_name="service_handovers")
    delivered_at = models.DateTimeField(default=timezone.now)
    note = models.TextField(blank=True, max_length=2000, validators=[MaxLengthValidator(2000)], help_text=NO_CREDENTIALS)

    class Meta(DeliveryEvidence.Meta):
        ordering = ["delivered_at", "id"]
        constraints = [
            models.CheckConstraint(condition=(models.Q(recipient_type="CUSTOMER", recipient_customer__isnull=False, recipient_relationship="",
                verification_method__in=["CUSTOMER_RECORD", "MANUAL_DOCUMENT_CHECK"]) |
                (models.Q(recipient_type="AUTHORIZED_REPRESENTATIVE", recipient_customer__isnull=True,
                    verification_method__in=["AUTHORIZED_REPRESENTATIVE", "MANUAL_DOCUMENT_CHECK"]) &
                 ~models.Q(recipient_mobile="") & ~models.Q(recipient_relationship="") & ~models.Q(verification_reference=""))) & ~models.Q(recipient_name=""), name="service_handover_recipient_valid"),
            models.CheckConstraint(condition=(models.Q(verification_method="MANUAL_DOCUMENT_CHECK",
                recipient_identity_type__in=["NATIONAL_ID", "PASSPORT", "OTHER_DOCUMENT"]) & ~models.Q(recipient_identity_reference="") & ~models.Q(verification_reference="")) |
                models.Q(verification_method__in=["CUSTOMER_RECORD", "AUTHORIZED_REPRESENTATIVE"], recipient_identity_type="", recipient_identity_reference=""), name="service_handover_verification_valid"),
            models.CheckConstraint(condition=models.Q(delivered_at__lte=models.F("created_at")), name="service_handover_time_valid"),
        ]

    def clean_fields(self, exclude=None):
        if isinstance(self.recipient_mobile, str):
            self.recipient_mobile = normalize_mobile(self.recipient_mobile)
        super().clean_fields(exclude=exclude)

    def clean(self):
        super().clean()
        from .models import ServiceCase
        _aware_past(self.delivered_at)
        release = ServiceCaseDeliveryRelease.objects.filter(pk=self.delivery_release_id, service_case_id=self.service_case_id).first()
        if release is None or self.delivered_at is None or self.delivered_at < release.readied_at:
            raise ValidationError("Handover must follow this case's delivery release.")
        if self.recipient_type == self.Recipient.CUSTOMER and not ServiceCase.objects.filter(pk=self.service_case_id, customer_id=self.recipient_customer_id).exists():
            raise ValidationError("Recipient Customer must be the ServiceCase Customer.")


class ServiceCaseHandoverAccessory(DeliveryEvidence):
    handover = models.ForeignKey(ServiceCaseHandover, on_delete=models.PROTECT, related_name="accessories")
    intake_accessory = models.ForeignKey("service.ServiceCaseAccessory", on_delete=models.PROTECT, related_name="handover_acknowledgements")
    returned_quantity = models.PositiveIntegerField()
    condition_note = models.TextField(blank=True, max_length=2000, validators=[MaxLengthValidator(2000)], help_text=NO_CREDENTIALS)

    class Meta(DeliveryEvidence.Meta):
        constraints = [
            models.UniqueConstraint(fields=["handover", "intake_accessory"], name="service_handover_accessory_unique"),
            models.CheckConstraint(condition=models.Q(returned_quantity__gte=0), name="service_return_quantity_nonnegative"),
        ]

    def clean(self):
        super().clean()
        from .models import ServiceCaseAccessory
        case_id = ServiceCaseHandover.objects.filter(pk=self.handover_id).values_list("service_case_id", flat=True).first()
        accessory = ServiceCaseAccessory.objects.filter(pk=self.intake_accessory_id, service_case_id=case_id).first()
        if accessory is None or type(self.returned_quantity) is not int or not 0 <= self.returned_quantity <= accessory.quantity:
            raise ValidationError("Returned quantity must be within this case's received quantity.")
        if self.returned_quantity < accessory.quantity and not self.condition_note.strip():
            raise ValidationError("Accessory shortage requires an explanation.")


class ServiceCaseClosure(DeliveryEvidence):
    service_case = models.OneToOneField("service.ServiceCase", on_delete=models.PROTECT, related_name="closure")
    handover = models.OneToOneField(ServiceCaseHandover, on_delete=models.PROTECT, related_name="closure")
    closed_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, related_name="service_closures")
    closed_at = models.DateTimeField(default=timezone.now)
    note = models.TextField(blank=True, max_length=2000, validators=[MaxLengthValidator(2000)], help_text=NO_CREDENTIALS)

    class Meta(DeliveryEvidence.Meta):
        ordering = ["closed_at", "id"]
        constraints = [models.CheckConstraint(condition=models.Q(closed_at__lte=models.F("created_at")), name="service_closure_time_valid")]

    def clean(self):
        super().clean()
        _aware_past(self.closed_at)
        handover = ServiceCaseHandover.objects.filter(pk=self.handover_id, service_case_id=self.service_case_id).first()
        if handover is None or self.closed_at is None or self.closed_at < handover.delivered_at:
            raise ValidationError("Closure must follow this case's handover.")
