import re

from cms.utils.conf import get_cms_setting

CMS_PAGE_CACHE_VERSION_KEY = get_cms_setting("CACHE_PREFIX") + '_PAGE_CACHE_VERSION'


def _get_cache_version():
    """
    Returns the current page cache version, explicitly setting one if not
    defined.
    """
    from django.core.cache import cache

    version = cache.get(CMS_PAGE_CACHE_VERSION_KEY)

    if version:
        return version
    else:
        _set_cache_version(1)
        return 1


def _set_cache_version(version):
    """
    Set the cache version to the specified value.
    """
    from django.core.cache import cache

    cache.set(
        CMS_PAGE_CACHE_VERSION_KEY,
        version,
        get_cms_setting('CACHE_DURATIONS')['content']
    )


def invalidate_cms_page_cache():
    """
    Invalidates the CMS PAGE CACHE.
    """

    #
    # NOTE: We're using a cache versioning strategy for invalidating the page
    # cache when necessary. Instead of wiping all the old entries, we simply
    # increment the version number rendering all previous entries
    # inaccessible and left to expire naturally.
    #
    # ALSO NOTE: According to the Django documentation, a timeout value of
    # `None' (in version 1.6+) is supposed to mean "cache forever", however,
    # this is actually only implemented as only slightly less than 30 days in
    # some backends (memcached, in particular). In older Djangos, `None' means
    # "use default value".  To avoid issues arising from different Django
    # versions and cache backend implementations, we will explicitly set the
    # lifespan of the CMS_PAGE_CACHE_VERSION entry to whatever is set in
    # settings.CACHE_DURATIONS['content']. This allows users to adjust as
    # necessary for their backend.
    #
    # To prevent writing cache entries that will live longer than our version
    # key, we will always re-write the current version number into the cache
    # just after we write any new cache entries, thus ensuring that the
    # version number will always outlive any entries written against that
    # version. This is a cheap operation.
    #
    # If there are no new cache writes before the version key expires, its
    # perfectly OK, since any previous entries cached against that version
    # will have also expired, so, it'd be pointless to try to access them
    # anyway.
    #
    version = _get_cache_version()
    _set_cache_version(version + 1)


CLEAN_KEY_PATTERN = re.compile(r'[^a-zA-Z0-9_-]')


def _clean_key(key):
    return CLEAN_KEY_PATTERN.sub('-', key)


def _get_cache_key(name, page_lookup, lang, site_id):
    from cms.models import Page
    if isinstance(page_lookup, Page):
        page_key = str(page_lookup.pk)
    else:
        page_key = str(page_lookup)
    page_key = _clean_key(page_key)
    return f'{get_cms_setting("CACHE_PREFIX")}{name}__page_lookup:{page_key}_site:{str(site_id)}_lang:{str(lang)}'
