'''
    Firebase - library to generate a token
    License: https://github.com/firebase/firebase-token-generator-python/blob/master/LICENSE
    Tweaked and Edited by @danielcebrianr and @lduarte1991

    This library will take either objects or strings and use python's built-in encoding
    system as specified by RFC 3548. Thanks to the firebase team for their open-source
    library. This was made specifically for speaking with the annotation_storage_url and
    can be used and expanded, but not modified by anyone else needing such a process.
'''
from base64 import urlsafe_b64encode
import hashlib
import hmac
import sys
try:
    import json
except ImportError:
    import simplejson as json

__all__ = ['create_token']

TOKEN_SEP = '.'


def create_token(secret, data):
    '''
    Simply takes in the secret key and the data and
    passes it to the local function _encode_token
    '''
    return _encode_token(secret, data)


if sys.version_info < (2, 7):
    def _encode(bytes_data):
        '''
        Takes a json object, string, or binary and
        uses python's urlsafe_b64encode to encode data
        and make it safe pass along in a url.
        To make sure it does not conflict with variables
        we make sure equal signs are removed.
        More info: docs.python.org/2/library/base64.html
        '''
        encoded = urlsafe_b64encode(bytes(bytes_data))
        return encoded.decode('utf-8').replace('=', '')
else:
    def _encode(bytes_info):
        '''
        Same as above function but for Python 2.7 or later
        '''
        encoded = urlsafe_b64encode(bytes_info)
        return encoded.decode('utf-8').replace('=', '')


def _encode_json(obj):
    '''
    Before a python dict object can be properly encoded,
    it must be transformed into a jason object and then
    transformed into bytes to be encoded using the function
    defined above.
    '''
    return _encode(bytearray(json.dumps(obj), 'utf-8'))


def _sign(secret, to_sign):
    '''
    This function creates a sign that goes at the end of the
    message that is specific to the secret and not the actual
    content of the encoded body.
    More info on hashing: http://docs.python.org/2/library/hmac.html
    The function creates a hashed values of the secret and to_sign
    and returns the digested values based the secure hash
    algorithm, 256
    '''
    def portable_bytes(string):
        '''
        Simply transforms a string into a bytes object,
        which is a series of immutable integers 0<=x<=256.
        Always try to encode as utf-8, unless it is not
        compliant.
        '''
        try:
            return bytes(string, 'utf-8')
        except TypeError:
            return bytes(string)
    return _encode(hmac.new(portable_bytes(secret), portable_bytes(to_sign), hashlib.sha256).digest())  # pylint: disable=E1101


def _encode_token(secret, claims):
    '''
    This is the main function that takes the secret token and
    the data to be transmitted. There is a header created for decoding
    purposes. Token_SEP means that a period/full stop separates the
    header, data object/message, and signatures.
    '''
    encoded_header = _encode_json({'typ': 'JWT', 'alg': 'HS256'})
    encoded_claims = _encode_json(claims)
    secure_bits = '%s%s%s' % (encoded_header, TOKEN_SEP, encoded_claims)
    sig = _sign(secret, secure_bits)
    return '%s%s%s' % (secure_bits, TOKEN_SEP, sig)
