"""Base models for lara_django_organisms.

This module contains generic/base model classes that are used across
the organisms app, including ExtraData and Trait models.

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

import hashlib
import json
import logging
import uuid

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

from lara_django_base.models import ExtraDataAbstr, PhysicalUnit

logger = logging.getLogger(__name__)


class ExtraData(ExtraDataAbstr):
    """Extended data model for additional organism information.

    This class can be used to extend data by extra information,
    e.g. more telephone numbers, customer numbers, files, etc.
    """

    model_curi = "lara:organisms/ExtraData"
    file = models.FileField(
        upload_to="organisms",
        blank=True,
        null=True,
        help_text="rel. path/filename",
    )

    def save(self, *args, **kwargs):
        """Generate default values for version, hash, name, and IRI."""
        if self.version is None or self.version == "":
            self.version = "0.0.1"

        if self.hash_sha256 is None:
            if self.data_json is not None:
                to_hash = json.dumps(self.data_json)
                self.hash_sha256 = hashlib.sha256(to_hash.encode("utf-8")).hexdigest()
            else:
                self.hash_sha256 = hashlib.sha256(
                    str(self.extradata_id).encode("utf-8")
                ).hexdigest()

        if self.name is None or self.name == "":
            if self.name_display is None or self.name_display == "":
                self.name = "_".join(
                    (
                        timezone.now().strftime("%Y%m%d_%H%M%S"),
                        "data",
                        self.hash_sha256[:8],
                    )
                )
            else:
                self.name = self.name_display.replace(" ", "_").lower().strip()
        else:
            self.name = self.name.replace(" ", "_").lower().strip()

        if self.name_full is None or self.name_full == "":
            if self.namespace is not None and self.version is not None:
                self.name_full = (
                    f"{self.namespace.uri}/"
                    + "_".join((self.name.replace(" ", "_"), self.version))
                )
            else:
                self.name_full = "_".join((self.name.replace(" ", "_"), self.version))

        if not self.iri:
            netloc = self.namespace.parse_URI().netloc
            path = self.namespace.parse_URI().path
            self.iri = (
                f"{settings.LARA_PREFIXES['lara']}organisms/ExtraData/"
                f"{netloc}{path}/{self.name.replace(' ', '_').lower().strip()}"
            )

        self.datetime_last_modified = timezone.now()

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


class Trait(models.Model):
    """Phenotypical, phenomenological or genetic traits of organisms.

    Examples include: branched, round, oval, GRAM staining, genetic fingerprint sequence,
    antibiotic resistance, morphology, size, shape, motility, spore formation, flagella,
    capsule presence, growth optima (temp, pH, salt, sugar), doubling rate, metabolites,
    nutrients, metabolic characteristics, pathogenicity, membrane potential, legal status,
    culture conditions, etc.
    """

    model_curi = "lara:organisms/Trait"
    trait_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name_display = models.TextField(
        help_text="human readable display name of the trait, can contain spaces"
    )
    name = models.TextField(
        unique=True,
        help_text=(
            "Name of the trait / feature attribute of the organisms, "
            "like branched, round, oval, staining, genetic fingerprint sequence"
        ),
    )
    value_range = models.JSONField(
        null=True,
        blank=True,
        help_text="range of values for this trait, like min, max, average, ...",
    )
    characteristics = models.JSONField(
        blank=True,
        null=True,
        help_text="characteristics of this trait",
    )
    unit = models.ForeignKey(
        PhysicalUnit,
        on_delete=models.CASCADE,
        related_name="%(app_label)s_%(class)s_unit_related",
        related_query_name="%(app_label)s_%(class)s_unit_related_query",
        blank=True,
        null=True,
        help_text="physical unit of the parameter, e.g. m, m^2, V, ...",
    )
    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 the feature class",
    )
    datetime_last_modified = models.DateTimeField(
        blank=True,
        null=True,
        help_text="date and time when trait 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/FeatureClass/"
                f"{self.name.replace(' ', '_').lower().strip()}"
            )

        self.datetime_last_modified = timezone.now()

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

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

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

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

        verbose_name_plural = "FeatureClasses"
