"""Immutable SLA expectations and escalation evidence, not ServiceCase state."""
import uuid
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models
from django.utils import timezone


class HistoryQuerySet(models.QuerySet):
    def delete(self):
        raise ValidationError("SLA history cannot be deleted.")

    def update(self, **kwargs):
        raise ValidationError("Use SLA services.")

    def bulk_create(self, *args, **kwargs):
        raise ValidationError("Use SLA services.")

    def bulk_update(self, *args, **kwargs):
        raise ValidationError("Use SLA services.")


class Record(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    created_at = models.DateTimeField(default=timezone.now, editable=False)
    objects = HistoryQuerySet.as_manager()

    class Meta:
        abstract = True

    def save(self, *args, **kwargs):
        raise ValidationError("Use SLA services.")

    def delete(self, *args, **kwargs):
        raise ValidationError("SLA history cannot be deleted.")

    def _persist(self):
        if not self._state.adding and not isinstance(self, SlaPolicy):
            raise ValidationError("SLA evidence is immutable.")
        self.full_clean()
        super().save(using="default", force_insert=self._state.adding)


class SlaPolicy(Record):
    company = models.ForeignKey("organization.Company", on_delete=models.PROTECT)
    service_center = models.ForeignKey("organization.ServiceCenter", null=True, blank=True, on_delete=models.PROTECT)
    product_category = models.ForeignKey("catalog.ProductCategory", null=True, blank=True, on_delete=models.PROTECT)
    code = models.SlugField(max_length=64)
    name = models.CharField(max_length=150)
    is_active = models.BooleanField(default=True)
    effective_from = models.DateField()
    effective_until = models.DateField(null=True, blank=True)
    target_minutes = models.PositiveIntegerField()
    warning_minutes = models.PositiveIntegerField()
    due_soon_template = models.ForeignKey("communications.NotificationTemplate", null=True, blank=True,
                                         on_delete=models.PROTECT, related_name="sla_due_policies")
    overdue_template = models.ForeignKey("communications.NotificationTemplate", null=True, blank=True,
                                        on_delete=models.PROTECT, related_name="sla_overdue_policies")
    updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
    updated_at = models.DateTimeField(default=timezone.now)

    class Meta:
        default_permissions = ()
        permissions = [("manage_slapolicy", "Manage company SLA policies")]
        ordering = ["company_id", "code"]
        constraints = [
            models.UniqueConstraint(fields=["company", "code"], name="sla_policy_code_unique"),
            models.CheckConstraint(condition=models.Q(target_minutes__gte=1, warning_minutes__gte=0,
                warning_minutes__lte=models.F("target_minutes")), name="sla_duration_valid"),
            models.CheckConstraint(condition=models.Q(effective_until=None) | models.Q(effective_until__gte=models.F("effective_from")), name="sla_effective_range_valid"),
        ]

    def clean(self):
        if self.service_center_id and self.service_center.company_id != self.company_id:
            raise ValidationError("Policy center must belong to the company.")
        for template in (self.due_soon_template, self.overdue_template):
            if template and template.company_id != self.company_id:
                raise ValidationError("Notification template must belong to the policy company.")

    def __str__(self):
        return f"{self.company} / {self.code}"


class ServiceSla(Record):
    service_case = models.OneToOneField("service.ServiceCase", on_delete=models.PROTECT, related_name="sla_record")
    policy = models.ForeignKey(SlaPolicy, on_delete=models.PROTECT)
    product_category = models.ForeignKey("catalog.ProductCategory", on_delete=models.PROTECT)
    policy_snapshot = models.JSONField()
    started_at = models.DateTimeField()
    due_at = models.DateTimeField()
    warning_at = models.DateTimeField()
    enrolled_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)

    class Meta:
        default_permissions = ()
        permissions = [("view_sla", "View scoped SLA monitoring and history"),
                       ("monitor_sla", "Enroll service cases and record SLA escalations")]
        ordering = ["due_at", "pk"]
        constraints = [models.CheckConstraint(condition=models.Q(due_at__gt=models.F("started_at"),
            warning_at__gte=models.F("started_at"), warning_at__lte=models.F("due_at")), name="sla_clock_valid")]


class SlaEscalation(Record):
    sla = models.ForeignKey(ServiceSla, on_delete=models.PROTECT, related_name="escalations")
    kind = models.CharField(max_length=10, choices=[("DUE_SOON", "Due soon"), ("OVERDUE", "Overdue")])
    actor = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
    origin = models.CharField(max_length=10, choices=[("MANUAL", "Manual"), ("MONITOR", "Monitor")])
    occurred_at = models.DateTimeField()
    context = models.JSONField()
    notification_template = models.ForeignKey("communications.NotificationTemplate", null=True, blank=True, on_delete=models.PROTECT)

    class Meta:
        default_permissions = ()
        ordering = ["occurred_at", "pk"]
        constraints = [models.UniqueConstraint(fields=["sla", "kind"], name="sla_escalation_unique"),
            models.CheckConstraint(condition=models.Q(kind__in=["DUE_SOON", "OVERDUE"], origin__in=["MANUAL", "MONITOR"]), name="sla_escalation_valid")]


class EscalationCommunication(Record):
    escalation = models.ForeignKey(SlaEscalation, on_delete=models.PROTECT, related_name="communication_attempts")
    actor = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
    notification = models.ForeignKey("communications.Notification", null=True, blank=True, on_delete=models.PROTECT)
    error_code = models.CharField(max_length=40, blank=True)

    class Meta:
        default_permissions = ()
        ordering = ["created_at", "pk"]
        constraints = [models.UniqueConstraint(fields=["escalation"], condition=models.Q(notification__isnull=False),
                                                name="sla_one_notification_link"),
            models.CheckConstraint(condition=models.Q(notification=None, error_code="NOTIFICATION_REQUEST_FAILED")
                | models.Q(notification__isnull=False, error_code=""), name="sla_communication_result")]
