"""Protected QC attempts and versioned checklist/complaint 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 .models import IntakeRecord, NO_CREDENTIALS
from .repair_models import _aware_past


class ServiceQualityControl(IntakeRecord):
    class Status(models.TextChoices):
        IN_PROGRESS = "IN_PROGRESS", "In progress"
        COMPLETED = "COMPLETED", "Completed"
        ABANDONED = "ABANDONED", "Abandoned"

    class Outcome(models.TextChoices):
        PASSED = "PASSED", "Passed"
        FAILED = "FAILED", "Failed"

    service_case = models.ForeignKey("service.ServiceCase", on_delete=models.PROTECT, related_name="quality_controls")
    repair_execution = models.ForeignKey("service.ServiceRepairExecution", on_delete=models.PROTECT, related_name="quality_controls")
    inspector = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, related_name="quality_controls")
    status = models.CharField(max_length=11, choices=Status.choices, default=Status.IN_PROGRESS)
    checklist_version = models.PositiveSmallIntegerField(default=1)
    started_at = models.DateTimeField(default=timezone.now)
    completed_at = models.DateTimeField(null=True, blank=True)
    outcome = models.CharField(max_length=6, choices=Outcome.choices, null=True, blank=True)
    summary = models.TextField(blank=True, max_length=4000, validators=[MaxLengthValidator(4000)], help_text=NO_CREDENTIALS)
    failure_note = models.TextField(blank=True, max_length=2000, validators=[MaxLengthValidator(2000)], help_text=NO_CREDENTIALS)
    abandoned_at = models.DateTimeField(null=True, blank=True)
    abandoned_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.PROTECT, related_name="abandoned_quality_controls")
    abandon_reason = models.CharField(max_length=2000, blank=True, help_text=NO_CREDENTIALS)
    immutable_fields = ("service_case_id", "repair_execution_id", "inspector_id", "started_at", "checklist_version")

    class Meta(IntakeRecord.Meta):
        ordering = ["started_at", "id"]
        constraints = [
            models.UniqueConstraint(fields=["service_case"], condition=models.Q(status="IN_PROGRESS"), name="service_one_open_qc"),
            models.CheckConstraint(condition=(
                models.Q(status="IN_PROGRESS", outcome__isnull=True, completed_at__isnull=True, failure_note="", abandoned_at__isnull=True, abandoned_by__isnull=True, abandon_reason="") |
                (models.Q(status="COMPLETED", completed_at__isnull=False, completed_at__gte=models.F("started_at"), outcome__isnull=False,
                    abandoned_at__isnull=True, abandoned_by__isnull=True, abandon_reason="") &
                 (models.Q(outcome="PASSED", failure_note="") | (models.Q(outcome="FAILED") & ~models.Q(failure_note="")))) |
                (models.Q(status="ABANDONED", outcome__isnull=True, completed_at__isnull=True, failure_note="", abandoned_at__isnull=False,
                    abandoned_at__gte=models.F("started_at"), abandoned_by__isnull=False) & ~models.Q(abandon_reason=""))
            ), name="service_qc_outcome_coherent"),
            models.CheckConstraint(condition=models.Q(checklist_version__gte=1), name="service_qc_version_positive"),
        ]

    def clean(self):
        super().clean()
        from .models import ServiceRepairExecution
        repair = ServiceRepairExecution.objects.filter(pk=self.repair_execution_id, service_case_id=self.service_case_id,
            status="COMPLETED", outcome="REPAIRED").select_related("engineer_assignment").first()
        if repair is None or repair.engineer_assignment.engineer_id == self.inspector_id:
            raise ValidationError("QC requires successful same-case repair and an independent inspector.")
        _aware_past(self.started_at, self.completed_at, self.abandoned_at)
        previous = type(self).objects.filter(pk=self.pk).first()
        if previous and previous.status != "IN_PROGRESS" and any(getattr(previous, f) != getattr(self, f) for f in
            ("status", "completed_at", "outcome", "summary", "failure_note", "abandoned_at", "abandoned_by_id", "abandon_reason")):
            raise ValidationError("Finalized QC history is immutable.")


class QCChild(IntakeRecord):
    quality_control = models.ForeignKey(ServiceQualityControl, on_delete=models.PROTECT, related_name="%(class)ss")
    note = models.TextField(blank=True, max_length=2000, validators=[MaxLengthValidator(2000)], help_text=NO_CREDENTIALS)

    class Meta(IntakeRecord.Meta):
        abstract = True

    def clean(self):
        super().clean()
        if ServiceQualityControl.objects.filter(pk=self.quality_control_id).exclude(status="IN_PROGRESS").exists():
            raise ValidationError("Finalized QC evidence cannot change.")


class ServiceQualityControlCheck(QCChild):
    class Result(models.TextChoices):
        PASS = "PASS", "Pass"
        FAIL = "FAIL", "Fail"
        NOT_APPLICABLE = "NOT_APPLICABLE", "Not applicable"

    check_code = models.CharField(max_length=32)
    check_name = models.CharField(max_length=160)
    sequence = models.PositiveSmallIntegerField()
    allows_not_applicable = models.BooleanField(default=False)
    result = models.CharField(max_length=14, choices=Result.choices, null=True, blank=True)
    immutable_fields = ("quality_control_id", "check_code", "check_name", "sequence", "allows_not_applicable")

    class Meta(QCChild.Meta):
        ordering = ["sequence", "id"]
        constraints = [
            models.UniqueConstraint(fields=["quality_control", "check_code"], name="service_qc_check_unique"),
            models.CheckConstraint(condition=models.Q(result__isnull=True) | models.Q(result__in=["PASS", "FAIL", "NOT_APPLICABLE"]), name="service_qc_check_result_valid"),
            models.CheckConstraint(condition=~models.Q(result="NOT_APPLICABLE") | (models.Q(allows_not_applicable=True) & ~models.Q(note="")), name="service_qc_na_explained"),
        ]


class ServiceQualityControlComplaintCheck(QCChild):
    class Result(models.TextChoices):
        RESOLVED = "RESOLVED", "Resolved"
        NOT_RESOLVED = "NOT_RESOLVED", "Not resolved"
        NOT_TESTABLE = "NOT_TESTABLE", "Not testable"

    complaint = models.ForeignKey("service.ServiceCaseComplaint", on_delete=models.PROTECT, related_name="quality_control_checks")
    result = models.CharField(max_length=12, choices=Result.choices, null=True, blank=True)
    immutable_fields = ("quality_control_id", "complaint_id")

    class Meta(QCChild.Meta):
        constraints = [
            models.UniqueConstraint(fields=["quality_control", "complaint"], name="service_qc_complaint_unique"),
            models.CheckConstraint(condition=models.Q(result__isnull=True) | models.Q(result__in=["RESOLVED", "NOT_RESOLVED", "NOT_TESTABLE"]), name="service_qc_complaint_result_valid"),
        ]

    def clean(self):
        super().clean()
        from .models import ServiceCaseComplaint
        case_id = ServiceQualityControl.objects.filter(pk=self.quality_control_id).values_list("service_case_id", flat=True).first()
        if not ServiceCaseComplaint.objects.filter(pk=self.complaint_id, service_case_id=case_id).exists():
            raise ValidationError("Complaint must belong to the QC case.")
