import datetime
import uuid

from django.conf import settings
from django.contrib.postgres.indexes import GinIndex
from django.contrib.postgres.search import SearchVectorField
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _

from .models_base import (
    ItemStatus,
    Namespace,
    Tag,
)
from .models_location import GeoLocation, Location


def _get_syncserver_types():
    """helper function to get the available LARA server types for synchronisation for choices
    which requires a function to be called"""
    return settings.LARA_SERVER_TYPES


class LARASyncServer(models.Model):
    """LARA synchronisation Server description,
    e.g., for synchronisation between two LARA instances or other server types."""

    class SyncServerTypeChoices(models.TextChoices):
        SYNC_FULL = "f", _("full")
        SYNC_INCREMENTAL = "i", _("incremental")

    model_curi = "lara:base/LARASyncServer"

    laraserver_id = models.UUIDField(
        primary_key=True, default=uuid.uuid4, editable=False
    )
    name = models.TextField(
        blank=True, help_text="human readable name of the Sync server"
    )
    url = models.URLField(
        blank=True,
        help_text="Universal Resource Locator - URL of the LARA server",
    )
    port = models.IntegerField(
        blank=True, null=True, help_text="port of the LARA server"
    )
    server_type = models.CharField(
        max_length=4,
        choices=_get_syncserver_types,
        default="LARA",
        help_text="type of server to be synchronised with",
    )
    connection_json = models.JSONField(
        blank=True,
        null=True,
        help_text="JSON field for more advanced connection information,"
        "e.g., advanced routing, ports, ..., could be even encrypted.",
    )
    auth_json = models.JSONField(
        blank=True,
        null=True,
        help_text="JSON field for authentication information, e.g. username, "
        "password, token, ..., could be even encrypted",
    )
    cert_pub = models.TextField(blank=True, help_text="server public certificate")

    tags = models.ManyToManyField(
        Tag,
        related_name="%(app_label)s_%(class)s_tags_related",
        related_query_name="%(app_label)s_%(class)s_tags_related_query",
        blank=True,
        help_text="tags LARA server",
    )
    description = models.TextField(blank=True, help_text="server description")

    class Meta:
        db_table_comment = "LARA Server description, e.g., for synchronisation between two LARA instances"

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

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

    def save(self, *args, **kwargs):
        if self.name is None:
            self.name = self.url

        self.name = self.name.replace(" ", "_").lower().strip()

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


class SyncStatusAbstr(models.Model):
    """An abstract class to store sync status information, e.g. for synchronisation between two LARA instances"""

    syncstatus_id = models.UUIDField(
        primary_key=True, default=uuid.uuid4, editable=False
    )
    status = models.ForeignKey(
        ItemStatus,
        related_name="%(app_label)s_%(class)s_statuses_related",
        related_query_name="%(app_label)s_%(class)s_statuses_related_query",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        help_text="scheduled, in-progress, interrupted, completed",
    )
    server = models.ForeignKey(
        LARASyncServer,
        related_name="%(app_label)s_%(class)s_servers_related",
        related_query_name="%(app_label)s_%(class)s_servers_related_query",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
    )
    datetime_last_synced = models.DateTimeField(
        default=timezone.now, help_text="date and time when data was last modified"
    )
    # sync_conflicts = models.FileField(help_text="File containing detailed information
    # about conflicts during synchronisation")

    class Meta:
        abstract = True
        verbose_name_plural = "SyncStatus"


class SynchronisationAbstr(models.Model):
    """Abstract Synchronisation settings class for (recursive) LARA projects synchronisation with other LARA systems

    :param models: _description_
    :type models: _type_
    """

    class SyncModeChoices(models.TextChoices):
        SYNC_FULL = "f", _("full")
        SYNC_INCREMENTAL = "i", _("incremental")

    class SyncPolicyChoices(models.TextChoices):
        UPDATE_SOURCE = "s", _("source")
        UPDATE_TARGET = "t", _("target")
        UPDATE_MERGE = "m", _("merge")

    sync_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 synchronisation",
    )
    name = models.TextField(
        blank=True, unique=True, help_text="name of the synchronisaton"
    )
    server_target = models.ForeignKey(
        LARASyncServer,
        related_name="%(app_label)s_%(class)s_server_related",
        related_query_name="%(app_label)s_%(class)s_server_related_query",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        help_text="LARA server to be synchronised with",
    )
    datetime_start = models.DateTimeField(
        default=timezone.now, help_text="synchronisation start"
    )
    datetime_end = models.DateTimeField(
        default=datetime.datetime(2066, 12, 31, tzinfo=datetime.timezone.utc),
        help_text="synchronisation end",
    )
    mode = models.CharField(
        max_length=1,
        choices=SyncModeChoices.choices,
        default=SyncModeChoices.SYNC_FULL,
        help_text="synchronisation mode: full, incremental",
    )
    update_policy = models.CharField(
        max_length=1,
        choices=SyncPolicyChoices.choices,
        default=SyncPolicyChoices.UPDATE_MERGE,
        help_text="update policy: source, target, merge",
    )
    event_repeat = models.TextField(
        blank=True,
        help_text="prefect / apache airflow ('@daily', '@weekly') or cron expression"
        "like entry: '12 23 * * *' :  every day at 23:12h do this task",
    )
    event_repeat_json = models.JSONField(
        blank=True,
        null=True,
        help_text="advanced repeat expressions possible, e.g. for uneven occurrences",
    )
    config_json = models.JSONField(
        blank=True,
        null=True,
        help_text="advanced configuration settings in JSON format,"
        "e.g., for advanced settings like filters, mappings, ...",
    )
    logs = models.JSONField(
        null=True, blank=True, help_text="logs of the synchronisation"
    )
    status = models.ForeignKey(
        ItemStatus,
        related_name="%(app_label)s_%(class)s_status_related",
        related_query_name="%(app_label)s_%(class)s_status_related_query",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        help_text="synchronisation status classifier: planned, started, in_progress, finished",
    )
    log_file = models.FileField(
        upload_to="projects/sync",
        blank=True,
        null=True,
        help_text="log file with rel. path/filename",
    )
    description = models.TextField(
        blank=True, help_text="description of extra data type"
    )

    class Meta:
        abstract = True

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

    def __repr__(self) -> str:
        return self.name or ""

    # duration will be calculated


