# By: Riasat Ullah
# This class syncs up API keys data stored in db with that in cache.

from cache_queries import cache_api_keys
from dbqueries import db_api_keys
from taskcallrest import settings
from utils import times
import configuration


def store_api_keys_in_cache(conn, client, timestamp):
    '''
    Retrieves API keys of all organizations and stores them in cache.
    :param conn: db connection
    :param client: cache client
    :param timestamp: timestamp when this request is being made
    '''
    all_api_keys = db_api_keys.get_enabled_api_key_accepting_info(conn, timestamp)
    cache_api_keys.remove_all_api_keys(client)
    cache_api_keys.store_api_keys(client, all_api_keys)


def get_api_key_details(conn, client, timestamp, api_key, limit_check=True):
    '''
    Get the details of an API key (information needed to accept the request. Instead of throwing an error
    when limit check is enforced, the function returns None.
    :param conn: db connection
    :param client: cache client
    :param timestamp: timestamp this request is being made on
    :param api_key: the API key
    :param limit_check: True if upon fetch request per minute should be checked or not
    :return: (list) -> [key id, org id, perm, for user, ip restrictions]
    '''
    details = None
    if settings.CACHE_ON:
        details = cache_api_keys.get_api_key_details(client, api_key)

    if details is None:
        details = db_api_keys.get_enabled_api_key_accepting_info(conn, timestamp, api_key=api_key)
        details = details[api_key] if api_key in details else None

    if settings.CACHE_ON and details is not None and limit_check:
        if len(details) > 5:
            log_time = details[5]
            if isinstance(log_time, str):
                log_time = times.get_timestamp_from_string(log_time)
            if (timestamp - log_time).total_seconds() <= 60:
                count = details[6] if len(details) > 6 else 0
                if count > configuration.api_key_maximum_request_per_minute:
                    return None
                else:
                    count += 1
                    details[6] = count
            else:
                details[5] = timestamp
                details[6] = 1
        else:
            count = 1
            details.append(timestamp)
            details.append(count)

        cache_api_keys.store_single_api_key(client, api_key, details)

    if details is None:
        return None
    else:
        return details[:5]


def create_api_key(conn, client, timestamp, user_id, organization_id, api_key, key_name, access_type, api_version,
                   for_user_id=None, ip_restrictions=None):
    '''
    Create an API key and syncs it up in the cache.
    :param conn: db connection
    :param client: cache client
    :param timestamp: timestamp when this request is being made
    :param user_id: user_id of the user creating the api key
    :param organization_id: ID of the organization this key belongs to
    :param api_key: the actual API key
    :param key_name: readable name to identify the API key with
    :param access_type: the type of access this API key will have
    :param api_version: the version/type of API this key will have access to
    :param for_user_id: user_id of the user who the key is for if it is user specific
    :param ip_restrictions: (list) of IP addresses to restrict requests from
    :errors: AssertionError, DatabaseError, Redis errors
    '''
    db_api_keys.create_api_key(conn, timestamp, user_id, organization_id, api_key, key_name, access_type,
                               api_version, for_user_id=for_user_id, ip_restrictions=ip_restrictions)
    if settings.CACHE_ON:
        details = db_api_keys.get_enabled_api_key_accepting_info(conn, timestamp, api_key=api_key)[api_key]
        cache_api_keys.store_single_api_key(client, api_key, details)


def edit_api_key(conn, client, timestamp, organization_id, api_key_id, to_enable=None, ip_restrictions=None):
    '''
    Edits basic details of an existing API key.
    :param conn: db connection
    :param client: cache client
    :param timestamp: timestamp when this request is being made
    :param organization_id: ID of the organization the API key belongs to
    :param api_key_id: integer ID of the api key
    :param to_enable: True if should be enabled; False otherwise
    :param ip_restrictions: edited IP restrictions
    :return: the actual API key
    :errors: AssertionError, DatabaseError, Redis errors
    '''
    api_key = db_api_keys.edit_api_key(conn, timestamp, organization_id, api_key_id, to_enable, ip_restrictions)
    if settings.CACHE_ON:
        if not to_enable:
            cache_api_keys.remove_single_api_key(client, api_key)
        else:
            key_details = db_api_keys.get_enabled_api_key_accepting_info(conn, timestamp, api_key=api_key)[api_key]
            cache_api_keys.store_single_api_key(client, api_key, key_details)


def delete_api_key(conn, client, timestamp, organization_id, api_key_id):
    '''
    Delete an API key.
    :param conn: db connection
    :param client: cache client
    :param timestamp: timestamp when this request is being made.
    :param organization_id: ID of the organization the API key belongs to
    :param api_key_id: ID of the API key to delete
    :return: the actual API key
    :errors: AssertionError, DatabaseError, Redis errors
    '''
    api_key = db_api_keys.delete_api_key(conn, timestamp, organization_id, api_key_id)
    if settings.CACHE_ON:
        cache_api_keys.remove_single_api_key(client, api_key)
