"""Taxonomy-related models for lara_django_organisms.

This module contains models for the taxonomic hierarchy including
Domain, Regnum, Phylum, Classis, Ordo, Familia, Genus, Species,
Subspecies, Variant, and Cultivar.

:authors: mark doerr <mark.doerr@uni-greifswald.de>
"""

import uuid

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

from .base import Trait


class Domain(models.Model):
    """Taxonomic domain / super kingdom / empire.

    Examples: Archaea, Bacteria, and Eukarya
    """

    model_curi = "lara:organisms/Domain"
    domain_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name_display = models.TextField(
        help_text="human readable display name of the domain, can contain spaces"
    )
    name = models.TextField(
        blank=True,
        null=True,
        help_text="domain name: Archaea, Bacteria, and Eukarya",
    )
    iri = models.URLField(
        blank=True,
        null=True,
        unique=True,
        max_length=512,
        help_text="International Resource Identifier - IRI: is used for semantic representation",
    )
    traits = models.ManyToManyField(
        Trait,
        related_name="%(app_label)s_%(class)s_domain_traits_related",
        related_query_name="%(app_label)s_%(class)s_domain_traits",
        blank=True,
        help_text="General domain's traits / features / attributes",
    )
    characteristics = models.JSONField(
        blank=True,
        null=True,
        help_text="General characteristics of Domain. Use 'traits' for specific traits.",
    )
    description = models.TextField(
        null=True,
        blank=True,
        help_text="description of domain",
    )
    datetime_last_modified = models.DateTimeField(
        blank=True,
        null=True,
        help_text="date and time when habitat parameter data was last modified",
    )

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

        if self.iri is None or self.iri == "":
            self.iri = (
                f"{settings.LARA_PREFIXES['lara']}organisms/domain/"
                f"{self.name.replace(' ', '_').lower().strip()}"
            )
        self.datetime_last_modified = timezone.now()
        super().save(*args, **kwargs)

    def __str__(self):
        """Return the name of the domain."""
        return self.name or ""

    def __repr__(self):
        """Return the name of the domain."""
        return self.name or ""


class Regnum(models.Model):
    """Taxonomic Regnum (Kingdom).

    Examples: Animalia, Plantae, Fungi, Protista,
    Archaea/Archaebacteria, and Bacteria/Eubacteria

    Note: might be deprecated
    """

    model_curi = "lara:organisms/Regnum"
    regnum_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name_display = models.TextField(
        help_text="human readable display name of the regnum, can contain spaces"
    )
    name = models.TextField(
        blank=True,
        null=True,
        help_text="Regnum name, like Plantae, Fungi",
    )
    iri = models.URLField(
        blank=True,
        null=True,
        unique=True,
        max_length=512,
        help_text="International Resource Identifier - IRI: is used for semantic representation",
    )
    traits = models.ManyToManyField(
        Trait,
        related_name="%(app_label)s_%(class)s_regnum_traits_related",
        related_query_name="%(app_label)s_%(class)s_regnum_traits",
        blank=True,
        help_text="General regnum's traits / features / attributes",
    )
    characteristics = models.JSONField(
        blank=True,
        null=True,
        help_text="General characteristics of Regnum, use 'traits' for specific traits.",
    )
    description = models.TextField(
        null=True,
        blank=True,
        help_text="description of Regnum",
    )
    datetime_last_modified = models.DateTimeField(
        blank=True,
        null=True,
        help_text="date and time when habitat parameter data was last modified",
    )

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

        if self.iri is None or self.iri == "":
            self.iri = (
                f"{settings.LARA_PREFIXES['lara']}organisms/regnum/"
                f"{self.name.replace(' ', '_').lower().strip()}"
            )
        self.datetime_last_modified = timezone.now()
        super().save(*args, **kwargs)

    def __str__(self):
        """Return the name of the regnum."""
        return self.name or ""

    def __repr__(self):
        """Return the name of the regnum."""
        return self.name or ""

    class Meta:
        """Meta options for Regnum model."""

        verbose_name_plural = "regna"


