# -*- encoding: utf-8 -*-
""" Helper and utils functions """
from datetime import datetime
from email.utils import parsedate

from .cache import BaseCache
from .cache import DictCache
from .cache import DummyCache


def make_cache_key(request):
    """ Generate a cache key from request object data """
    headers = frozenset(request._p['header'].items())
    path = frozenset(request._p['path'].items())
    query = frozenset(request._p['query'])
    return (request.url, headers, path, query)


def check_cache(cache):
    """ check if a cache fits esipy needs or not """
    if isinstance(cache, BaseCache):
        return cache
    elif cache is False:
        return DictCache()
    elif cache is None:
        return DummyCache()
    else:
        raise ValueError('Provided cache must implement BaseCache')


def get_cache_time_left(expires_header):
    """ return the time left in second for an expires header """
    epoch = datetime(1970, 1, 1)
    # this date is ALWAYS in UTC (RFC 7231)
    expire = (
        datetime(
            *parsedate(expires_header)[:6]
        ) - epoch
    ).total_seconds()
    now = (datetime.utcnow() - epoch).total_seconds()
    return int(expire) - int(now)

# -*- encoding: utf-8 -*-
""" Helper and utils functions """
import base64
import hashlib
import os

from datetime import datetime
from email.utils import parsedate

from .cache import BaseCache
from .cache import DictCache
from .cache import DummyCache


def make_cache_key(request):
    """ Generate a cache key from request object data """
    headers = frozenset(request._p['header'].items())
    path = frozenset(request._p['path'].items())
    query = frozenset(request._p['query'])
    return (request.url, headers, path, query)


def check_cache(cache):
    """ check if a cache fits esipy needs or not """
    if isinstance(cache, BaseCache):
        return cache
    elif cache is False:
        return DictCache()
    elif cache is None:
        return DummyCache()
    else:
        raise ValueError('Provided cache must implement BaseCache')


def get_cache_time_left(expires_header):
    """ return the time left in second for an expires header """
    epoch = datetime(1970, 1, 1)
    # this date is ALWAYS in UTC (RFC 7231)
    expire = (
        datetime(
            *parsedate(expires_header)[:6]
        ) - epoch
    ).total_seconds()
    now = (datetime.utcnow() - epoch).total_seconds()
    return int(expire) - int(now)


def code_verifier(n_bytes=64):
    """
    source: https://github.com/openstack/deb-python-oauth2client
    Generates a 'code_verifier' as described in section 4.1 of RFC 7636.
    This is a 'high-entropy cryptographic random string' that will be
    impractical for an attacker to guess.
    Args:
        n_bytes: integer between 31 and 96, inclusive. default: 64
            number of bytes of entropy to include in verifier.
    Returns:
        Bytestring, representing urlsafe base64-encoded random data.
    """
    verifier = base64.urlsafe_b64encode(os.urandom(n_bytes)).rstrip(b'=')
    # https://tools.ietf.org/html/rfc7636#section-4.1
    # minimum length of 43 characters and a maximum length of 128 characters.
    if len(verifier) < 43:
        raise ValueError("Verifier too short. n_bytes must be > 30.")
    elif len(verifier) > 128:
        raise ValueError("Verifier too long. n_bytes must be < 97.")
    else:
        return verifier


def code_challenge(verifier):
    """
    source: https://github.com/openstack/deb-python-oauth2client
    Creates a 'code_challenge' as described in section 4.2 of RFC 7636
    by taking the sha256 hash of the verifier and then urlsafe
    base64-encoding it.
    Args:
        verifier: bytestring, representing a code_verifier as generated by
            code_verifier().
    Returns:
        Bytestring, representing a urlsafe base64-encoded sha256 hash digest,
            without '=' padding.
    """
    digest = hashlib.sha256(verifier).digest()
    return base64.urlsafe_b64encode(digest).rstrip(b'=')

add utils for PKCE