"""
Parts models for lara_django_material.

This module contains Part-related models including shape, Part,
and subset classes.
"""

import uuid

from django.conf import settings
from django.db import models
from django.utils import timezone
from lara_django_people.models import ItemSubsetAbstr

from .base import ExtraData, PartAbstr, PartDeviceClass, PartShapeAbstr


class PartShape(PartShapeAbstr):
    """Part shape and dimensions."""

    model_curi = "lara:material/PartShape"
    partshape_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    data_extra = models.ManyToManyField(
        ExtraData,
        related_name="%(app_label)s_%(class)s_ps_data_extra_related",
        related_query_name="%(app_label)s_%(class)s_ps_data_extra_related_query",
        blank=True,
        help_text="part shape extra data, like 3D model or polygon describing the part shape/dimensions",
    )

    def save(self, *args, **kwargs):
        """Generate default values for IRI."""
        if self.iri is None or self.iri == "":
            self.iri = (
                f"{settings.LARA_PREFIXES['lara']}material/PartShape/{self.name.replace(' ', '_').lower().strip()}"
            )

        super().save(*args, **kwargs)


class Part(PartAbstr):
    """Generic Part class."""

    model_curi = "lara:material/Part"
    part_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    part_class = models.ForeignKey(
        PartDeviceClass,
        on_delete=models.CASCADE,
        blank=True,
        null=True,
        help_text="screw, bolt, nut, lamp, diode, resitor, IC, ...",
    )
    shape = models.ForeignKey(
        PartShape,
        on_delete=models.CASCADE,
        blank=True,
        null=True,
        help_text="shape of Part",
    )
    components = models.ManyToManyField(
        "self",
        related_query_name="%(app_label)s_%(class)s_components_related_query",
        blank=True,
        help_text="components of the part",
    )
    data_extra = models.ManyToManyField(
        ExtraData,
        related_name="%(app_label)s_%(class)s_parts_data_extra_related",
        related_query_name="%(app_label)s_%(class)s_parts_data_extra_related_query",
        blank=True,
        help_text="e.g. manual",
    )

    def save(self, *args, **kwargs):
        """Generate default values for type barcode and IRI."""
        if self.name is None or self.name == "":
            if self.name_display is not None or self.name_display != "":
                self.name = self.name_display.replace(" ", "_").lower().strip()

        if self.name is not None or self.name != "":
            self.name = self.name.replace(" ", "_").lower().strip()

        if self.name_display is None or self.name_display == "":
            self.name_display = self.name

        if self.type_barcode == "" or self.type_barcode is None:
            self.type_barcode = f"AUTO_{str(uuid.uuid4())[:8]}"  # avoiding unique constraint violation

        if self.iri is None or self.iri == "":
            self.iri = f"{settings.LARA_PREFIXES['lara']}/material/Part/{self.name.replace(' ', '_').lower().strip()}_{str(self.part_id)[:8]}"

        if self.datetime_last_modified is None:
            self.datetime_last_modified = timezone.now()

        super().save(*args, **kwargs)


class PartsSubset(ItemSubsetAbstr):
    """
    Parts subset.

    It can be used for user or group defined subsets of parts.
    """

    model_curi = "lara:material/PartsSubset"
    partssubset_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    parts = models.ManyToManyField(
        Part,
        related_name="%(app_label)s_%(class)s_parts_subset_related",
        related_query_name="%(app_label)s_%(class)s_parts_subset_related_query",
        blank=True,
        help_text="parts in the subset",
        through="OrderedPartsSubset",
    )


class OrderedPartsSubset(models.Model):
    """
    Through model for PartsSubset and Part relationship.

    This class can be used to store the order of parts in a subset.
    """

    model_curi = "lara:material/OrderedPartsSubset"

    orderedpartssubset_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

    part = models.ForeignKey(
        Part,
        related_name="%(app_label)s_%(class)s_part_related",
        related_query_name="%(app_label)s_%(class)s_part",
        on_delete=models.CASCADE,
        help_text="part in the subset",
    )

    parts_subset = models.ForeignKey(
        PartsSubset,
        related_name="%(app_label)s_%(class)s_parts_subset_related",
        related_query_name="%(app_label)s_%(class)s_parts_subset",
        on_delete=models.CASCADE,
        help_text="subset of parts",
    )

    order = models.IntegerField(help_text="order of part in subset")

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=["part", "parts_subset"],
                name="%(app_label)s_%(class)s_part_parts_subset_unique",
            ),
        ]
        ordering = ("order",)