class Phylum(models.Model):
    """Taxonomic Phylum / Devisio / Division."""

    model_curi = "lara:organisms/Phylum"
    phylum_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name_display = models.TextField(
        help_text="human readable display name of the phylum, can contain spaces"
    )
    name = models.TextField(
        blank=True,
        null=True,
        help_text="phylum name, like ",
    )
    name_common = models.TextField(
        blank=True,
        null=True,
        help_text="common phylum name, like ",
    )
    traits = models.ManyToManyField(
        Trait,
        related_name="%(app_label)s_%(class)s_phylum_traits_related",
        related_query_name="%(app_label)s_%(class)s_phylum_traits",
        blank=True,
        help_text="General phylum's traits / features / attributes",
    )
    characteristics = models.JSONField(
        blank=True,
        null=True,
        help_text="Generic characteristics of phylum. Use 'traits' for specific traits.",
    )
    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(
        null=True,
        blank=True,
        help_text="description of phylum",
    )
    datetime_last_modified = models.DateTimeField(
        blank=True,
        null=True,
        help_text="date and time when habitat parameter data was last modified",
    )

    def save(self, *args, **kwargs):
        """Generate default values for name and IRI."""
        if self.name is None or self.name == "":
            self.name = self.name_display.replace(" ", "_").lower().strip()
        if self.iri is None or self.iri == "":
            self.iri = (
                f"{settings.LARA_PREFIXES['lara']}organisms/phylum/"
                f"{self.name.replace(' ', '_').lower().strip()}"
            )
        self.datetime_last_modified = timezone.now()
        super().save(*args, **kwargs)

    def __str__(self):
        """Return the name of the phylum."""
        return self.name or ""

    def __repr__(self):
        """Return the name of the phylum."""
        return self.name or ""

    class Meta:
        """Meta options for Phylum model."""

        verbose_name_plural = "Phyla"


class Classis(models.Model):
    """Taxonomic Classis / Class."""

    model_curi = "lara:organisms/Classis"
    classis_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name_display = models.TextField(
        help_text="human readable display name of the classis, can contain spaces"
    )
    name = models.TextField(
        blank=True,
        null=True,
        help_text="classis name, like ",
    )
    name_common = models.TextField(
        blank=True,
        null=True,
        help_text="common classis name, like ",
    )
    traits = models.ManyToManyField(
        Trait,
        related_name="%(app_label)s_%(class)s_classis_traits_related",
        related_query_name="%(app_label)s_%(class)s_classis_traits",
        blank=True,
        help_text="General classis's traits / features / attributes",
    )
    characteristics = models.JSONField(
        blank=True,
        null=True,
        help_text="generic characteristics of this classis, use 'traits' for specific traits.",
    )
    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(
        null=True,
        blank=True,
        help_text="description of this classis",
    )
    datetime_last_modified = models.DateTimeField(
        blank=True,
        null=True,
        help_text="date and time when habitat parameter data was last modified",
    )

    def save(self, *args, **kwargs):
        """Generate default values for name and IRI."""
        if self.name is None or self.name == "":
            self.name = self.name_display.replace(" ", "_").lower().strip()
        if self.iri is None or self.iri == "":
            self.iri = (
                f"{settings.LARA_PREFIXES['lara']}organisms/classis/"
                f"{self.name.replace(' ', '_').lower().strip()}"
            )
        self.datetime_last_modified = timezone.now()
        super().save(*args, **kwargs)

    def __str__(self):
        """Return the name of the classis."""
        return self.name or ""

    def __repr__(self):
        """Return the name of the classis."""
        return self.name or ""

    class Meta:
        """Meta options for Classis model."""

        verbose_name_plural = "Classes"


