"""
_____________________________________________________________________.

:PROJECT: LARAsuite

*lara_django_base models *

:details: lara_django_base database models.
         -
         - fixtures can be generated with
         lara-django-dev dumpdata lara_django_base.itemstatus --indent 2 --natural-primary > 2_itemstatus_fix.json

         model_curi are compact URIs (CURIs) for the model, e.g. lara:base/ItemStatus.
         They are used for semantic representation.

:authors: mark doerr <mark.doerr@uni-greifswald.de>
          David Lacko @deiwo
          Jan Velecký

.. note:: -
.. todo:: -
________________________________________________________________________
"""

import datetime
import hashlib
import json
import uuid
from typing import Any
from urllib.parse import urlparse

from django.conf import settings
from django.contrib.postgres.indexes import GinIndex
from django.contrib.postgres.search import SearchVectorField
from django.core.validators import RegexValidator
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from mptt.models import MPTTModel, TreeForeignKey

from .models_base import Currency, ExtraData, Namespace

settings.FIXTURES += [
    "3_geolocation_fix",
    # "4_location_fix",
    "4_room_category_fix",
    "1_countries_fix",
    "3_country_subdiv_type_fix",
    # "4_country_subdiv_fix",
    "4_cities_fix",
]


class URNField(models.CharField):
    """URN field that accepts URLs that adhere the scheme for URNs  only."""

    default_validators = [
        RegexValidator(
            regex=r"^urn:[a-z0-9][a-z0-9-]{1,31}:([a-z0-9()+,-.:=@;$_!*\'%/?#]|%[0-9a-f][0-9a-f])*$",
            message="Please enter a valid URN, s. https://en.wikipedia.org/wiki/Uniform_Resource_Name",
            code="invalid_URN",
        )
    ]  # [URLValidator(schemes=['urn'])]

    def __init__(self, *args: Any, **kwargs: Any) -> None:
        super().__init__(*args, **kwargs)
        self.max_length = 512



class GeoLocation(models.Model):
    """
    Generic geo location information class to store geo location data.

    Like Decimal Degrees of the location of, e.g. a building, a sample, etc. ...

     .. todo:: accept only one input format, add converters to common formats
    """

    model_curi = "lara:base/GeoLocation"
    geolocation_id = models.UUIDField(
        primary_key=True, default=uuid.uuid4, editable=False
    )
    name = models.TextField(unique=True, help_text="Unique location name")
    iri = models.URLField(
        blank=True,
        null=True,
        unique=True,
        max_length=512,
        help_text="International Resource Identifier - IRI: is used for semantic representation ",
    )
    coordinates_dd_lat = models.DecimalField(
        max_digits=32,
        decimal_places=24,
        help_text="in Decimal degrees (DD) - latitude, e.g., 54.0915068",
    )
    coordinates_dd_long = models.DecimalField(
        max_digits=32,
        decimal_places=24,
        help_text="in Decimal degrees (DD) - longitude, e.g., 13.4029247",
    )
    elevation = models.IntegerField(
        null=True, blank=True, help_text="elevation in m above sea level"
    )
    # this could be autogenerated upon saved...
    openstreetmap = models.URLField(
        blank=True,
        max_length=1024,
        help_text="OpenStreetMap (www.openstreetmap.org) link to address",
    )
    # this could be autogenerated upon saved...
    googlemap = models.URLField(
        blank=True, max_length=1024, help_text="google maps link to address"
    )
    data_extra = models.ManyToManyField(
        ExtraData,
        related_name="%(app_label)s_%(class)s_data_extra_related",
        related_query_name="%(app_label)s_%(class)s_data_extra_related_query",
        blank=True,
        help_text="this can be used to extend the geo location: like maps to location etc.",
    )
    datetime_last_modified = models.DateTimeField(
        null=True, blank=True, help_text="date and time when data was last modified"
    )
    description = models.TextField(
        blank=True, help_text="description of the geo location"
    )

    class Meta:
        db_table_comment = (
            "Generic geo location information class to store geo location data,"
            "like Decimal Degrees of the location of, e.g. a building, a sample, etc. ..."
        )

    def __str__(self):
        return self.name or ""  # self.openstreetmap

    def save(self, *args, **kwargs):
        """
        Here we generate some default values for the IRI
        """
        if not self.iri:
            self.iri = (
                settings.LARA_PREFIXES["lara"]
                + "base/GeoLocation/"
                + self.name.replace(" ", "_").lower().strip()
            )

        self.datetime_last_modified = timezone.now()

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

    # coordinate converters
    def to_dms(self):
        """converting DD to Degrees, minutes, and seconds (DMS)."""
        # return DD_to_DMS( self.coordinates_dd_lat, self.coordinates_dd_long)
        # coordinates_DMS =  models.TextField(blank=True, default="""0deg0'0.0"N 0deg0'0.0"E""",
        # help_text="""in Degrees, minutes, and seconds (DMS): 54deg5'29.434"N 13deg24'10.527"E""")
        # # might need a syntax checker

    def to_dmm(self):
        """converting DD to Degrees and decimal minutes (DMM)"""
        # return DD_to_DMM(self.coordinates_dd_lat, self.coordinates_dd_long)
        # coordinates_DMM =  models.TextField(blank=True, default="0 0.0, 0 0.0",
        #  help_text="in Degrees and decimal minutes (DMM): 41 24.2028, 2 10.4418")


