# By: Riasat Ullah
# This file contains queries to handle all types of permission storage in cache.

from utils import cache_names, helpers, var_names
import json


def store_organization_permissions(client, org_perms):
    '''
    Store organization permissions in cache.
    :param client: cache client
    :param org_perms: (dict) -> {org_id: perm, ...}
    '''
    assert isinstance(org_perms, dict) and len(org_perms) > 0
    client.hmset(cache_names.organization_permissions, org_perms)


def store_single_organization_permissions(client, org_id, org_perm):
    '''
    Sets the permissions of a single organization in cache.
    :param client: cache client
    :param org_id: organization ID
    :param org_perm: permission of the organization
    '''
    assert isinstance(org_id, int)
    client.hset(cache_names.organization_permissions, org_id, org_perm)


def get_organization_permissions(client, organization_ids):
    '''
    Get the permissions of organizations given their organization IDs.
    :param client: cache client
    :param organization_ids: (list) of organization IDs
    :return: (dict) -> {org ID: perm, ...}
    '''
    org_ids = helpers.get_int_list(organization_ids)
    perms = client.hmget(cache_names.organization_permissions, organization_ids)
    data = dict()
    if perms is not None:
        for i in range(0, len(org_ids)):
            if perms[i] is not None:
                data[org_ids[i]] = perms[i]
    return data


def get_single_organization_permission(client, organization_id):
    '''
    Get the permissions of organizations given their organization IDs.
    :param client: cache client
    :param organization_id: ID of the organization
    :return: (str) -> perm
    '''
    perm = client.hget(cache_names.organization_permissions, organization_id)
    if perm is not None:
        return perm
    return None


def remove_single_organization_permission(client, org_id):
    '''
    Removes the stored permission of a single organization.
    :param client: cache client
    :param org_id: ID of the organization
    '''
    client.hdel(cache_names.organization_permissions, org_id)


def remove_all_organization_permissions(client):
    '''
    Removes the cache for organization permissions.
    :param client: cache client
    '''
    client.delete(cache_names.organization_permissions)


def store_organization_month_costs(client, org_costs):
    '''
    Stores the percentage of cost incurred by organizations in the current month in cache.
    :param client: cache client
    :param org_costs: (dict) -> {org ID: cost percentage, ...}
    '''
    assert isinstance(org_costs, dict) and len(org_costs) > 0
    client.hmset(cache_names.organization_month_costs, org_costs)


def get_organization_month_costs(client, org_ids: list):
    '''
    Get the percentage of total costs incurred by an organization in the current month.
    :param client: cache client
    :param org_ids: (list) of organization IDs
    :return: (dict) -> {org ID: cost percentage, ...}
    '''
    costs = client.hmget(cache_names.organization_month_costs, org_ids)
    data = dict()
    if costs is not None:
        for i in range(0, len(org_ids)):
            if costs[i] is not None:
                data[org_ids[i]] = float(costs[i])
    return data


def remove_organization_month_costs(client, org_ids: list):
    '''
    Removes the current month costs of certain organizations.
    :param client: cache client
    :param org_ids: (list) of organization IDs
    '''
    if len(org_ids) > 0:
        client.hdel(cache_names.organization_month_costs, *org_ids)


def remove_all_organization_month_costs(client):
    '''
    Deletes the organization month costs cache.
    :param client: cache client
    '''
    client.delete(cache_names.organization_month_costs)


def get_organization_ids_from_cached_month_costs(client):
    '''
    Get the list of organization IDs whose month costs are stored in cache.
    :param client: cache client
    :return: (list) of organization IDs
    '''
    org_ids = client.hkeys(cache_names.organization_month_costs)
    return [int(x) for x in org_ids] if org_ids is not None else []


def store_single_blacklist_event(client, org_id, timestamp, reason, inst_id):
    '''
    Store a potential malicious action details of an organization in cache.
    :param client: cache client
    :param org_id: ID of the organization triggering the malicious actions
    :param timestamp: timestamp when this action was detected
    :param reason: reason for blacklisting the action
    :param inst_id: ID of the instance the blacklisting event is for
    :errors: AssertionError
    '''
    new_item = {var_names.timestamp: timestamp, var_names.reason: reason, var_names.instance_id: inst_id}
    stored_items = client.hget(cache_names.potential_blacklist, org_id)
    stored_items = [] if stored_items is None else json.loads(stored_items)
    stored_items.append(new_item)

    client.hset(cache_names.potential_blacklist, org_id,
                json.dumps(stored_items, default=helpers.jsonify_unserializable))


def store_all_blacklisted_events(client, org_blacklists):
    '''
    Store blacklisted events of all organizations in cache.
    :param client: cache client
    :param org_blacklists: (dict) -> {org_id: [{timestamp: ..., reason: ..., instance_id: ...}, ...], ...}
    '''
    assert isinstance(org_blacklists, dict) and len(org_blacklists) > 0
    for key in org_blacklists:
        org_blacklists[key] = json.dumps(org_blacklists[key], default=helpers.jsonify_unserializable)
    client.hmset(cache_names.potential_blacklist, org_blacklists)


def get_organization_blacklisted_events(client, org_id):
    '''
    Gets the blacklisted events of an organization.
    :param client: cache client
    :param org_id: ID of the organization
    :return: (list of dict) -> [{timestamp: ..., reason: ...}, ...]
    '''
    org_blacklist_items = client.hget(cache_names.potential_blacklist, org_id)
    if org_blacklist_items is None:
        return []
    else:
        return json.loads(org_blacklist_items)


def remove_all_blacklisted_events(client):
    '''
    Removes the potential_blacklists cache.
    :param client: cache client
    '''
    client.delete(cache_names.potential_blacklist)


def remove_single_blacklisted_organization(client, org_id):
    '''
    Removes a single organization from the blacklist.
    :param client: cache client
    :param org_id: ID of the organization
    '''
    client.hdel(cache_names.potential_blacklist, org_id)