class Ordo(models.Model):
    """Taxonomic Ordo / Order."""

    model_curi = "lara:organisms/Ordo"
    ordo_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name_display = models.TextField(
        help_text="human readable display name of the ordo, can contain spaces"
    )
    name = models.TextField(
        blank=True,
        null=True,
        help_text="ordo name, like ",
    )
    name_common = models.TextField(
        blank=True,
        null=True,
        help_text="common ordo name, like ",
    )
    traits = models.ManyToManyField(
        Trait,
        related_name="%(app_label)s_%(class)s_ordo_traits_related",
        related_query_name="%(app_label)s_%(class)s_ordo_traits",
        blank=True,
        help_text="General ordo's traits / features / attributes",
    )
    characteristics = models.JSONField(
        blank=True,
        null=True,
        help_text="Generic characteristics of this ordo, use 'traits' for specific traits.",
    )
    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(
        null=True,
        blank=True,
        help_text="description of this ordo",
    )
    datetime_last_modified = models.DateTimeField(
        blank=True,
        null=True,
        help_text="date and time when habitat parameter data was last modified",
    )

    def save(self, *args, **kwargs):
        """Generate default values for name and IRI."""
        if self.name is None or self.name == "":
            self.name = self.name_display.replace(" ", "_").lower().strip()
        if self.iri is None or self.iri == "":
            self.iri = (
                f"{settings.LARA_PREFIXES['lara']}organisms/ordo/"
                f"{self.name.replace(' ', '_').lower().strip()}"
            )
        self.datetime_last_modified = timezone.now()
        super().save(*args, **kwargs)

    def __str__(self):
        """Return the name of the ordo."""
        return self.name or ""

    def __repr__(self):
        """Return the name of the ordo."""
        return self.name or ""

    class Meta:
        """Meta options for Ordo model."""

        verbose_name_plural = "Ordae"


class Familia(models.Model):
    """Taxonomic Familia / Family."""

    model_curi = "lara:organisms/Familia"
    familia_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name_display = models.TextField(
        help_text="human readable display name of the familia, can contain spaces"
    )
    name = models.TextField(
        blank=True,
        null=True,
        help_text="familia name, like ",
    )
    name_common = models.TextField(
        blank=True,
        null=True,
        help_text="common familia name, like ",
    )
    traits = models.ManyToManyField(
        Trait,
        related_name="%(app_label)s_%(class)s_familia_traits_related",
        related_query_name="%(app_label)s_%(class)s_familia_traits",
        blank=True,
        help_text="General familia's traits / features / attributes",
    )
    characteristics = models.JSONField(
        blank=True,
        null=True,
        help_text="Generic characteristics of this familia, use 'traits' for specific traits.",
    )
    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(
        null=True,
        blank=True,
        help_text="description of this familia",
    )
    datetime_last_modified = models.DateTimeField(
        blank=True,
        null=True,
        help_text="date and time when habitat parameter data was last modified",
    )

    def save(self, *args, **kwargs):
        """Generate default values for name and IRI."""
        if self.name is None or self.name == "":
            self.name = self.name_display.replace(" ", "_").lower().strip()
        if self.iri is None or self.iri == "":
            self.iri = (
                f"{settings.LARA_PREFIXES['lara']}organisms/familia/"
                f"{self.name.replace(' ', '_').lower().strip()}"
            )
        self.datetime_last_modified = timezone.now()
        super().save(*args, **kwargs)

    def __str__(self):
        """Return the name of the familia."""
        return self.name or ""

    def __repr__(self):
        """Return the name of the familia."""
        return self.name or ""

    class Meta:
        """Meta options for Familia model."""

        verbose_name_plural = "Familiae"


class Genus(models.Model):
    """Taxonomic Genus."""

    model_curi = "lara:organisms/Genus"
    genus_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name_display = models.TextField(
        help_text="human readable display name of the genus, can contain spaces"
    )
    name = models.TextField(
        blank=True,
        null=True,
        help_text="genus name, like ",
    )
    name_common = models.TextField(
        blank=True,
        null=True,
        help_text="common genus name, like ",
    )
    traits = models.ManyToManyField(
        Trait,
        related_name="%(app_label)s_%(class)s_genus_traits_related",
        related_query_name="%(app_label)s_%(class)s_genus_traits",
        blank=True,
        help_text="General genus's traits / features / attributes",
    )
    characteristics = models.JSONField(
        blank=True,
        null=True,
        help_text="General characteristics of this genus, use 'traits' for specific traits.",
    )
    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(
        null=True,
        blank=True,
        help_text="description of this genus",
    )
    datetime_last_modified = models.DateTimeField(
        blank=True,
        null=True,
        help_text="date and time when habitat parameter data was last modified",
    )

    def save(self, *args, **kwargs):
        """Generate default values for name and IRI."""
        if self.name is None or self.name == "":
            self.name = self.name_display.replace(" ", "_").lower().strip()
        if self.iri is None or self.iri == "":
            self.iri = (
                f"{settings.LARA_PREFIXES['lara']}organisms/genus/"
                f"{self.name.replace(' ', '_').lower().strip()}"
            )
        self.datetime_last_modified = timezone.now()
        super().save(*args, **kwargs)

    def __str__(self):
        """Return the name of the genus."""
        return self.name or ""

    def __repr__(self):
        """Return the name of the genus."""
        return self.name or ""

    class Meta:
        """Meta options for Genus model."""

        verbose_name_plural = "Genera"


