import uuid
from django.contrib.gis.db import models
from django.contrib.auth import get_user_model
from django.utils.translation import gettext_lazy as _
from django.utils.text import slugify
from main.fields import WEBPField
# Create your models here.

User = get_user_model()

CHECK_BOOLEAN = [
    (True,_("submitted")),
    (False,_("ignored")),
]

ACTIVE_BOOLEAN = [
    (True,_("active")),
    (False,_("deactive")),
]

QUESTION_BOOLEAN = [
    (True,_("yes")),
    (False,_("no")),
]


###
# get data from api
# https://nominatim.org/release-docs/develop/
# https://restcountries.com/
# https://www.geonames.org/
# #





class Country(models.Model):
    name = models.CharField(_("name"), max_length=255, db_index=True)
    slug = models.SlugField(_("Slug"), max_length=250, db_index=True, unique=True, null=True, blank=True)
    code = models.CharField(_("code"), max_length=255, db_index=True)
    similar_name = models.TextField(_("similar name"), db_index=True, null=True, blank=True)
    
    default_language = models.ForeignKey("main.Language", verbose_name=_("default language"), related_name="country_language", on_delete=models.CASCADE, null=True, blank=True)    
    default_currency = models.ForeignKey("development.Currency", verbose_name=_("default currency"), related_name="country_currency", on_delete=models.CASCADE, null=True, blank=True)
    coordinates = models.MultiPolygonField(_("coordinates"), srid=4326, null=True, blank=True)
    
    is_featured = models.BooleanField(_("Featured"), default=False, choices=QUESTION_BOOLEAN)

    translated = models.BooleanField(_("translated"), default=False, choices=QUESTION_BOOLEAN)
    
    icon = models.FileField(_("icon"), upload_to="country/icons/", db_index=True, null=True, blank=True)
    data = models.JSONField(_("data"), null=True, blank=True)
    data_check = models.BooleanField(_("data check"), default=False, choices=CHECK_BOOLEAN)
    created_at = models.DateTimeField(_("created at"), auto_now_add=True)
    updated_at = models.DateTimeField(_("updated at"), auto_now=True)
    
    def save(self, **kwargs):
        if self.name: self.slug = slugify(self.name, allow_unicode=True)
        if self.similar_name is None or self.similar_name == "":
            sn = []
            if self.code and self.code not in sn:
                sn.append(self.code)
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        else:
            sn = self.similar_name.split("|,| ")
            if self.code and self.code not in sn:
                sn.append(self.code)
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                if self.name not in sn:
                    sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        return super().save()
    
    def __str__(self):
        return f"{self.name}"
    
    class Meta:
        verbose_name = _("Country")
        verbose_name_plural = _("Countries")
        ordering = ["name"]
        
class CountryTranslate(models.Model):
    country = models.ForeignKey(Country, on_delete=models.CASCADE, verbose_name=_("country"), related_name="translates")
    language = models.ForeignKey("main.Language", verbose_name=_("language"), related_name="country_translates", on_delete=models.CASCADE)
    name = models.CharField(_("name"), max_length=255, db_index=True)
    slug = models.SlugField(_("Slug"), max_length=250, db_index=True, null=True, blank=True)
    similar_name = models.TextField(_("similar name"), db_index=True, null=True, blank=True)
    description = models.TextField(_("description"), null=True, blank=True)
    filter_text = models.TextField(_("filter text"), null=True, blank=True)
    
    def save(self, **kwargs):
        self.slug = slugify(self.country.name, allow_unicode=True)
        if self.similar_name is None or self.similar_name == "":
            sn = []
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        else:
            sn = self.similar_name.split("|,| ")
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                if self.name not in sn:
                    sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        return super().save()
    
    def __str__(self):
        return f"{self.name}"
    
    class Meta:
        verbose_name = _("Country Translate")
        verbose_name_plural = _("Countries Translates")
        ordering = ["name"]
        constraints = [
            models.UniqueConstraint(fields=['language', 'country'], name='repeated_language_country_translate'),
        ]
    
class State (models.Model):
    name = models.CharField(_("name"), max_length=255)
    slug = models.SlugField(_("Slug"), max_length=250, unique=True, null=True, blank=True)
    code = models.CharField(_("code"), max_length=255)
    country = models.ForeignKey(Country, verbose_name=_("country"), related_name="states", on_delete=models.CASCADE, null=True, blank=True)
    coordinates = models.MultiPolygonField(_("coordinates"), srid=4326, null=True, blank=True)
    similar_name = models.TextField(_("similar name"), null=True, blank=True)
    translated = models.BooleanField(_("translated"), default=False, choices=QUESTION_BOOLEAN)
    
    is_featured = models.BooleanField(_("Featured"), default=False, choices=QUESTION_BOOLEAN)
    
    icon = models.FileField(_("icon"), upload_to="state/icons/", null=True, blank=True)
    data = models.JSONField(_("data"), null=True, blank=True)
    data_check = models.BooleanField(_("data check"), default=False, choices=CHECK_BOOLEAN)
    created_at = models.DateTimeField(_("created at"), auto_now_add=True)
    updated_at = models.DateTimeField(_("updated at"), auto_now=True)
    
    def save(self, **kwargs):
        if self.name: self.slug = slugify(self.name, allow_unicode=True)
        if self.similar_name is None or self.similar_name == "":
            sn = []
            if self.code and self.code not in sn:
                sn.append(self.code)
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        else:
            sn = self.similar_name.split("|,| ")
            if self.code and self.code not in sn:
                sn.append(self.code)
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                if self.name not in sn:
                    sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        return super().save()
    
    def __str__(self):
        return f"{self.name}"
    
    class Meta:
        verbose_name = _("State")
        verbose_name_plural = _("States")
        ordering = ["name"]
        
