"""Global part definitions, not physical stock or repair transactions."""
import uuid

from django.core.exceptions import ValidationError
from django.core.validators import MaxLengthValidator, RegexValidator
from django.db import models
from django.db.models.functions import Trim

from apps.organization.model_base import TimeStampedModel

CODE_PATTERN = r"^[A-Z0-9][A-Z0-9_-]*$"
CODE_VALIDATOR = RegexValidator(CODE_PATTERN, "Use uppercase letters, digits, hyphens and underscores.")


class RetainedQuerySet(models.QuerySet):
    def delete(self):
        raise ValidationError("Parts records are retained; use the parts services.")


class PartsRecord(TimeStampedModel):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    objects = RetainedQuerySet.as_manager()

    class Meta:
        abstract = True

    def clean_fields(self, exclude=None):
        for field in self._meta.concrete_fields:
            value = getattr(self, field.attname)
            if isinstance(field, (models.CharField, models.TextField)) and isinstance(value, str):
                value = value.strip()
                setattr(self, field.attname, value.upper() if field.name in ("code", "part_code") else value)
        super().clean_fields(exclude=exclude)

    def save(self, *args, **kwargs):
        raise ValidationError("Use the parts services.")

    def delete(self, *args, **kwargs):
        raise ValidationError("Parts records are retained; use the parts services.")

    def _persist(self):
        self.full_clean()
        super().save(using="default", force_insert=self._state.adding)


class PartCategory(PartsRecord):
    code = models.CharField(max_length=64, unique=True, validators=[CODE_VALIDATOR])
    name = models.CharField(max_length=200)
    description = models.TextField(blank=True, validators=[MaxLengthValidator(4000)])
    is_active = models.BooleanField(default=True)

    class Meta:
        ordering = ["code"]
        verbose_name_plural = "part categories"
        constraints = [
            models.CheckConstraint(condition=models.Q(code__regex=CODE_PATTERN), name="parts_category_code_format"),
            models.CheckConstraint(condition=~models.Q(name="") & models.Q(name=Trim("name")), name="parts_category_name_trimmed"),
        ]

    def __str__(self):
        return f"{self.code}: {self.name}"


class SparePart(PartsRecord):
    class SerializationPolicy(models.TextChoices):
        NOT_SERIALIZED = "NOT_SERIALIZED", "Not serialized"
        OPTIONAL_SERIAL = "OPTIONAL_SERIAL", "Optional serial"
        REQUIRED_SERIAL = "REQUIRED_SERIAL", "Required serial"

    part_code = models.CharField(max_length=64, unique=True, validators=[CODE_VALIDATOR])
    name = models.CharField(max_length=200)
    description = models.TextField(blank=True, validators=[MaxLengthValidator(4000)])
    category = models.ForeignKey(PartCategory, on_delete=models.PROTECT, related_name="spare_parts")
    manufacturer_part_number = models.CharField(max_length=128, blank=True)
    serialization_policy = models.CharField(max_length=20, choices=SerializationPolicy.choices)
    is_active = models.BooleanField(default=True)

    class Meta:
        ordering = ["part_code"]
        constraints = [
            models.CheckConstraint(condition=models.Q(part_code__regex=CODE_PATTERN), name="parts_part_code_format"),
            models.CheckConstraint(condition=~models.Q(name="") & models.Q(name=Trim("name")), name="parts_part_name_trimmed"),
            models.CheckConstraint(condition=models.Q(serialization_policy__in=["NOT_SERIALIZED", "OPTIONAL_SERIAL", "REQUIRED_SERIAL"]), name="parts_serialization_policy_valid"),
        ]

    def __str__(self):
        return f"{self.part_code}: {self.name}"


class SparePartCompatibility(PartsRecord):
    spare_part = models.ForeignKey(SparePart, on_delete=models.PROTECT, related_name="compatibilities")
    # Exactly one endpoint: a variant's model is derived, never duplicated.
    product_model = models.ForeignKey("catalog.ProductModel", null=True, blank=True,
        on_delete=models.PROTECT, related_name="spare_part_compatibilities")
    product_variant = models.ForeignKey("catalog.ProductVariant", null=True, blank=True,
        on_delete=models.PROTECT, related_name="spare_part_compatibilities")
    is_active = models.BooleanField(default=True)

    class Meta:
        ordering = ["created_at", "id"]
        verbose_name_plural = "spare part compatibilities"
        constraints = [
            models.CheckConstraint(condition=(models.Q(product_model__isnull=False, product_variant__isnull=True)
                | models.Q(product_model__isnull=True, product_variant__isnull=False)), name="parts_compat_exactly_one_target"),
            models.UniqueConstraint(fields=["spare_part", "product_model"], name="parts_compat_model_unique"),
            models.UniqueConstraint(fields=["spare_part", "product_variant"], name="parts_compat_variant_unique"),
        ]

    def __str__(self):
        return f"{self.spare_part} / {self.product_model or self.product_variant}"
