"""
DeviceSetup models for lara_django_material.

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

import uuid

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

from .base import ExtraData, PartAbstr, PartDeviceClass, PartShapeAbstr
from .devices import Device


class DeviceSetupShape(PartShapeAbstr):
    """DeviceSetup shape and dimensions."""

    model_curi = "lara:material/DeviceSetupShape"
    devicesetupshape_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    data_extra = models.ManyToManyField(
        ExtraData,
        related_name="%(app_label)s_%(class)s_dss_data_extra_related",
        related_query_name="%(app_label)s_%(class)s_dss_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/DeviceSetupShape/{self.name.replace(' ', '_').lower().strip()}"

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


class DeviceSetup(PartAbstr):
    """
    Setup or assembly of devices.

    E.g., setup of a physical or chemical experiment: distillation apparatus,
    flow reactor, ultra-high vacuum depositor, ... with all connectivities.
    """

    model_curi = "lara:material/DeviceSetup"
    devicesetup_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    setup_class = models.ForeignKey(
        PartDeviceClass,
        related_name="%(app_label)s_%(class)s_setup_classes_related",
        related_query_name="%(app_label)s_%(class)s_setup_classes_related_query",
        on_delete=models.CASCADE,
        blank=True,
        null=True,
        help_text="screw, bolt, nut, lamp, diode, resistor, IC, ...",
    )
    shape = models.ForeignKey(
        DeviceSetupShape,
        on_delete=models.CASCADE,
        blank=True,
        null=True,
        help_text="shape of Device setup",
    )
    components = models.ManyToManyField(
        Device,
        related_query_name="%(app_label)s_%(class)s_components_related_query",
        blank=True,
        help_text="components of the device setup",
    )
    blueprint_image = models.ImageField(
        upload_to="material/",
        blank=True,
        null=True,
        # default="",
        help_text="rel. path/filename to image",
    )
    blueprint_media_type = models.ForeignKey(
        MediaType,
        related_name="%(app_label)s_%(class)s_media_types_related",
        related_query_name="%(app_label)s_%(class)s_media_types_related_query",
        on_delete=models.CASCADE,
        blank=True,
        null=True,
        help_text="file type of extra data file",
    )
    blueprint_file = models.FileField(upload_to="material/", blank=True, null=True, help_text="blueprint file")
    blueprint_pdf = models.FileField(upload_to="material/", blank=True, null=True, help_text="blueprint PDF file")
    scheme_svg = models.TextField(blank=True, null=True, help_text="setup scheme in SVG")
    assembly = models.JSONField(blank=True, null=True, help_text="assembly with connections in JSON")
    data_extra = models.ManyToManyField(
        ExtraData,
        related_name="%(app_label)s_%(class)s_ds_data_extra_related",
        related_query_name="%(app_label)s_%(class)s_ds_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/Device/{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 DeviceSetupsSubset(ItemSubsetAbstr):
    """
    DeviceSetups subset.

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

    model_curi = "lara:material/DeviceSetupsSubset"
    devicesetupssubset_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    devicesetups = models.ManyToManyField(
        DeviceSetup,
        related_name="%(app_label)s_%(class)s_devicesetups_subset_related",
        related_query_name="%(app_label)s_%(class)s_devicesetups_subset_related_query",
        blank=True,
        help_text="device setups in the subset",
        through="OrderedDeviceSetupsSubset",
    )


class OrderedDeviceSetupsSubset(models.Model):
    """
    Through model for DeviceSetupsSubset and DeviceSetup relationship.

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

    model_curi = "lara:material/OrderedDeviceSetupsSubset"

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

    devicesetup = models.ForeignKey(
        DeviceSetup,
        related_name="%(app_label)s_%(class)s_devicesetup_related",
        related_query_name="%(app_label)s_%(class)s_devicesetup",
        on_delete=models.CASCADE,
        help_text="device setup in the subset",
    )

    devicesetups_subset = models.ForeignKey(
        DeviceSetupsSubset,
        related_name="%(app_label)s_%(class)s_devicesetups_subset_related",
        related_query_name="%(app_label)s_%(class)s_devicesetups_subset",
        on_delete=models.CASCADE,
        help_text="subset of device setups",
    )

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

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=["devicesetup", "devicesetups_subset"],
                name="%(app_label)s_%(class)s_devicesetup_devicesetups_subset_unique",
            ),
        ]
        ordering = ("order",)