class Country(models.Model):
    """generic country representation class"""

    model_curi = "lara:base/Country"
    country_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.TextField(unique=True,
                            help_text="international name of the country")
    name_local = models.TextField(
        unique=True,
        blank=True, help_text="local name of the country, e.g. Deutschland"
    )
    alpha2code = models.CharField(
        max_length=2,
        unique=True,
        blank=True,
        help_text="International 2-letter code, like DE, SE, DK ...",
    )
    alpha3code = models.CharField(
        max_length=3,
        unique=True,
        help_text="International 3-letter code, like DEU, USA, HEL ..."
    )
    numeric_code = models.IntegerField(
        unique=True,
        blank=True,
        null=True,
        help_text="International 3 number code, like 276 (for DEU)",
    )
    phone_code = models.IntegerField(
        blank=True,
        null=True,
        unique=True,
        help_text="International telephone code, like 49, 45, ...",
    )
    icon = models.TextField(
        blank=True, help_text="XML/SVG icon / symbol / flag of the country"
    )
    currency = models.ForeignKey(
        Currency,
        related_name="%(app_label)s_%(class)s_currencies_related",
        related_query_name="%(app_label)s_%(class)s_currencies_related_query",
        on_delete=models.CASCADE,
        blank=True,
        null=True,
        help_text="currency",
    )
    iri = models.URLField(
        blank=True,
        null=True,
        unique=True,
        max_length=512,
        help_text="International Resource Identifier - IRI: is used for semantic representation ",
    )
    timezone = models.IntegerField(
        blank=True, null=True, help_text="time zone offset from UTC, e.g. 5, -3 "
    )
    description = models.TextField(blank=True, help_text="description of the country")

    class Meta:
        verbose_name_plural = "Countries"
        db_table_comment = "Generic country representation class"

    def __str__(self):
        return self.name_local or ""

    def save(self, *args, **kwargs):
        """
        Here we generate some default values for the IRI
        """
        if not self.iri:
            self.iri = (
                settings.LARA_PREFIXES["lara"]
                + "base/Country/"
                + self.name.replace(" ", "_").lower().strip()
            )

        if self.country_id is None:
            if self.numeric_code is not None:
                self.country_id = uuid.uuid5(uuid.NAMESPACE_URL, self.numeric_code)
            elif self.alpha3code is not None and self.alpha3code != "":
                self.country_id = uuid.uuid5(uuid.NAMESPACE_URL, self.alpha3code)
            else:
                self.country_id = uuid.uuid4()

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


