"""A openid store using django cache"""

from openid.store.interface import OpenIDStore
from openid.store import nonce

from django.core.cache import cache

import logging
import time

DEFAULT_ASSOCIATIONS_TIMEOUT = 60
DEFAULT_NONCE_TIMEOUT = 600

ASSOCIATIONS_KEY_PREFIX = 'openid.provider.associations.'
NONCE_KEY_PREFIX = 'openid.provider.nonce.'

log = logging.getLogger('DjangoOpenIDStore')


def get_url_key(server_url):
    key = ASSOCIATIONS_KEY_PREFIX + server_url
    return key


def get_nonce_key(server_url, timestamp, salt):
    key = '{prefix}{url}.{ts}.{salt}'.format(prefix=NONCE_KEY_PREFIX,
                                             url=server_url,
                                             ts=timestamp,
                                             salt=salt)
    return key


class DjangoOpenIDStore(OpenIDStore):
    def __init__(self):
        log.info('DjangoStore cache:' + str(cache.__class__))

    def storeAssociation(self, server_url, assoc):
        key = get_url_key(server_url)

        log.info('storeAssociation {0}'.format(key))

        associations = cache.get(key, {})
        associations[assoc.handle] = assoc

        cache.set(key, associations, DEFAULT_ASSOCIATIONS_TIMEOUT)

    def getAssociation(self, server_url, handle=None):
        key = get_url_key(server_url)

        log.info('getAssociation {0}'.format(key))

        associations = cache.get(key, {})

        assoc = None

        if handle is None:
            # get best association
            valid_assocs = [a for a in associations if a.getExpiresIn() > 0]
            if valid_assocs:
                valid_assocs.sort(lambda a: a.getExpiresIn(), reverse=True)
                assoc = valid_assocs.sort[0]
        else:
            assoc = associations.get(handle)

        # check expiration and remove if it has expired
        if assoc and assoc.getExpiresIn() <= 0:
            if handle is None:
                cache.delete(key)
            else:
                associations.pop(handle)
                cache.set(key, associations, DEFAULT_ASSOCIATIONS_TIMEOUT)
            assoc = None

        return assoc

    def removeAssociation(self, server_url, handle):
        key = get_url_key(server_url)

        log.info('removeAssociation {0}'.format(key))

        associations = cache.get(key, {})

        removed = False

        if associations:
            if handle is None:
                cache.delete(key)
                removed = True
            else:
                assoc = associations.pop(handle, None)
                if assoc:
                    cache.set(key, associations, DEFAULT_ASSOCIATIONS_TIMEOUT)
                    removed = True

        return removed

    def useNonce(self, server_url, timestamp, salt):
        key = get_nonce_key(server_url, timestamp, salt)

        log.info('useNonce {0}'.format(key))

        if abs(timestamp - time.time()) > nonce.SKEW:
            return False

        anonce = cache.get(key)

        found = False

        if anonce is None:
            cache.set(key, '-', DEFAULT_NONCE_TIMEOUT)
            found = False
        else:
            found = True

        return found

    def cleanupNonces(self):
        # not necesary, keys will timeout
        return 0

    def cleanupAssociations(self):
        # not necesary, keys will timeout
        return 0