class StateTranslate(models.Model):
    state = models.ForeignKey(State, on_delete=models.CASCADE, verbose_name=_("state"), related_name="translates")
    language = models.ForeignKey("main.Language", verbose_name=_("language"), related_name="state_translates", on_delete=models.CASCADE)
    name = models.CharField(_("name"), max_length=255)
    slug = models.SlugField(_("Slug"), max_length=250, null=True, blank=True)
    similar_name = models.TextField(_("similar name"), null=True, blank=True)
    description = models.TextField(_("description"), null=True, blank=True)
    filter_text = models.TextField(_("filter text"), null=True, blank=True)

    def save(self, **kwargs):
        if self.slug is None or self.slug == "":
            self.slug = slugify(self.state.name, allow_unicode=True)
        if self.similar_name is None or self.similar_name == "":
            sn = []
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        else:
            sn = self.similar_name.split("|,| ")
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                if self.name not in sn:
                    sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        return super().save()

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

    class Meta:
        verbose_name = _("State Translate")
        verbose_name_plural = _("States Translates")
        ordering = ['name']
        constraints = [
            models.UniqueConstraint(fields=['language', 'state'], name='repeated_language_state_translate'),
        ]
    
    
class City(models.Model):
    name = models.CharField(_("name"), max_length=255, db_index=True)
    slug = models.SlugField(_("Slug"), max_length=250, db_index=True, unique=True, null=True, blank=True)
    code = models.CharField(_("code"), max_length=255, db_index=True, null=True, blank=True)   
    similar_name = models.TextField(_("similar name"), db_index=True, null=True, blank=True)
    coordinates = models.MultiPolygonField(_("coordinates"), srid=4326, null=True, blank=True)
    translated = models.BooleanField(_("translated"), default=False, choices=QUESTION_BOOLEAN)
    is_featured = models.BooleanField(_("Featured"), default=False, choices=QUESTION_BOOLEAN)
    icon = models.FileField(_("icon"), upload_to="cities/icons/", null=True, blank=True)
    country = models.ForeignKey(Country, on_delete=models.CASCADE, verbose_name=_("country"), related_name="cities", null=True, blank=True)
    data = models.JSONField(_("data"), null=True, blank=True)
    data_check = models.BooleanField(_("data check"), default=False, choices=CHECK_BOOLEAN)
    created_at = models.DateTimeField(_("created at"), auto_now_add=True)
    updated_at = models.DateTimeField(_("updated at"), auto_now=True)
    
    def save(self, **kwargs):
        if self.name: self.slug = slugify(self.name, allow_unicode=True)
        if self.similar_name is None or self.similar_name == "":
            sn = []
            if self.code and self.code not in sn:
                sn.append(self.code)
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        else:
            sn = self.similar_name.split("|,| ")
            if self.code and self.code not in sn:
                sn.append(self.code)
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                if self.name not in sn:
                    sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        return super().save()
    
    def __str__(self):
        return f"{self.name}"
    
    class Meta:
        verbose_name = _("City")
        verbose_name_plural = _("Cities")
        ordering = ["name"]
        
class CityTranslate(models.Model):
    city = models.ForeignKey(City, on_delete=models.CASCADE, verbose_name=_("city"), related_name="translates")
    language = models.ForeignKey("main.Language", verbose_name=_("language"), related_name="city_translates", on_delete=models.CASCADE)
    name = models.CharField(_("name"), max_length=255, db_index=True)
    slug = models.SlugField(_("Slug"), max_length=250, db_index=True, null=True, blank=True)
    similar_name = models.TextField(_("similar name"), null=True, blank=True)
    description = models.TextField(_("description"), null=True, blank=True)
    filter_text = models.TextField(_("filter text"), null=True, blank=True)
    
    def save(self,*args, **kwargs):
        self.slug = slugify(self.city.name, allow_unicode=True)
        if self.similar_name is None or self.similar_name == "":
            sn = []
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        else:
            sn = self.similar_name.split("|,| ")
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                if self.name not in sn:
                    sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        return super().save(*args, **kwargs)
    
    def __str__(self):
        return f"{self.name}"
    
    class Meta:
        verbose_name = _("City Translate")
        verbose_name_plural = _("Cities Translates")
        ordering = ['name']
        constraints = [
            models.UniqueConstraint(fields=['language', 'city'], name='repeated_language_city_translate'),
        ]
        
class LondonZipState(models.Model):
    country = models.ForeignKey(Country, on_delete=models.CASCADE, verbose_name=_("country"), related_name="london_zip_state", null=True, blank=True)
    city = models.ForeignKey(City, on_delete=models.CASCADE,verbose_name=_("City"), related_name="london_zip_state", null=True, blank=True)
    name = models.CharField(_("name"), max_length=255)
    slug = models.SlugField(_("Slug"), max_length=250, null=True, blank=True)
    code = models.CharField(_("code"), max_length=255, null=True, blank=True)
    similar_name = models.TextField(_("similar name"), null=True, blank=True)
    coordinates = models.MultiPolygonField(_("coordinates"), srid=4326, null=True, blank=True)
    created_at = models.DateTimeField(_("created at"), auto_now_add=True)
    updated_at = models.DateTimeField(_("updated at"), auto_now=True)

    def save(self, **kwargs):
        if self.name: self.slug = slugify(self.name, allow_unicode=True)
        if self.similar_name is None or self.similar_name == "":
            sn = []
            if self.code and self.code not in sn:
                sn.append(self.code)
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        else:
            sn = self.similar_name.split("|,| ")
            if self.code and self.code not in sn:
                sn.append(self.code)
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                if self.name not in sn:
                    sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        return super().save()

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

    class Meta:
        verbose_name = _("London Zip State")
        verbose_name_plural = _("London Zip States")
        ordering = ['name']
        