class SubdivisionType(models.Model):
    """Country Subdivision Type: e.g. State, Province, Emirate, ..."""

    model_curi = "lara:base/SubdivisionType"
    subdivisiontype_id = models.UUIDField(
        primary_key=True, default=uuid.uuid4, editable=False
    )  # models.AutoField( primary_key=True)  #
    name = models.TextField(
        unique=True,
        help_text="subdivision type, e.g. State, Province, Emirate, ... ",
    )
    iri = models.URLField(
        blank=True,
        null=True,
        unique=True,
        max_length=512,
        help_text="International Resource Identifier - IRI: is used for semantic representation ",
    )
    description = models.TextField(
        blank=True, help_text="description of subdivision type"
    )

    class Meta:
        db_table_comment = (
            "Country Subdivision Type: e.g. State, Province, Emirate, ..."
        )

    def __str__(self):
        return self.name or ""

    def save(self, *args, **kwargs):
        """
        Here we generate some default values for the IRI
        """
        if not self.iri:
            self.iri = (
                settings.LARA_PREFIXES["lara"]
                + "base/SubdivisionType/"
                + self.name.replace(" ", "_").lower().strip()
            )

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


class CountrySubdivision(models.Model):
    """Generic country subdivision  to fit federal states, provinces, counties etc. into the same concept
    Not limited to US federal states - also Germany and many other countries have them, s. pypi iso3166 and pycountry
    """

    model_curi = "lara:base/CountrySubdivision"
    countrysubdivision_id = models.UUIDField(
        primary_key=True, default=uuid.uuid4, editable=False
    )
    name = models.TextField(
        blank=True,
        help_text="international name of the subdivision/federal state, Hessia, Thuringia, Baveria, ...",
    )
    name_local = models.TextField(
        blank=True,
        help_text="local name of the state, e.g. Hessen, Thüringen",
    )
    code = models.CharField(
        max_length=16,
        blank=True,
        help_text="multi-letter code, like US-MA, DE-TH ,  ...",
    )
    alpha2code = models.CharField(
        max_length=3,
        blank=True,
        help_text="2-letter code, like MA, BY, DK ...",
    )
    subdivision_type = models.ForeignKey(
        SubdivisionType,
        related_name="%(app_label)s_%(class)s_subdivision_types_related",
        related_query_name="%(app_label)s_%(class)s_subdivision_types_related_query",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
    )
    country = models.ForeignKey(
        Country,
        related_name="%(app_label)s_%(class)s_countries_related",
        related_query_name="%(app_label)s_%(class)s_countries_related_query",
        on_delete=models.CASCADE,
        help_text="country",
    )
    timezone = models.IntegerField(
        blank=True, null=True, help_text="time zone offset from UTC, e.g. 5, -3 "
    )
    iri = models.URLField(
        blank=True,
        null=True,
        unique=True,
        max_length=512,
        help_text="International Resource Identifier - IRI: is used for semantic representation ",
    )
    description = models.TextField(
        blank=True, help_text="description of the country subdivision"
    )

    class Meta:
        db_table_comment = ("Generic country subdivision  to fit federal states,"
                            "provinces, counties etc. into the same concept.")
        constraints = [
            models.UniqueConstraint(
                fields=["name", "country"],
                name="%(app_label)s_%(class)s_name_country_unique",
            ),
        ]

    def __str__(self):
        if self.name_local != "":
            return f"{self.name} ({self.name_local})" or ""
        return self.name or ""

    def __repr__(self):
        if self.name_local != "":
            return f"{self.name} ({self.name_local})" or ""
        return self.name or ""

    def save(self, *args, **kwargs):
        """
        Here we generate some default values for the IRI
        """
        if not self.iri:
            self.iri = (
                settings.LARA_PREFIXES["lara"]
                + "base/CountrySubdivision/"
                + self.name.replace(" ", "_").lower().strip()
            )

        if self.timezone is None:
            self.timezone = self.country.timezone
        super().save(*args, **kwargs)


