from tinydb import TinyDB, Query
import hug
import hashlib
import logging
import os


logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
db = TinyDB("db.json")

"""
  Helper Methods
"""


def hash_password(password, salt):
    """
    Securely hash a password using a provided salt
    :param password:
    :param salt:
    :return: Hex encoded SHA512 hash of provided password
    """
    password = str(password).encode("utf-8")
    salt = str(salt).encode("utf-8")
    return hashlib.sha512(password + salt).hexdigest()


def gen_api_key(username):
    """
    Create a random API key for a user
    :param username:
    :return: Hex encoded SHA512 random string
    """
    salt = str(os.urandom(64)).encode("utf-8")
    return hash_password(username, salt)


@hug.cli()
def authenticate_user(username, password):
    """
    Authenticate a username and password against our database
    :param username:
    :param password:
    :return: authenticated username
    """
    user_model = Query()
    user = db.get(user_model.username == username)

    if not user:
        logger.warning("User %s not found", username)
        return False

    if user["password"] == hash_password(password, user.get("salt")):
        return user["username"]

    return False


@hug.cli()
def authenticate_key(api_key):
    """
    Authenticate an API key against our database
    :param api_key:
    :return: authenticated username
    """
    user_model = Query()
    user = db.search(user_model.api_key == api_key)[0]
    if user:
        return user["username"]
    return False


"""
  API Methods start here
"""

api_key_authentication = hug.authentication.api_key(authenticate_key)
basic_authentication = hug.authentication.basic(authenticate_user)


@hug.cli()
def add_user(username, password):
    """
    CLI Parameter to add a user to the database
    :param username:
    :param password:
    :return: JSON status output
    """

    user_model = Query()
    if db.search(user_model.username == username):
        return {"error": "User {0} already exists".format(username)}

    salt = hashlib.sha512(str(os.urandom(64)).encode("utf-8")).hexdigest()
    password = hash_password(password, salt)
    api_key = gen_api_key(username)

    user = {"username": username, "password": password, "salt": salt, "api_key": api_key}
    user_id = db.insert(user)

    return {"result": "success", "eid": user_id, "user_created": user}


@hug.get("/api/get_api_key", requires=basic_authentication)
def get_token(authed_user: hug.directives.user):
    """
    Get Job details
    :param authed_user:
    :return:
    """
    user_model = Query()
    user = db.search(user_model.username == authed_user)[0]

    if user:
        out = {"user": user["username"], "api_key": user["api_key"]}
    else:
        # this should never happen
        out = {"error": "User {0} does not exist".format(authed_user)}

    return out


# Same thing, but authenticating against an API key
@hug.get(("/api/job", "/api/job/{job_id}/"), requires=api_key_authentication)
def get_job_details(job_id):
    """
    Get Job details
    :param job_id:
    :return:
    """
    job = {"job_id": job_id, "details": "Details go here"}

    return job


if __name__ == "__main__":
    add_user.interface.cli()