class LondonArea(models.Model):
    country = models.ForeignKey(Country, on_delete=models.CASCADE, verbose_name=_("country"), related_name="lonson_area", null=True, blank=True)
    city = models.ForeignKey(City, on_delete=models.CASCADE,verbose_name=_("City"), related_name="lonson_area", null=True, blank=True)
    name = models.CharField(_("name"), max_length=255)
    slug = models.SlugField(_("Slug"), max_length=250, null=True, blank=True)
    code = models.CharField(_("code"), max_length=255, null=True, blank=True)
    similar_name = models.TextField(_("similar name"), null=True, blank=True)
    coordinates = models.MultiPolygonField(_("coordinates"), srid=4326, null=True, blank=True)
    created_at = models.DateTimeField(_("created at"), auto_now_add=True)
    updated_at = models.DateTimeField(_("updated at"), auto_now=True)
    
    def save(self, **kwargs):
        if self.name: self.slug = slugify(self.name, allow_unicode=True)
        if self.similar_name is None or self.similar_name == "":
            sn = []
            if self.code and self.code not in sn:
                sn.append(self.code)
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        else:
            sn = self.similar_name.split("|,| ")
            if self.code and self.code not in sn:
                sn.append(self.code)
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                if self.name not in sn:
                    sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        return super().save()
    
    def __str__(self):
        return f"{self.name}"
    
    class Meta:
        verbose_name = _("London Area")
        verbose_name_plural = _("London Areas")
        ordering = ['name']
        
class LondonZone(models.Model):
    country = models.ForeignKey(Country, on_delete=models.CASCADE, verbose_name=_("country"), related_name="london_zone", null=True, blank=True)
    city = models.ForeignKey(City, on_delete=models.CASCADE,verbose_name=_("City"), related_name="london_zone", null=True, blank=True)
    name = models.CharField(_("name"), max_length=255)
    slug = models.SlugField(_("Slug"), max_length=250, null=True, blank=True)
    code = models.CharField(_("code"), max_length=255, null=True, blank=True)
    similar_name = models.TextField(_("similar name"), null=True, blank=True)
    coordinates = models.MultiPolygonField(_("coordinates"), srid=4326, null=True, blank=True)
    created_at = models.DateTimeField(_("created at"), auto_now_add=True)
    updated_at = models.DateTimeField(_("updated at"), auto_now=True)

    def save(self, **kwargs):
        if self.name: self.slug = slugify(self.name, allow_unicode=True)
        if self.similar_name is None or self.similar_name == "":
            sn = []
            if self.code and self.code not in sn:
                sn.append(self.code)
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        else:
            sn = self.similar_name.split("|,| ")
            if self.code and self.code not in sn:
                sn.append(self.code)
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                if self.name not in sn:
                    sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        return super().save()
    
    def __str__(self):
        return f"{self.name}"
    
    class Meta:
        verbose_name = _("London Zone")
        verbose_name_plural = _("London Zones")
        ordering = ['name']

class District(models.Model):
    country = models.ForeignKey(Country, on_delete=models.CASCADE, verbose_name=_("country"), related_name="districts", null=True, blank=True)
    city = models.ForeignKey(City, on_delete=models.CASCADE,verbose_name=_("City"), related_name="districts", null=True, blank=True)
    name = models.CharField(_("name"), max_length=255)
    slug = models.SlugField(_("Slug"), max_length=250, null=True, blank=True)
    code = models.CharField(_("code"), max_length=255, null=True, blank=True)
    similar_name = models.TextField(_("similar name"), null=True, blank=True)
    coordinates = models.MultiPolygonField(_("coordinates"), srid=4326, null=True, blank=True)
    parent = models.ForeignKey('self', on_delete=models.CASCADE, verbose_name=_("parent"), related_name="children", null=True, blank=True)
    created_at = models.DateTimeField(_("created at"), auto_now_add=True)
    updated_at = models.DateTimeField(_("updated at"), auto_now=True)

    def save(self, *args, **kwargs):
        if self.name: self.slug = slugify(self.name, allow_unicode=True)
        if self.similar_name is None or self.similar_name == "":
            sn = []
            if self.code and self.code not in sn:
                sn.append(self.code)
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        else:
            sn = self.similar_name.split("|,| ")
            if self.code and self.code not in sn:
                sn.append(self.code)
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                if self.name not in sn:
                    sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        return super().save(*args, **kwargs)
    
    def __str__(self):
        return f"{self.name}"
    
    class Meta:
        verbose_name = _("District")
        verbose_name_plural = _("District")
        ordering = ['name']        
        
class Iso31662Type(models.Model):
    name = models.CharField(_("name"), max_length=255)
    slug = models.SlugField(_("Slug"), max_length=250, null=True, blank=True)
    code = models.CharField(_("code"), max_length=255, null=True, blank=True)
    level = models.IntegerField(_("level"), default=0)
    parent = models.ForeignKey("self", on_delete=models.CASCADE, verbose_name=_("parent"), related_name="child", null=True, blank=True)

    def save(self, **kwargs):
        if self.name: self.slug = slugify(self.name, allow_unicode=True)
        return super().save()

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

    class Meta:
        verbose_name = _("Iso31662 Type")
        verbose_name_plural = _("Iso31662 Types")
        ordering = ['name']
        