class City(models.Model):
    """Generic city representation class"""

    model_curi = "lara:base/City"
    city_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.TextField(
         help_text="international name of the city, e.g. Munich"
    )
    name_local = models.TextField(
        blank=True, help_text="local name of the city, e.g. München"
    )
    country = models.ForeignKey(
        Country,
        related_name="%(app_label)s_%(class)s_contries_related",
        related_query_name="%(app_label)s_%(class)s_countries_related_query",
        on_delete=models.CASCADE,
        null=True,
        help_text="country",
    )
    subdivision = models.ForeignKey(
        CountrySubdivision,
        related_name="%(app_label)s_%(class)s_subdivisions_related",
        related_query_name="%(app_label)s_%(class)s_subdivisions_related_query",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        help_text="country subdivision where the city is loacted",
    )
    timezone = models.IntegerField(
        blank=True, null=True, help_text="time zone offset from UTC, e.g. 5, -3 "
    )
    # ZIP codes (in extra table) ?
    iri = models.URLField(
        blank=True,
        null=True,
        unique=True,
        max_length=512,
        help_text="International Resource Identifier - IRI: is used for semantic representation ",
    )
    description = models.TextField(blank=True, help_text="description of the city")
    search_vector = SearchVectorField(null=True)

    class Meta:
        verbose_name_plural = "Cities"
        db_table_comment = "Generic city representation class"
        indexes = (GinIndex(fields=["search_vector"]),)
        constraints = [
            models.UniqueConstraint(
                fields=["name", "country"],
                name="%(app_label)s_%(class)s_name_country_unique",
            ),
        ]

    def __str__(self):
        return f"{self.name} ({self.name}/{self.country})" or ""

    def __repr__(self):
        return f"{self.name} ({self.name}/{self.country})" or ""

    def save(self, *args, **kwargs):
        """
        Here we generate some default values for the IRI
        """
        if not self.iri:
            self.iri = (
                settings.LARA_PREFIXES["lara"]
                + "base/City/"
                + self.name.replace(" ", "_").lower().strip()
            )

        if self.country is not None and self.timezone is None:
            self.timezone = self.country.timezone

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


