from datetime import datetime

from django.contrib.gis.geos import Point
from django.db import models
from django.template.defaultfilters import slugify
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django_better_admin_arrayfield.models.fields import ArrayField
from django_ckeditor_5.fields import CKEditor5Field
from django_extensions.db.fields import AutoSlugField
from location_field.models.spatial import LocationField


class Place(models.Model):
    name = models.CharField(verbose_name=_("Name"), help_text=_("Name of the place"))
    address = models.CharField(
        verbose_name=_("Address"),
        help_text=_("Address of this place (without city name)"),
        blank=True,
        null=True,
    )
    postcode = models.CharField(
        verbose_name=_("Postcode"),
        help_text=_(
            "The post code is not displayed, but makes it easier to find an address when you enter it."
        ),
        blank=True,
        null=True,
    )
    city = models.CharField(verbose_name=_("City"), help_text=_("City name"))
    location = LocationField(
        based_fields=["name", "address", "postcode", "city"],
        zoom=12,
        default=Point(3.08333, 45.783329),
    )

    description = CKEditor5Field(
        verbose_name=_("Description"),
        help_text=_("Description of the place, including accessibility."),
        blank=True,
        null=True,
    )

    aliases = ArrayField(
        models.CharField(max_length=512),
        verbose_name=_("Alternative names"),
        help_text=_(
            "Alternative names or addresses used to match a place with the free-form location of an event."
        ),
        blank=True,
        null=True,
    )

    class Meta:
        verbose_name = _("Place")
        verbose_name_plural = _("Places")
        ordering = ["name"]
        indexes = [
            models.Index(fields=["name"]),
            models.Index(fields=["city"]),
            models.Index(fields=["location"]),
        ]

    def __str__(self):
        if self.address:
            return self.name + ", " + self.address + ", " + self.city
        else:
            return self.name + ", " + self.city

    def get_absolute_url(self):
        return reverse(
            "view_place_fullname",
            kwargs={"pk": self.pk, "extra": slugify(self.name)},
        )

    def nb_events(self):
        from ..models.event import Event

        return Event.objects.filter(exact_location=self).count()

    def nb_events_future(self):
        from ..models.event import Event

        return (
            Event.objects.filter(start_day__gte=datetime.now())
            .filter(exact_location=self)
            .count()
        )

    def match(self, event):
        if self.aliases and event.location:
            return event.location.strip() in self.aliases
        else:
            return False

    def associate_matching_events(self):
        from ..models.event import Event

        u_events = Event.objects.filter(exact_location__isnull=True)

        to_be_updated = []
        # try to find matches
        for ue in u_events:
            if self.match(ue):
                ue.exact_location = self
                to_be_updated.append(ue)
                continue
        # update events with a location
        Event.objects.bulk_update(to_be_updated, fields=["exact_location"])
        return len(to_be_updated)

    @classmethod
    def get_all_cities(cls):
        try:
            tags = list(
                [
                    p["city"]
                    for p in Place.objects.values("city").distinct().order_by("city")
                ]
            )
        except Exception:
            tags = []
        return tags


class ReferenceLocation(models.Model):
    name = models.CharField(
        verbose_name=_("Name"),
        help_text=_("Name of the location"),
        unique=True,
        null=False,
    )
    location = LocationField(
        based_fields=["name"],
        zoom=12,
        default=Point(3.08333, 45.783329),
        srid=4326,
    )
    main = models.IntegerField(
        verbose_name=_("Main"),
        help_text=_(
            "This location is one of the main locations (shown first higher values)."
        ),
        default=0,
    )
    suggested_distance = models.IntegerField(
        verbose_name=_("Suggested distance (km)"),
        help_text=_(
            "If this distance is given, this location is part of the suggested filters."
        ),
        null=True,
        default=None,
    )

    slug = AutoSlugField(null=True, default=None, unique=True, populate_from="name")

    class Meta:
        verbose_name = _("Reference location")
        verbose_name_plural = _("Reference locations")
        indexes = [
            models.Index(fields=["name"]),
        ]

    def __str__(self):
        return self.name