class Iso31662(models.Model):
    country = models.ForeignKey(Country, on_delete=models.CASCADE, verbose_name=_("country"))
    name = models.CharField(_("name"), max_length=255)
    slug = models.SlugField(_("Slug"), max_length=250, null=True, blank=True)
    code = models.CharField(_("code"), max_length=255, null=True, blank=True)
    similar_name = models.TextField(_("similar name"), null=True, blank=True)
    type = models.CharField(_("type"), max_length=255, null=True, blank=True)
    coordinates = models.MultiPolygonField(_("coordinates"), srid=4326, null=True, blank=True)
    level = models.IntegerField(_("level"), default=0)
    gid = models.CharField(_("gid"), max_length=255, unique=True, null=True, blank=True)
    parent = models.ForeignKey("self", on_delete=models.CASCADE, verbose_name=_("parent"), related_name="child", null=True, blank=True)
    
    gid_0 = models.CharField(_("GID 0"), max_length=255,null=True, blank=True)
    gid_1 = models.CharField(_("GID 1"), max_length=255,null=True, blank=True)
    gid_2 = models.CharField(_("GID 2"), max_length=255,null=True, blank=True)
    gid_3 = models.CharField(_("GID 3"), max_length=255,null=True, blank=True)
    gid_4 = models.CharField(_("GID 4"), max_length=255,null=True, blank=True)
    
    name_0 = models.CharField(_("name 0"), max_length=255,null=True, blank=True)
    name_1 = models.CharField(_("name 1"), max_length=255,null=True, blank=True)
    name_2 = models.CharField(_("name 2"), max_length=255,null=True, blank=True)
    name_3 = models.CharField(_("name 3"), max_length=255,null=True, blank=True)
    name_4 = models.CharField(_("name 4"), max_length=255,null=True, blank=True)
    
    nl_name_0 = models.CharField(_("national name 0"), max_length=255,null=True, blank=True)
    nl_name_1 = models.CharField(_("national name 1"), max_length=255,null=True, blank=True)
    nl_name_2 = models.CharField(_("national name 2"), max_length=255,null=True, blank=True)
    nl_name_3 = models.CharField(_("national name 3"), max_length=255,null=True, blank=True)
    nl_name_4 = models.CharField(_("national name 4"), max_length=255,null=True, blank=True)
    
    var_name_0 = models.CharField(_("var_name 0"), max_length=255,null=True, blank=True)
    var_name_1 = models.CharField(_("var_name 1"), max_length=255,null=True, blank=True)
    var_name_2 = models.CharField(_("var_name 2"), max_length=255,null=True, blank=True)
    var_name_3 = models.CharField(_("var_name 3"), max_length=255,null=True, blank=True)
    var_name_4 = models.CharField(_("var_name 4"), max_length=255,null=True, blank=True)
    
    type_0 = models.CharField(_("type 0"), max_length=255,null=True, blank=True)
    type_1 = models.CharField(_("type 1"), max_length=255,null=True, blank=True)
    type_2 = models.CharField(_("type 2"), max_length=255,null=True, blank=True)
    type_3 = models.CharField(_("type 3"), max_length=255,null=True, blank=True)
    type_4 = models.CharField(_("type 4"), max_length=255,null=True, blank=True)
    
    eng_type_0 = models.CharField(_("english type 0"), max_length=255,null=True, blank=True)
    eng_type_1 = models.CharField(_("english type 1"), max_length=255,null=True, blank=True)
    eng_type_2 = models.CharField(_("english type 2"), max_length=255,null=True, blank=True)
    eng_type_3 = models.CharField(_("english type 3"), max_length=255,null=True, blank=True)
    eng_type_4 = models.CharField(_("english type 4"), max_length=255,null=True, blank=True)
    
    cc_0 = models.CharField(_("cc 0"), max_length=255,null=True, blank=True)
    cc_1 = models.CharField(_("cc 1"), max_length=255,null=True, blank=True)
    cc_2 = models.CharField(_("cc 2"), max_length=255,null=True, blank=True)
    cc_3 = models.CharField(_("cc 3"), max_length=255,null=True, blank=True)
    cc_4 = models.CharField(_("cc 4"), max_length=255,null=True, blank=True)
    
    hasc_0 = models.CharField(_("hasc 0"), max_length=255,null=True, blank=True)
    hasc_1 = models.CharField(_("hasc 1"), max_length=255,null=True, blank=True)
    hasc_2 = models.CharField(_("hasc 2"), max_length=255,null=True, blank=True)
    hasc_3 = models.CharField(_("hasc 3"), max_length=255,null=True, blank=True)
    hasc_4 = models.CharField(_("hasc 4"), max_length=255,null=True, blank=True)
    iso_1 = models.CharField(_("iso 1"), max_length=255,null=True, blank=True)
    
    country_name = models.CharField(_("country name"), max_length=255,null=True, blank=True)
    created_at = models.DateTimeField(_("created at"), auto_now_add=True)
    updated_at = models.DateTimeField(_("updated at"), auto_now=True)
    
    def save(self, **kwargs):
        if self.name: self.slug = slugify(self.name, allow_unicode=True)
        if self.similar_name is None or self.similar_name == "":
            sn = []
            if self.code and self.code not in sn:
                sn.append(self.code)
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        else:
            sn = self.similar_name.split("|,| ")
            if self.code and self.code not in sn:
                sn.append(self.code)
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                if self.name not in sn:
                    sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        return super().save()

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

    class Meta:
        verbose_name = _("ISO 3166 V2")
        verbose_name_plural = _("ISO 3166 V2")
        ordering = ["name"]
        