class Address(models.Model):
    """generic Address class:
    this class can be used to store all kind of Addresses"""

    model_curi = "lara:base/Address"
    address_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    namespace = models.ForeignKey(
        Namespace,
        related_name="%(app_label)s_%(class)s_namespaces_related",
        related_query_name="%(app_label)s_%(class)s_namespaces_related_query",
        on_delete=models.CASCADE,
        help_text="namespace of address",
    )
    iri = models.URLField(
        blank=True,
        null=True,
        unique=True,
        max_length=512,
        help_text="International Resource Identifier - IRI: is used for semantic representation ",
    )
    building = models.TextField(
        blank=True,
        help_text="in case the institution is within a bigger complex/campus",
    )
    street = models.TextField(blank=True, help_text="street, excluding house number")
    house_number = models.TextField(blank=True, help_text="house number")
    supplement = models.TextField(
        blank=True,
        help_text="address supplement info to make it unique, like c/o, room/floor",
    )
    city = models.ForeignKey(
        City,
        related_name="%(app_label)s_%(class)s_cities_related",
        related_query_name="%(app_label)s_%(class)s_cities_related_query",
        on_delete=models.CASCADE,
        blank=True,
        null=True,
        help_text="city",
    )
    subdivision = models.ForeignKey(
        CountrySubdivision,
        related_name="%(app_label)s_%(class)s_subdivisions_related",
        related_query_name="%(app_label)s_%(class)s_subdivisions_related_query",
        on_delete=models.CASCADE,
        blank=True,
        null=True,
        help_text="e.g. federal state, like Thuringia or Alaska",
    )
    country = models.ForeignKey(
        Country,
        related_name="%(app_label)s_%(class)s_countries_related",
        related_query_name="%(app_label)s_%(class)s_countries_related_query",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        help_text="country",
    )
    postal_code = models.TextField(blank=True, help_text="postal code / ZIP")
    geolocation = models.ForeignKey(
        GeoLocation,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        help_text="Geographical location",
    )
    data_extra = models.ManyToManyField(
        ExtraData,
        related_name="%(app_label)s_%(class)s_data_extra_related",
        related_query_name="%(app_label)s_%(class)s_data_extra_related_query",
        blank=True,
        help_text="extra information to addresses",
    )
    datetime_last_modified = models.DateTimeField(
        null=True, blank=True, help_text="date and time when data was last modified"
    )
    # iri = models.URLField(
    #     blank=True,  null=True, unique=True, max_length=512,
    #   help_text="International Resource Identifier - IRI: is used for semantic representation ")
    description = models.TextField(blank=True, help_text="description of the address")
    search_vector = SearchVectorField(null=True)

    class Meta:
        verbose_name_plural = "Addresses"
        db_table_comment = "Generic Address class: this class can be used to store all kind of Addresses"
        indexes = (
            GinIndex(fields=["search_vector"]),
            models.Index(
                fields=["namespace", "street", "postal_code", "city"],
                name="namespace_str_post_city_idx",
            ),
        )
        constraints = [
            models.UniqueConstraint(
                fields=["street", "house_number", "building", "postal_code", "city"],
                name="%(app_label)s_%(class)s_street_house_number_building_postal_code_city_unique",
            ),
        ]

    def __str__(self):
        house_number = self.house_number or ""
        postal_code = self.postal_code or ""
        alpha2code = self.country.alpha2code + "-" if self.country else ""
        return (
            f"{self.street} {house_number}, {alpha2code}{postal_code}, {self.city}"
            or ""
        )

    def __repr__(self):
        house_number = self.house_number or ""
        building = self.building or ""

        alpha2code = self.country.alpha2code + "-" if self.country else ""
        return (
            f"{self.street}, {house_number}, ({building}) {alpha2code}{self.postal_code}, {self.city}"
            or ""
        )

    def save(self, *args, **kwargs):
        """
        Here we generate some default values for the IRI
        """

        if not self.iri:
            self.iri = (
                settings.LARA_PREFIXES["lara"] + "base/Address/" + str(self.address_id)
            )

        self.datetime_last_modified = timezone.now()

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

    @property
    def name(self):
        """Returns a concatenation of street, building, house_number, postal_code, city and country"""
        building_info = f"({self.building})/" if self.building else ""
        number_info = self.house_number or ""
        postal_code = self.postal_code or ""
        city = self.city.name or ""
        country = self.country.name or ""

        return f"{building_info}{self.street}/{number_info}/{postal_code}/{city}/{country}".strip()


class RoomCategory(models.Model):
    """room category: office, lab, lab_S1, lab_S2, ... seminar room"""

    model_curi = "lara:base/RoomCategory"
    roomcategory_id = models.UUIDField(
        primary_key=True, default=uuid.uuid4, editable=False
    )
    category = models.TextField(unique=True, help_text="room category")
    iri = models.URLField(
        blank=True,
        null=True,
        unique=True,
        max_length=512,
        help_text="International Resource Identifier - IRI: is used for semantic representation ",
    )
    description = models.TextField(blank=True, help_text="room category description")

    class Meta:
        db_table_comment = (
            "Room category: office, lab, lab_S1, lab_S2, ... seminar room"
        )
        verbose_name_plural = "RoomCategories"

    def __str__(self):
        return self.category or ""

    def __repr__(self):
        return self.category or ""

    def save(self, *args, **kwargs):
        """
        Here we generate some default values for the IRI
        """
        if not self.iri:
            self.iri = (
                settings.LARA_PREFIXES["lara"]
                + "base/RoomCategory/"
                + self.category.replace(" ", "_").lower().strip()
            )

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