class Species(models.Model):
    """Taxonomic Species."""

    model_curi = "lara:organisms/Species"
    species_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name_display = models.TextField(
        help_text="human readable display name of the species, can contain spaces"
    )
    name = models.TextField(
        blank=True,
        null=True,
        help_text="species name, like ",
    )
    name_common = models.TextField(
        blank=True,
        null=True,
        help_text="common species name, like ",
    )
    traits = models.ManyToManyField(
        Trait,
        related_name="%(app_label)s_%(class)s_species_traits_related",
        related_query_name="%(app_label)s_%(class)s_species_traits",
        blank=True,
        help_text="General species's traits / features / attributes",
    )
    characteristics = models.JSONField(
        blank=True,
        null=True,
        help_text="General characteristics of this species, use 'traits' for specific traits.",
    )
    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(
        null=True,
        blank=True,
        help_text="description of this species",
    )
    datetime_last_modified = models.DateTimeField(
        blank=True,
        null=True,
        help_text="date and time when habitat parameter data was last modified",
    )

    def save(self, *args, **kwargs):
        """Generate default values for name and IRI."""
        if self.name is None or self.name == "":
            self.name = self.name_display.replace(" ", "_").lower().strip()
        if self.iri is None or self.iri == "":
            self.iri = (
                f"{settings.LARA_PREFIXES['lara']}organisms/species/"
                f"{self.name.replace(' ', '_').lower().strip()}"
            )
        self.datetime_last_modified = timezone.now()
        super().save(*args, **kwargs)

    def __str__(self):
        """Return the name of the species."""
        return self.name or ""

    def __repr__(self):
        """Return the name of the species."""
        return self.name or ""

    class Meta:
        """Meta options for Species model."""

        verbose_name_plural = "Speciae"


class Subspecies(models.Model):
    """Taxonomic Subspecies."""

    model_curi = "lara:organisms/Subspecies"
    subspecies_id = models.UUIDField(
        primary_key=True,
        default=uuid.uuid4,
        editable=False,
    )
    name_display = models.TextField(
        help_text="human readable display name of the subspecies, can contain spaces"
    )
    name = models.TextField(
        blank=True,
        null=True,
        help_text="subspecies name, like ",
    )
    name_common = models.TextField(
        blank=True,
        null=True,
        help_text="common subspecies name, like ",
    )
    traits = models.ManyToManyField(
        Trait,
        related_name="%(app_label)s_%(class)s_subspecies_traits_related",
        related_query_name="%(app_label)s_%(class)s_subspecies_traits",
        blank=True,
        help_text="General subspecies's traits / features / attributes",
    )
    characteristics = models.JSONField(
        blank=True,
        null=True,
        help_text="General characteristics of this subspecies, use 'traits' for specific traits.",
    )
    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(
        null=True,
        blank=True,
        help_text="description of this subspecies",
    )
    datetime_last_modified = models.DateTimeField(
        blank=True,
        null=True,
        help_text="date and time when habitat parameter data was last modified",
    )

    def save(self, *args, **kwargs):
        """Generate default values for name and IRI."""
        if self.name is None or self.name == "":
            self.name = self.name_display.replace(" ", "_").lower().strip()
        if self.iri is None or self.iri == "":
            self.iri = (
                f"{settings.LARA_PREFIXES['lara']}organisms/subspecies/"
                f"{self.name.replace(' ', '_').lower().strip()}"
            )
        self.datetime_last_modified = timezone.now()
        super().save(*args, **kwargs)

    def __str__(self):
        """Return the name of the subspecies."""
        return self.name or ""

    def __repr__(self):
        """Return the name of the subspecies."""
        return self.name or ""

    class Meta:
        """Meta options for Subspecies model."""

        verbose_name_plural = "Subspecieae"