class Iso31662Translate(models.Model):
    iso31662 = models.ForeignKey(Iso31662, on_delete=models.CASCADE, verbose_name=_("iso31662"), related_name="translates")
    language = models.ForeignKey("main.Language", verbose_name=_("language"), related_name="iso31662_translates", on_delete=models.CASCADE)
    name = models.CharField(_("name"), max_length=255)
    slug = models.SlugField(_("Slug"), max_length=250, null=True, blank=True)
    similar_name = models.TextField(_("similar name"), null=True, blank=True)
    description = models.TextField(_("description"), null=True, blank=True)

    def save(self, **kwargs):
        if self.slug is None or self.slug == "":
            self.slug = slugify(self.iso31662.name, allow_unicode=True)
        if self.similar_name is None or self.similar_name == "":
            sn = []
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        else:
            sn = self.similar_name.split("|,| ")
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                if self.name not in sn:
                    sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        return super().save()

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

    class Meta:
        verbose_name = _("ISO 3166 V2 Translate")
        verbose_name_plural = _("ISO 3166 V2 Translates")
        ordering = ['name']
        
class Region(models.Model):
    
    """
    For the /postcode-key-stats endpoint, available regions are:    
    north_east
    north_west
    east_midlands
    west_midlands
    east_of_england
    greater_london
    south_east
    south_west
    wales
    scotland
    northern_ireland

    """
    name = models.CharField(_("Name"), max_length=255)
    slug = models.SlugField(_("Slug"), max_length=255, unique=True)
    similar_name = models.TextField(_("similar name"), null=True, blank=True)
    country = models.ForeignKey(Country, on_delete=models.CASCADE, verbose_name=_("Country"), related_name="regions")
    created_at = models.DateTimeField(_("created at"), auto_now_add=True)
    updated_at = models.DateTimeField(_("updated at"), auto_now=True)

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

    def save(self, *args, **kwargs):
        if self.name: self.slug = slugify(self.name, allow_unicode=True)
        if self.similar_name is None or self.similar_name == "":
            sn = []
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        else:
            sn = self.similar_name.split("|,| ")
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                if self.name not in sn:
                    sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        super().save(*args, **kwargs)
        
class PostCode(models.Model):
    region = models.ForeignKey(Region, on_delete=models.CASCADE, verbose_name=_("Region"), related_name="postcodes")
    country = models.ForeignKey(Country, on_delete=models.CASCADE, verbose_name=_("Country"), related_name="postcodes")
    out_code = models.CharField(_("Out code"), max_length=255, null=True, blank=True)
    avg_price = models.FloatField(_("Average Price"), null=True, blank=True)
    avg_price_psf = models.FloatField(_("Average Price per Square Foot"), null=True, blank=True)
    avg_rent = models.FloatField(_("Average Rent"), null=True, blank=True)
    avg_yield = models.FloatField(_("Average Yield"), null=True, blank=True)
    growth_1y = models.FloatField(_("Growth 1 Year"), null=True, blank=True)
    growth_3y = models.FloatField(_("Growth 3 Year"), null=True, blank=True)
    growth_5y = models.FloatField(_("Growth 5 Year"), null=True, blank=True)
    sales_per_month = models.IntegerField(_("Sales per Month"), null=True, blank=True)
    turnover = models.IntegerField(_("Turnover"), null=True, blank=True)
    
    created_at = models.DateTimeField(_("created at"), auto_now_add=True)
    updated_at = models.DateTimeField(_("updated at"), auto_now=True)
    
    def __str__(self):
        return f"{self.out_code}"
    