class Room(models.Model):
    """Rooms and laboratories"""

    model_curi = "lara:base/Room"
    room_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    namespace = models.ForeignKey(
        Namespace,
        related_name="%(app_label)s_%(class)s_namespaces_related",
        related_query_name="%(app_label)s_%(class)s_namespaces_related_query",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        help_text="namespace of room",
    )

    name = models.TextField(
        blank=True, help_text="name of the room - some rooms have names"
    )
    category = models.ForeignKey(
        RoomCategory,
        related_name="%(app_label)s_%(class)s_categories_related",
        related_query_name="%(app_label)s_%(class)s_categories_related_query",
        on_delete=models.CASCADE,
        blank=True,
        null=True,
        help_text="room category",
    )
    number = models.TextField(blank=True, help_text="room number")
    floor = models.TextField(blank=True, help_text="Floor")
    phone_number = models.TextField(
        blank=True,
        help_text="room phone number, if possible add the +Country code notation, e.g., +44 3834 420 4411",
    )
    # address = models.ForeignKey(
    #     Address,
    #     related_name="%(app_label)s_%(class)s_addresses_related",
    #     related_query_name="%(app_label)s_%(class)s_addresses_related_query",
    #     on_delete=models.CASCADE,
    #     blank=True,
    #     null=True,
    #     help_text="reference to an address",
    # )  # check, if it can be 0 !
    room_map = models.ImageField(
        upload_to="people/rooms/",
        blank=True,
        null=True,
        help_text="location room map rel. path/filename to image",
    )
    # further info about the room
    max_people = models.IntegerField(
        null=True, blank=True, help_text="max number of people for this room"
    )
    area = models.DecimalField(
        max_digits=9,
        decimal_places=2,
        blank=True,
        null=True,
        help_text="room area in square meters",
    )
    volume = models.DecimalField(
        max_digits=9,
        decimal_places=2,
        blank=True,
        null=True,
        help_text="room volume in cubic meters",
    )
    features = models.JSONField(
        blank=True,
        null=True,
        help_text="features of this room, like sockets, lights, gas, furniture, infrastructure,...",
    )
    data_extra = models.ManyToManyField(
        ExtraData,
        related_name="%(app_label)s_%(class)s_room_data_extra_related",
        related_query_name="%(app_label)s_%(class)s_room_data_extra_related_query",
        blank=True,
        help_text="extra information for this room",
    )
    iri = models.URLField(
        blank=True,
        null=True,
        unique=True,
        max_length=512,
        help_text="International Resource Identifier - IRI: is used for semantic representation ",
    )
    description = models.TextField(blank=True, help_text="room description")

    datetime_last_modified = models.DateTimeField(
        null=True, blank=True, help_text="date and time when data was last modified"
    )
    search_vector = SearchVectorField(null=True)

    class Meta:
        db_table_comment = "Rooms and laboratories"
        indexes = (GinIndex(fields=["search_vector"]),)
        constraints = [
            models.UniqueConstraint(
                fields=["namespace", "name", "number", "floor"],
                name="%(app_label)s_%(class)s_namespace_name_number_floor_unique",
            ),
        ]

    def __str__(self):
        return f"{self.name}" or ""

    def __repr__(self):
        return f"{self.name}" or ""

    def save(self, *args, **kwargs):
        """
        Here we generate some default values for the IRI
        """
        if not self.iri:
            self.iri = (
                settings.LARA_PREFIXES["lara"]
                + "base/Room/"
                + self.name.replace(" ", "_").lower().strip()
            )

        self.datetime_last_modified = timezone.now()

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


class LocationType(models.Model):
    """Location type: e.g. shelf, drawer, box, container, device, ..."""

    model_curi = "lara:base/LocationType"
    locationtype_id = models.UUIDField(
        primary_key=True, default=uuid.uuid4, editable=False
    )
    name = models.TextField(unique=True, help_text="location type name")
    iri = models.URLField(
        blank=True,
        null=True,
        unique=True,
        max_length=512,
        help_text="International Resource Identifier - IRI: is used for semantic representation ",
    )
    description = models.TextField(blank=True, help_text="description of the location type")

    class Meta:
        db_table_comment = "Location type: e.g. shelf, drawer, box, container, device, ..."
        verbose_name_plural = "LocationTypes"

    def __str__(self):
        return self.name or ""

    def __repr__(self):
        return self.name or ""

