from contextlib import contextmanager

from django.conf import settings
from django.core.cache import cache
from django.db import models, transaction
from django.utils.http import int_to_base36
from django.utils.timezone import make_naive
from django.utils.translation import ugettext_lazy as _

from c3nav.mapdata.tasks import delete_old_cached_tiles


class MapUpdate(models.Model):
    """
    A map update. created whenever mapdata is changed.
    """
    datetime = models.DateTimeField(auto_now_add=True, db_index=True)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.PROTECT)
    type = models.CharField(max_length=32)

    class Meta:
        verbose_name = _('Map update')
        verbose_name_plural = _('Map updates')
        default_related_name = 'mapupdates'
        get_latest_by = 'datetime'

    @classmethod
    def last_update(cls):
        last_update = cache.get('mapdata:last_update', None)
        if last_update is not None:
            return last_update
        with cls.lock():
            last_update = cls.objects.latest()
            cache.set('mapdata:last_update', (last_update.pk, last_update.datetime), 900)
        return last_update.pk, last_update.datetime

    @property
    def cache_key(self):
        return int_to_base36(self.pk)+'_'+int_to_base36(int(make_naive(self.datetime).timestamp()))

    @classmethod
    def current_cache_key(cls):
        pk, dt = cls.last_update()
        return int_to_base36(pk)+'_'+int_to_base36(int(make_naive(dt).timestamp()))

    @classmethod
    @contextmanager
    def lock(cls):
        with transaction.atomic():
            yield cls.objects.select_for_update().earliest()

    def save(self, **kwargs):
        if self.pk is not None:
            raise TypeError

        old_cache_key = MapUpdate.current_cache_key()

        from c3nav.mapdata.models import AltitudeArea
        AltitudeArea.recalculate()

        from c3nav.mapdata.render.base import LevelRenderData
        LevelRenderData.rebuild()

        super().save(**kwargs)

        from c3nav.mapdata.cache import changed_geometries
        changed_geometries.save(old_cache_key, self.cache_key)

        cache.set('mapdata:last_update', (self.pk, self.datetime), 900)
        delete_old_cached_tiles.apply_async(countdown=5)

from contextlib import contextmanager

from django.conf import settings
from django.core.cache import cache
from django.db import models, transaction
from django.utils.http import int_to_base36
from django.utils.timezone import make_naive
from django.utils.translation import ugettext_lazy as _

from c3nav.mapdata.tasks import delete_old_cached_tiles


class MapUpdate(models.Model):
    """
    A map update. created whenever mapdata is changed.
    """
    datetime = models.DateTimeField(auto_now_add=True, db_index=True)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.PROTECT)
    type = models.CharField(max_length=32)

    class Meta:
        verbose_name = _('Map update')
        verbose_name_plural = _('Map updates')
        default_related_name = 'mapupdates'
        get_latest_by = 'datetime'

    @classmethod
    def last_update(cls):
        last_update = cache.get('mapdata:last_update', None)
        if last_update is not None:
            return last_update
        with cls.lock():
            last_update = cls.objects.latest()
            cache.set('mapdata:last_update', (last_update.pk, last_update.datetime), 900)
        return last_update.pk, last_update.datetime

    @property
    def cache_key(self):
        return int_to_base36(self.pk)+'_'+int_to_base36(int(make_naive(self.datetime).timestamp()))

    @classmethod
    def current_cache_key(cls):
        pk, dt = cls.last_update()
        return int_to_base36(pk)+'_'+int_to_base36(int(make_naive(dt).timestamp()))

    @classmethod
    @contextmanager
    def lock(cls):
        with transaction.atomic():
            yield cls.objects.select_for_update().earliest()

    def save(self, **kwargs):
        if self.pk is not None:
            raise TypeError

        old_cache_key = MapUpdate.current_cache_key()

        from c3nav.mapdata.models import AltitudeArea
        AltitudeArea.recalculate()

        super().save(**kwargs)

        from c3nav.mapdata.cache import changed_geometries
        changed_geometries.save(old_cache_key, self.cache_key)

        from c3nav.mapdata.render.base import LevelRenderData
        LevelRenderData.rebuild()

        cache.set('mapdata:last_update', (self.pk, self.datetime), 900)
        delete_old_cached_tiles.apply_async(countdown=5)

rebuild LevelRenderData after saving changed_geometries