class Variant(models.Model):
    """Taxonomic Variant."""

    model_curi = "lara:organisms/Variant"
    variant_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name_display = models.TextField(
        help_text="human readable display name of the variant, can contain spaces"
    )
    name = models.TextField(
        blank=True,
        null=True,
        help_text="variant name, like ",
    )
    name_common = models.TextField(
        blank=True,
        null=True,
        help_text="common variant name, like ",
    )
    traits = models.ManyToManyField(
        Trait,
        related_name="%(app_label)s_%(class)s_variant_traits_related",
        related_query_name="%(app_label)s_%(class)s_variant_traits",
        blank=True,
        help_text="General variant's traits / features / attributes",
    )
    characteristics = models.JSONField(
        blank=True,
        null=True,
        help_text="General characteristics of this variant, use 'traits' for specific traits.",
    )
    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(
        null=True,
        blank=True,
        help_text="description of this variant",
    )
    datetime_last_modified = models.DateTimeField(
        blank=True,
        null=True,
        help_text="date and time when habitat parameter data was last modified",
    )

    def save(self, *args, **kwargs):
        """Generate default values for name and IRI."""
        if self.name is None or self.name == "":
            self.name = self.name_display.replace(" ", "_").lower().strip()
        if self.iri is None or self.iri == "":
            self.iri = (
                f"{settings.LARA_PREFIXES['lara']}organisms/variant/"
                f"{self.name.replace(' ', '_').lower().strip()}"
            )
        self.datetime_last_modified = timezone.now()
        super().save(*args, **kwargs)

    def __str__(self):
        """Return the name of the variant."""
        return self.name or ""

    def __repr__(self):
        """Return the name of the variant."""
        return self.name or ""


class Cultivar(models.Model):
    """Taxonomic Cultivar / Plant variety originating from cultivation / cell line."""

    model_curi = "lara:organisms/Cultivar"
    cultivar_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name_display = models.TextField(
        help_text="human readable display name of the cultivar, can contain spaces"
    )
    name = models.TextField(
        blank=True,
        null=True,
        help_text="cultivar name, like ",
    )
    name_common = models.TextField(
        blank=True,
        null=True,
        help_text="common cultivar name, like ",
    )
    traits = models.ManyToManyField(
        Trait,
        related_name="%(app_label)s_%(class)s_cultivar_traits_related",
        related_query_name="%(app_label)s_%(class)s_cultivar_traits",
        blank=True,
        help_text="General cultivar's traits / features / attributes",
    )
    characteristics = models.JSONField(
        blank=True,
        null=True,
        help_text="General characteristics of this cultivar, use 'traits' for specific traits.",
    )
    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(
        null=True,
        blank=True,
        help_text="description of this cultivar",
    )
    datetime_last_modified = models.DateTimeField(
        blank=True,
        null=True,
        help_text="date and time when habitat parameter data was last modified",
    )

    def save(self, *args, **kwargs):
        """Generate default values for name and IRI."""
        if self.name is None or self.name == "":
            self.name = self.name_display.replace(" ", "_").lower().strip()
        if self.iri is None or self.iri == "":
            self.iri = (
                f"{settings.LARA_PREFIXES['lara']}organisms/cultivar/"
                f"{self.name.replace(' ', '_').lower().strip()}"
            )
        self.datetime_last_modified = timezone.now()
        super().save(*args, **kwargs)

    def __str__(self):
        """Return the name of the cultivar."""
        return self.name or ""

    def __repr__(self):
        """Return the name of the cultivar."""
        return self.name or ""