class Location(models.Model):
    coordinates = models.PointField(_("coordinates"), srid=4326)
    name = models.CharField(_("name"), max_length=255, null=True, blank=True)
    slug = models.SlugField(_("Slug"), max_length=250, null=True, blank=True)
    similar_name = models.TextField(_("similar name"), null=True, blank=True)
    code = models.CharField(_("code"), max_length=255, null=True, blank=True)  
    point_type = models.CharField(_("type"), max_length=255, null=True, blank=True)
    point_class = models.CharField(_("class"), max_length=255, null=True, blank=True)
    country = models.ForeignKey(Country, on_delete=models.CASCADE, verbose_name=_("country"), null=True, blank=True)
    city = models.ForeignKey(City, on_delete=models.CASCADE,verbose_name=_("City"), null=True, blank=True)
    state = models.ForeignKey(State, on_delete=models.CASCADE, verbose_name=_("state"), null=True, blank=True)
    street = models.CharField(_("street"), max_length=255, null=True, blank=True)
    street_number = models.CharField(_("street"), max_length=255, null=True, blank=True)
    address_type = models.CharField(_("address type"), max_length=255, null=True, blank=True)
    address = models.TextField(_("address"), null=True, blank=True)
    place_id = models.CharField(_("place id"), max_length=255, null=True, blank=True)
    zip_code = models.CharField(_("zip code"), max_length=255, null=True, blank=True)
    post_code = models.ForeignKey(PostCode, on_delete=models.SET_NULL, verbose_name=_("post code"), related_name="locations", null=True, blank=True)
    rank = models.IntegerField(_("rank"), null=True, blank=True)
    display_name = models.TextField(_("display name"), null=True, blank=True)
    raw = models.JSONField(_("raw"), null=True, blank=True)
    raw_check = models.BooleanField(_("raw check"), default=False)
    
    district = models.ForeignKey(District, on_delete=models.CASCADE, verbose_name=_("district"), related_name="locations", null=True, blank=True)
    london_zone = models.ForeignKey(LondonZone, on_delete=models.CASCADE, verbose_name=_("London Zone"), related_name="locations", null=True, blank=True)
    london_area = models.ForeignKey(LondonArea, on_delete=models.CASCADE, verbose_name=_("London Area"), related_name="locations", null=True, blank=True)
    london_zip_state = models.ForeignKey(LondonZipState, on_delete=models.CASCADE, verbose_name=_("London Zip State"), related_name="locations", null=True, blank=True)
    
    iso_3166_2_lvl_0 = models.ForeignKey(Iso31662, on_delete=models.CASCADE, verbose_name=_("iso 3166 2 lvl 0"), related_name="location_lvl_0", null=True, blank=True)
    iso_3166_2_lvl_1 = models.ForeignKey(Iso31662, on_delete=models.CASCADE, verbose_name=_("iso 3166 2 lvl 1"), related_name="location_lvl_1", null=True, blank=True)
    iso_3166_2_lvl_2 = models.ForeignKey(Iso31662, on_delete=models.CASCADE, verbose_name=_("iso 3166 2 lvl 2"), related_name="location_lvl_2", null=True, blank=True)
    iso_3166_2_lvl_3 = models.ForeignKey(Iso31662, on_delete=models.CASCADE, verbose_name=_("iso 3166 2 lvl 3"), related_name="location_lvl_3", null=True, blank=True)
    iso_3166_2_lvl_4 = models.ForeignKey(Iso31662, on_delete=models.CASCADE, verbose_name=_("iso 3166 2 lvl 4"), related_name="location_lvl_4", null=True, blank=True)
    iso_3166_2_lvl_5 = models.ForeignKey(Iso31662, on_delete=models.CASCADE, verbose_name=_("iso 3166 2 lvl 5"), related_name="location_lvl_5", null=True, blank=True)
    iso_3166_2_lvl_6 = models.ForeignKey(Iso31662, on_delete=models.CASCADE, verbose_name=_("iso 3166 2 lvl 6"), related_name="location_lvl_6", null=True, blank=True)
    iso_3166_2_lvl_7 = models.ForeignKey(Iso31662, on_delete=models.CASCADE, verbose_name=_("iso 3166 2 lvl 7"), related_name="location_lvl_7", null=True, blank=True)
    iso_3166_2_lvl_8 = models.ForeignKey(Iso31662, on_delete=models.CASCADE, verbose_name=_("iso 3166 2 lvl 8"), related_name="location_lvl_8", null=True, blank=True)
    iso_3166_2_lvl_9 = models.ForeignKey(Iso31662, on_delete=models.CASCADE, verbose_name=_("iso 3166 2 lvl 9"), related_name="location_lvl_9", null=True, blank=True)
    
    created_at = models.DateTimeField(_("created at"), auto_now_add=True)
    updated_at = models.DateTimeField(_("updated at"), auto_now=True)
    
    def save(self, *args, **kwargs):
        if self.name: self.slug = slugify(self.name, allow_unicode=True)
            
        if self.post_code is None and self.zip_code is not None:
            postcode = self.zip_code.split(" ")[0]
            try: self.post_code = PostCode.objects.get(out_code=postcode)
            except:pass
        if self.similar_name is None or self.similar_name == "":
            sn = []
            if self.code and self.code not in sn:
                sn.append(self.code)
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        else:
            sn = self.similar_name.split("|,| ")
            if self.code and self.code not in sn:
                sn.append(self.code)
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                if self.name not in sn:
                    sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        super(Location, self).save(*args, **kwargs)
    
    def __str__(self):
        return f"{self.name}"
    
    class Meta:
        verbose_name = _("Location")
        verbose_name_plural = _("Locations")
        ordering = ["name"]
        constraints = [
            models.UniqueConstraint(fields=['slug', 'coordinates'], name='repeated_slug_coordinates_location'),
        ]
        
        
class LocationTranslate(models.Model):
    location = models.ForeignKey(Location, on_delete=models.CASCADE, verbose_name=_("location"), related_name="translates")
    language = models.ForeignKey("main.Language", on_delete=models.CASCADE, verbose_name=_("language"), related_name="location_translates")
    display_name = models.CharField(_("display name"), max_length=255, null=True, blank=True)
    name = models.CharField(_("name"), max_length=255, null=True, blank=True)
    address = models.TextField(_("address"), null=True, blank=True)
    street = models.CharField(_("street"), max_length=255, null=True, blank=True)
    building = models.CharField(_("building"), max_length=255, null=True, blank=True)
    zip_code = models.CharField(_("zip code"), max_length=255, null=True, blank=True)
    street_number = models.CharField(_("street number"), max_length=255, null=True, blank=True)
    raw = models.JSONField(_("raw"), null=True, blank=True)
    raw_check = models.BooleanField(_("raw check"), default=False)
    similar_name = models.TextField(_("similar name"), null=True, blank=True)
    created_at = models.DateTimeField(_("created at"), auto_now_add=True)
    updated_at = models.DateTimeField(_("updated at"), auto_now=True)
    
    def save(self, *args, **kwargs):
        if self.similar_name is None or self.similar_name == "":
            sn = []
            if self.display_name and self.display_name not in sn:
                sn.append(self.display_name)
            if self.name: 
                sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        else:
            sn = self.similar_name.split("|,| ")
            if self.display_name and self.display_name not in sn:
                sn.append(self.display_name)
            if self.name: 
                if self.name not in sn:
                    sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        super().save(*args, **kwargs)

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

    class Meta:
        verbose_name = _("Location Translate")
        verbose_name_plural = _("Location Translates")
        ordering = ["name"]
        constraints = [
            models.UniqueConstraint(fields=['language', 'location'], name='repeated_language_location_translate'),
        ]