class Location(MPTTModel):
    """Physical location of objects like device, substances, organisms, containers etc.,
    at concrete place, e.g., in shelf or drawer in a lab or room.
    .. todo: a foreign key to a device for modelling a location in a device could be considered,
             but generates an additional dependency.....
    """

    model_curi = "lara:base/Location"
    location_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    namespace = models.ForeignKey(
        Namespace,
        related_name="%(app_label)s_%(class)s_namespaces_related",
        related_query_name="%(app_label)s_%(class)s_namespaces_related_query",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        help_text="namespace of location",
    )
    # name_display = models.TextField(
    #     help_text="display name of the location - some locations have display names"
    #     )
    name = models.TextField(unique=True, help_text="Unique location name")
    location_type = models.ForeignKey(
        LocationType,
        related_name="%(app_label)s_%(class)s_location_types_related",
        related_query_name="%(app_label)s_%(class)s_location_types_related_query",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        help_text="location type, e.g. shelf, drawer, box, container, device, ...",
    )
    parent = TreeForeignKey(
        "self",
        null=True,
        blank=True,
        related_name="%(app_label)s_%(class)s_experiment_parent_related",
        related_query_name="%(app_label)s_%(class)s_parent_related_query",
        on_delete=models.CASCADE,
        help_text="mptt - parent projects",
    )
    barcode = models.TextField(
        blank=True,
        unique=True,
        help_text="text representation of a barcode of the location.",
    )
    barcode_json = models.JSONField(
        blank=True,
        null=True,
        help_text="Advanced barcodes in JSON for multiple barcode representations, like QR codes etc.",
    )
    geolocation = models.ForeignKey(
        GeoLocation,
        related_name="%(app_label)s_%(class)s_geolocations_related",
        related_query_name="%(app_label)s_%(class)s_geolocations_related_query",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        help_text="geo location",
    )
    address = models.ForeignKey(
        Address,
        related_name="%(app_label)s_%(class)s_addresses_related",
        related_query_name="%(app_label)s_%(class)s_addresses_related_query",
        on_delete=models.CASCADE,
        blank=True,
        null=True,
        help_text="reference to an address",
    )
    location_json = models.JSONField(
        blank=True,
        null=True,
        help_text="JSON field for advanced location specification - can be used to store, e.g. locations/"
                  "positions in a device",
    )
    iri = models.URLField(
        blank=True,
        null=True,
        unique=True,
        max_length=512,
        help_text="International Resource Identifier - IRI: is used for semantic representation ",
    )
    # add PAC-ID
    url = models.URLField(
        blank=True,
        help_text="Universal Resource Locator - this can be used to link this location to an external location",
    )
    data_extra = models.ManyToManyField(
        ExtraData,
        related_name="%(app_label)s_%(class)s_data_extra_related",
        related_query_name="%(app_label)s_%(class)s_data_extra_related_query",
        blank=True,
        help_text="like maps to location etc.",
    )
    datetime_last_modified = models.DateTimeField(
        null=True, blank=True, help_text="date and time when data was last modified"
    )
    description = models.TextField(blank=True, help_text="location description")
    search_vector = SearchVectorField(null=True)

    class Meta:
        db_table_comment = (
            "Physical location of objects like device, substances, organisms, containers"
            "etc., at concrete place, e.g., in shelf or drawer in a lab or room."
        )
        indexes = (GinIndex(fields=["search_vector"]),)

    def __str__(self):
        return self.name or ""

    def __repr__(self):
        return f"{self.name} {self.description}" or ""

    def save(self, *args, **kwargs):
        """
        Here we generate some default values for the IRI
        """
        if not self.iri:
            self.iri = (
                settings.LARA_PREFIXES["lara"]
                + "base/Location/"
                + self.name.replace(" ", "_").lower().strip()
            )

        if self.barcode == "" or self.barcode is None:
            self.barcode = f"AUTO_{str(uuid.uuid4())[:8]}"

        self.datetime_last_modified = timezone.now()

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