class CalendarAbstr(models.Model):
    """_summary_
    see https://www.ietf.org/rfc/rfc2445.txt
        RFC 5545

    see also: https://pypi.org/project/ics/
         and  https://pypi.org/project/icalendar/

    :param models: _description_
    :type models: _type_
    """

    name_display = models.TextField(help_text="title of the calendar entry")
    calendar_url = models.URLField(
        blank=True,
        help_text="Universal Resource Locator (URL), to google calendar, iCalendar, .... ",
    )
    summary = models.TextField(
        blank=True, help_text="short summary text of the calendar entry"
    )
    description = models.TextField(blank=True, help_text="text of the calendar entry")
    color = models.TextField(
        # default=ColorBlind16.full_color_scheme[randint(0, 16)].get_web(),
        blank=True,
        help_text="text of the calendar entry",
    )
    # organizer, cn, attendees
    # user - needs to be defined in the instantiation
    ics = models.TextField(
        blank=True, help_text="iCalendar entry in (RFC 5545) file format"
    )
    datetime_start = models.DateTimeField(
        default=timezone.now,
        help_text="start date & time of the event. RFC 5545 - DTSTART",
    )
    datetime_end = models.DateTimeField(
        default=timezone.now, help_text="end datetime of the event. RFC 5545 - DTEND"
    )
    duration = models.TextField(
        blank=True,
        help_text=""" This value type is used to identify properties that contain
         a duration of time. RFC 5545 - DURATION""",
    )
    all_day = models.BooleanField(default=False, help_text="All day event ? (boolean)")

    created = models.DateTimeField(
        default=timezone.now, help_text="end datetime of the event"
    )
    datetime_last_modified = models.DateTimeField(
        default=timezone.now,
        help_text="date and time when calendar entry was last modified",
    )
    timestamp = models.DateTimeField(
        default=timezone.now, help_text="timestamp of the event"
    )
    # transparency
    location = models.ForeignKey(
        Location,
        related_name="%(app_label)s_%(class)s_location_related",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        help_text="location",
    )
    geolocation = models.ForeignKey(
        GeoLocation,
        related_name="%(app_label)s_%(class)s_geolocation_related",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        help_text="GEO location",
    )

    conference_url = models.URLField(
        blank=True,
        help_text="audio/video conference/meeting Universal Resource Locator (URL), e.g. zoom, jitsi, meet, ...",
    )

    tags = models.ManyToManyField(
        Tag,
        related_name="%(app_label)s_%(class)s_tags_related",
        related_query_name="%(app_label)s_%(class)s_tags_related_query",
        blank=True,
        help_text="tags of calendar entry",
    )
    # many parameters / fields of the RFC 5545 should be stored in the ics field
    # participation status
    range = models.TextField(
        blank=True,
        help_text="""To specify the effective range of recurrence instances from
        the instance specified by the recurrence identifier specified by
        the property. RFC 5545 - RANGE """,
    )
    related = models.TextField(
        blank=True,
        help_text="""To specify the relationship of the alarm trigger with
        respect to the start or end of the calendar component. RFC 5545 - RELATED""",
    )
    role = models.TextField(
        blank=True,
        help_text="""To specify the participation role for the calendar user
        specified by the property. RFC 5545 - ROLE""",
    )
    tzid = models.TextField(
        blank=True,
        help_text="""To specify the identifier for the time zone definition for
        a time component in the property value.
        RFC 5545 - TZID""",
    )
    offset_utc = models.IntegerField(
        blank=True,
        default=0,
        help_text=""" This value type is used to identify properties that contain
      an offset from UTC to local time (+ or - num. hours) . RFC 5545 -  UTC-OFFSET""",
    )
    # alarm_method - mail ...
    alarm_repeat_json = models.JSONField(
        blank=True,
        null=True,
        help_text="advanced alarm repeat expressions possible, e.g. for uneven occurrences",
    )
    event_repeat = models.TextField(
        blank=True,
        help_text="apache airflow ('@daily', '@weekly') or cron expression like entry: '12 23 * * *' : "
        "every day at 23:12h do this task",
    )
    event_repeat_json = models.JSONField(
        blank=True,
        null=True,
        help_text="advanced event repeat expressions possible, e.g. for uneven occurrences",
    )
    search_vector = SearchVectorField(null=True)

    class Meta:
        abstract = True
        indexes = (GinIndex(fields=["search_vector"]),)

    # autogenerate ICS on save, if ics is empty.
    # see: https://icalendar.readthedocs.io
    # def save(self, *args, **kwargs):
    #     if self.entity.email == "":
    #         self.entity.email = self.user.email

    #     if self.user.first_name == "":
    #         self.user.first_name = self.entity.name_first

    #     if self.user.last_name == "":
    #         self.user.last_name = self.entity.name_last

    #     if self.user.email == "":
    #         self.user.email = self.entity.email

    #     self.user.save()

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

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

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