FACILITY_TYPE = [
    (0,_("Schools")),
    (1,_("Subway stations")),
    (2,_("Train stations")),
    (3,_("Bus stations")),
    (4,_("Airports")),
    (5,_("Restaurants")),
    (6,_("Hospitals")),
    (7,_("Parking")),
    (8,_("Banks")),
    (9,_("Cinemas")),
    (10,_("Pharmacies")),
    (11,_("Markets")),
    (12,_("Cafes")),
    (13,_("Malls")),
    (14,_("Bars")),
    (15,_("Theaters")),
    (16,_("Parks")),
    (17,_("Gym")),
    (18,_("Supermarkets")),
    (19,_("123")),
]

class PublicFacilityType(models.Model):
    name = models.CharField(_("Name"), max_length=500)
    slug = models.SlugField(_("Slug"), unique=True, max_length=500, null=True, blank=True)
    icon = models.FileField(_("Icon"), upload_to="icons/", null=True, blank=True)
    created_at = models.DateTimeField(_("created at"), auto_now_add=True)
    updated_at = models.DateTimeField(_("updated at"), auto_now=True)
    
    def save(self, *args, **kwargs):
        if self.name: self.slug = slugify(self.name, allow_unicode=True)
        super().save(*args, **kwargs)
        
    def __str__(self):
        return self.name
    
    class Meta:
        verbose_name = _("Public Facility type")
        verbose_name_plural = _("Public Facilities Types")
        ordering = ["name"]
    
class PublicFacilityTypeTranslate(models.Model):
    language = models.ForeignKey("main.Language", on_delete=models.CASCADE, verbose_name=_("language"), related_name="public_facility_type_translates")
    type = models.ForeignKey(PublicFacilityType, on_delete=models.CASCADE, verbose_name=_("type"), related_name="translates")
    name = models.CharField(_("Name"), max_length=500, null=True, blank=True)
    slug = models.SlugField(_("Slug"), max_length=500, null=True, blank=True)
    
    created_at = models.DateTimeField(_("created at"), auto_now_add=True)
    updated_at = models.DateTimeField(_("updated at"), auto_now=True)
    
    def save(self, *args, **kwargs):
        self.slug = slugify(self.type.name, allow_unicode=True)
        super().save(*args, **kwargs)
        
    def __str__(self):
        return self.name
    
    class Meta:
        verbose_name = _("Public Facility type Translate")
        verbose_name_plural = _("Public Facilities Types Translates")
        ordering = ["name"]

class PublicFacility(models.Model):
    name = models.CharField(_("Name"), max_length=500, null=True, blank=True)
    slug = models.SlugField(_("Slug"), max_length=500, null=True, blank=True)
    similar_name = models.TextField(_("similar name"), null=True, blank=True)
    type = models.ForeignKey(PublicFacilityType, on_delete=models.CASCADE, verbose_name=_("type"), related_name="facilities")
    is_active = models.BooleanField(_("active"), default=False)
    location = models.ForeignKey(Location, on_delete=models.CASCADE, verbose_name=_("location"), related_name="public_facilities", null=True, blank=True)
    coordinates = models.PointField(_("coordinates"), srid=4326, null=True, blank=True)
    rank = models.PositiveSmallIntegerField(_("Rank"), default=10)
    
    created_at = models.DateTimeField(_("created at"), auto_now_add=True)
    updated_at = models.DateTimeField(_("updated at"), auto_now=True)
    
    def save(self, *args, **kwargs):
        if self.name: self.name = self.name.replace("\\'","'")
        if self.name: self.slug = slugify(self.name, allow_unicode=True)
        if self.similar_name is None or self.similar_name == "":
            sn = []
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        else:
            sn = self.similar_name.split("|,| ")
            if self.slug and self.slug not in sn:
                sn.append(self.slug)
            if self.name: 
                if self.name not in sn:
                    sn.append(self.name)
                if slugify(self.name, allow_unicode=True) not in sn:
                    sn.append(slugify(self.name, allow_unicode=True))
                if self.name.replace(" ", "") not in sn:
                    sn.append(self.name.replace(" ", ""))
                if self.name.replace(" ", "-") not in sn:
                    sn.append(self.name.replace(" ", "-"))
                if self.name.replace(" ", "_") not in sn:
                    sn.append(self.name.replace(" ", "_"))
            self.similar_name = "|,| ".join(sn)
        super().save(*args, **kwargs)

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

    class Meta:
        verbose_name = _("Public Facility")
        verbose_name_plural = _("Public Facilities")
        ordering = ["name"]
        constraints = [
            models.UniqueConstraint(fields=['slug', 'type', 'coordinates'], name='repeated_slug_type_public_facility'),
        ]
        

def image_street_view_folder(instance, filename):
    return f'street_view/{uuid.uuid4().hex}.webp'

