"""Repair attempts and explicit performed-action evidence."""
from datetime import datetime

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


def _aware_past(*values):
    for value in values:
        if value is not None and (not isinstance(value, datetime) or timezone.is_naive(value) or value > timezone.now()):
            raise ValidationError("Repair timestamps must be aware and no later than now.")


class ServiceRepairExecution(IntakeRecord):
    class Status(models.TextChoices):
        OPEN = "OPEN", "Open"
        COMPLETED = "COMPLETED", "Completed"
        ABANDONED = "ABANDONED", "Abandoned"

    class Outcome(models.TextChoices):
        REPAIRED = "REPAIRED", "Repaired"
        NOT_REPAIRED = "NOT_REPAIRED", "Not repaired"

    service_case = models.ForeignKey("service.ServiceCase", on_delete=models.PROTECT, related_name="repair_executions")
    engineer_assignment = models.ForeignKey("service.ServiceEngineerAssignment", on_delete=models.PROTECT, related_name="repair_executions")
    diagnostic_assessment = models.ForeignKey("service.ServiceDiagnosticAssessment", on_delete=models.PROTECT, related_name="repair_executions")
    status = models.CharField(max_length=9, choices=Status.choices, default=Status.OPEN)
    note = models.TextField(max_length=4000, blank=True, validators=[MaxLengthValidator(4000)], help_text=NO_CREDENTIALS)
    started_at = models.DateTimeField(default=timezone.now)
    started_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, related_name="started_repairs")
    completed_at = models.DateTimeField(null=True, blank=True)
    completed_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.PROTECT, related_name="completed_repairs")
    outcome = models.CharField(max_length=12, null=True, blank=True, choices=Outcome.choices)
    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_repairs")
    abandon_reason = models.CharField(max_length=2000, blank=True, help_text=NO_CREDENTIALS)
    immutable_fields = ("service_case_id", "engineer_assignment_id", "diagnostic_assessment_id", "started_at", "started_by_id")

    class Meta(IntakeRecord.Meta):
        ordering = ["started_at", "id"]
        constraints = [
            models.UniqueConstraint(fields=["service_case"], condition=models.Q(status="OPEN"), name="service_one_open_repair"),
            models.CheckConstraint(condition=(
                models.Q(status="OPEN", completed_at__isnull=True, completed_by__isnull=True, outcome__isnull=True,
                    abandoned_at__isnull=True, abandoned_by__isnull=True, abandon_reason="") |
                models.Q(status="COMPLETED", completed_at__isnull=False, completed_by__isnull=False,
                    completed_at__gte=models.F("started_at"), outcome__isnull=False, outcome__in=["REPAIRED", "NOT_REPAIRED"],
                    abandoned_at__isnull=True, abandoned_by__isnull=True, abandon_reason="") |
                (models.Q(status="ABANDONED", completed_at__isnull=True, completed_by__isnull=True, outcome__isnull=True,
                    abandoned_at__isnull=False, abandoned_by__isnull=False, abandoned_at__gte=models.F("started_at")) & ~models.Q(abandon_reason=""))
            ), name="service_repair_outcome_coherent"),
        ]

    def clean(self):
        super().clean()
        from .models import ServiceDiagnosticAssessment
        if not ServiceDiagnosticAssessment.objects.filter(pk=self.diagnostic_assessment_id, service_case_id=self.service_case_id,
            engineer_assignment_id=self.engineer_assignment_id, completed_at__isnull=False, abandoned_at=None).exists():
            raise ValidationError("Repair must reference completed diagnosis on the same case and assignment.")
        _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 != self.Status.OPEN and any(getattr(previous, field) != getattr(self, field) for field in
            ("status", "note", "completed_at", "completed_by_id", "outcome", "abandoned_at", "abandoned_by_id", "abandon_reason")):
            raise ValidationError("Historical repair execution is immutable.")


class ServiceRepairAction(IntakeRecord):
    repair_execution = models.ForeignKey(ServiceRepairExecution, on_delete=models.PROTECT, related_name="actions")
    repair_action = models.ForeignKey("service_catalog.RepairAction", on_delete=models.PROTECT, related_name="service_repair_actions")
    note = models.TextField(max_length=2000, blank=True, validators=[MaxLengthValidator(2000)], help_text=NO_CREDENTIALS)
    is_active = models.BooleanField(default=True)
    performed_at = models.DateTimeField(null=True, blank=True)
    performed_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.PROTECT, related_name="performed_repair_actions")
    immutable_fields = ("repair_execution_id",)

    class Meta(IntakeRecord.Meta):
        constraints = [
            models.UniqueConstraint(fields=["repair_execution", "repair_action"], condition=models.Q(is_active=True), name="service_active_repair_action_unique"),
            models.CheckConstraint(condition=(models.Q(performed_at__isnull=True, performed_by__isnull=True) |
                models.Q(performed_at__isnull=False, performed_by__isnull=False, performed_at__gte=models.F("created_at"))), name="service_repair_performed_coherent"),
        ]

    def clean(self):
        super().clean()
        if ServiceRepairExecution.objects.filter(pk=self.repair_execution_id).exclude(status="OPEN").exists():
            raise ValidationError("Historical repair actions cannot change.")
        _aware_past(self.performed_at)
        previous = type(self).objects.filter(pk=self.pk).first()
        if previous:
            if not previous.is_active and any(getattr(previous, f) != getattr(self, f) for f in
                ("repair_action_id", "note", "is_active", "performed_at", "performed_by_id")):
                raise ValidationError("Removed repair actions cannot change or be restored.")
            if previous.performed_at is not None and any(getattr(previous, f) != getattr(self, f) for f in
                ("repair_action_id", "note", "performed_at", "performed_by_id")):
                raise ValidationError("Performed evidence cannot be rewritten; remove and create a new planned row if needed.")
