"""Recorded ownership periods; transitions belong to relationship services."""
import uuid
from datetime import datetime

from django.contrib.postgres.constraints import ExclusionConstraint
from django.contrib.postgres.fields import DateTimeRangeField, RangeBoundary, RangeOperators
from django.core.exceptions import ValidationError
from django.db import models
from django.utils import timezone

from apps.organization.model_base import TimeStampedModel
from .models import RegistryQuerySet


class CustomerDeviceRelationship(TimeStampedModel):
    class Type(models.TextChoices):
        OWNER = "OWNER", "Owner"

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    customer = models.ForeignKey("customers.Customer", on_delete=models.PROTECT, related_name="device_relationships")
    device = models.ForeignKey("devices.Device", on_delete=models.PROTECT, related_name="customer_relationships")
    relationship_type = models.CharField(max_length=16, choices=Type.choices, default=Type.OWNER)
    started_at = models.DateTimeField(default=timezone.now)
    ended_at = models.DateTimeField(null=True, blank=True)
    end_reason = models.CharField(max_length=2000, blank=True)
    note = models.CharField(max_length=2000, blank=True)
    objects = RegistryQuerySet.as_manager()

    class Meta:
        ordering = ["-started_at", "id"]
        constraints = [
            models.CheckConstraint(condition=models.Q(relationship_type="OWNER"), name="devices_relationship_type_valid"),
            models.CheckConstraint(condition=models.Q(ended_at__isnull=True) | models.Q(ended_at__gte=models.F("started_at")), name="devices_owner_period_valid"),
            models.UniqueConstraint(fields=["device"], condition=models.Q(relationship_type="OWNER", ended_at__isnull=True), name="devices_one_current_owner"),
            ExclusionConstraint(name="devices_owner_period_no_overlap", expressions=[
                ("device", RangeOperators.EQUAL),
                (models.Func("started_at", "ended_at", RangeBoundary(), function="TSTZRANGE", output_field=DateTimeRangeField()), RangeOperators.OVERLAPS),
            ], condition=models.Q(relationship_type="OWNER")),
        ]

    def __str__(self):
        return f"Ownership {self.pk}"

    def clean(self):
        super().clean()
        for value in (self.started_at, self.ended_at):
            if value is not None and (not isinstance(value, datetime) or timezone.is_naive(value)):
                raise ValidationError("Ownership timestamps must be timezone-aware.")
        if self.started_at and self.ended_at and self.ended_at < self.started_at:
            raise ValidationError("Ownership cannot end before it starts.")
        previous = type(self).objects.filter(pk=self.pk).first()
        if previous:
            fields = ["customer_id", "device_id", "relationship_type", "started_at", "note"]
            if previous.ended_at is not None:
                fields += ["ended_at", "end_reason"]
            if any(getattr(previous, field) != getattr(self, field) for field in fields):
                raise ValidationError("Ownership facts and closed history are immutable.")

    def validate_constraints(self, exclude=None):
        # An invalid range must produce validation errors, not a PostgreSQL range error.
        # Django's conditional exclusion validator cannot resolve an excluded
        # condition field (e.g. a type already rejected by clean_fields).
        if "relationship_type" in (exclude or ()):
            return
        values = [value for value in (self.started_at, self.ended_at) if value is not None]
        if any(not isinstance(value, datetime) or timezone.is_naive(value) for value in values):
            return
        if self.started_at and self.ended_at and self.ended_at < self.started_at:
            return
        super().validate_constraints(exclude=exclude)

    def _persist(self):
        self.full_clean()
        super().save(using="default", force_insert=self._state.adding)

    def save(self, *args, **kwargs):
        raise ValidationError("Use ownership assignment, ending or transfer services.")

    def delete(self, *args, **kwargs):
        raise ValidationError("Ownership history cannot be deleted.")