class StreetView(models.Model):
    """
    Represents a street view image associated with a location.
    
    The `StreetView` model stores information about a street view image, including the location it is associated with, the image file, and various metadata about the image such as the panorama ID, coordinates, orientation, and date.
    
    The `image_street_view_folder` function is used to generate a unique file path for the street view image when it is uploaded.
    get image from https://cbk{num}.google.com/cbk?output=tile&panoid={panorama_id}&zoom=1&x={x}&y={y}  # num: 1,2,3
    
    zoom    x   y   2**n   example
    0       0   0   2**0[0]   [0,0]
    1       1   1   2**1[0,1]   [0,0],[0,1],[1,0],[1,1]
    2       3   3   2**2[0,1,2,3]   [0,0],[0,1],[0,2],[0,3],[1,0],[1,1],[1,2],[1,3],[2,0],[2,1],[2,2],[2,3],[3,0],[3,1],[3,2],[3,3]
    """
    location = models.ForeignKey(Location, on_delete=models.CASCADE, verbose_name=_("location"), related_name="street_view")
    development = models.ForeignKey("development.Development", on_delete=models.CASCADE, verbose_name=_("development"), related_name="street_views")
    image = WEBPField(_("Image"), upload_to=image_street_view_folder, blank=True, null=True)
    panorama_id = models.CharField(_("Panorama ID"), max_length=50, unique=True, blank=True, null=True)
    lat = models.FloatField(_("Latitude"), blank=True, null=True)
    lon = models.FloatField(_("Longitude"), blank=True, null=True)
    heading = models.FloatField(_("Heading"), blank=True, null=True)
    pitch = models.FloatField(_("Pitch"), blank=True, null=True)
    roll = models.FloatField(_("Roll"), blank=True, null=True)
    date = models.CharField(_("Date of image"), max_length=50, blank=True,null=True)
    elevation = models.FloatField(_("Elevation"), blank=True, null=True)
    image_downloaded = models.BooleanField(_("Image downloaded"), default=False)

    created_at = models.DateTimeField(_("created at"), auto_now_add=True)
    updated_at = models.DateTimeField(_("updated at"), auto_now=True)
    





DATA_TYPE = [
    (0, _("growth")),
    (1, _("price")),
    (2, _("postcode")),
    (3, _("demographics")),
]    
class PropertyDataRaw(models.Model):
    region = models.ForeignKey(Region, on_delete=models.CASCADE, verbose_name=_("Region"), related_name="property_data_raw", null=True, blank=True)
    postcode = models.ForeignKey(PostCode, on_delete=models.CASCADE, verbose_name=_("Postcode"), related_name="property_data_raw", null=True, blank=True)
    type = models.SmallIntegerField(_("Type"), choices=DATA_TYPE, default=0)
    data = models.JSONField(_("Data"), null=True, blank=True)
    checked = models.BooleanField(_("Checked"), default=False)
    created_at = models.DateTimeField(_("created at"), auto_now_add=True)
    updated_at = models.DateTimeField(_("updated at"), auto_now=True)
    

class PriceGrowth(models.Model):
    country = models.ForeignKey(Country, on_delete=models.CASCADE, verbose_name=_("Country"), related_name="price_growth")
    date = models.DateField(_("Date"), null=True, blank=True)
    postcode = models.CharField(_("Postcode"), max_length=255, null=True, blank=True)
    postcode_type = models.CharField(_("Postcode Type"), max_length=255, null=True, blank=True)
    post_code = models.ForeignKey(PostCode, on_delete=models.CASCADE, verbose_name=_("Postcode"), related_name="price_growth", null=True, blank=True)
    price = models.FloatField(_("Price"), null=True, blank=True)
    price_change = models.FloatField(_("Price Change"), null=True, blank=True)
    change_percent = models.FloatField(_("Change Percent"), null=True, blank=True)

    created_at = models.DateTimeField(_("created at"), auto_now_add=True)
    updated_at = models.DateTimeField(_("updated at"), auto_now=True)
    
    def save(self, *args, **kwargs):
        return super().save(*args, **kwargs)

    def __str__(self):
        return f"{self.postcode} {self.date}"

    class Meta:
        verbose_name = _("Price Growth")
        verbose_name_plural = _("Price Growth")
        ordering = ["postcode", "date", "-id"]
        constraints = [
            models.UniqueConstraint(fields=['country', 'date', 'postcode'], name='repeated_country_date_postcode_price_growth'),
        ]
        
class Demographic(models.Model):
    country = models.ForeignKey(Country, on_delete=models.CASCADE, verbose_name=_("Country"), related_name="demographic")
    postcode = models.CharField(_("Postcode"), max_length=255, null=True, blank=True)
    postcode_type = models.CharField(_("Postcode Type"), max_length=255, null=True, blank=True)
    post_code = models.ForeignKey(PostCode, on_delete=models.CASCADE, verbose_name=_("Postcode"), related_name="demographic", null=True, blank=True)
    deprivation = models.FloatField(_("Deprivation"), null=True, blank=True)
    health = models.FloatField(_("Health"), null=True, blank=True)
    social_grade = models.JSONField(_("Social Grade"), null=True, blank=True)
    age = models.JSONField(_("Age"), null=True, blank=True)
    politics = models.JSONField(_("Politics"), null=True, blank=True)
    proportion_with_degree = models.FloatField(_("Proportion with Degree"), null=True, blank=True)
    vehicles_per_household = models.FloatField(_("Vehicles per Household"), null=True, blank=True)
    commute_method = models.JSONField(_("Commute Method"), null=True, blank=True)
    
    created_at = models.DateTimeField(_("created at"), auto_now_add=True)
    updated_at = models.DateTimeField(_("updated at"), auto_now=True)

    def __str__(self):
        return f"{self.postcode}"
    
    class Meta:
        verbose_name = _("Demographic")
        verbose_name_plural = _("Demographic")
        ordering = ["postcode", "-id"]
        constraints = [
            models.UniqueConstraint(fields=['country', 'postcode'], name='repeated_country_postcode_demographic'),
        ]