# By: Riasat Ullah
# This file contains queries for handling policies in cache.

from objects.policy import Policy
from objects.routine import Routine
from utils import cache_names, helpers, times, var_names
import json
import uuid


def store_policies(client, policies):
    '''
    Store policies in cache.
    :param client: cache client
    :param policies: (list) Policy objects
    '''
    assert isinstance(policies, list) and len(policies) > 0
    pol_data = dict()
    ref_data = dict()

    for pol in policies:
        assert isinstance(pol, Policy)
        pol_data[pol.policy_id] = json.dumps(pol.to_dict(), default=helpers.jsonify_unserializable)
        ref_data[str(pol.reference_id)] = pol.policy_id
    client.hmset(cache_names.policies, pol_data)
    client.hmset(cache_names.policies_ref_map, ref_data)


def store_single_policy(client, policy_obj):
    '''
    Store policies in cache.
    :param client: cache client
    :param policy_obj: Policy object
    '''
    assert isinstance(policy_obj, Policy)
    client.hset(cache_names.policies, policy_obj.policy_id, json.dumps(policy_obj.to_dict(),
                                                                       default=helpers.jsonify_unserializable))
    client.hset(cache_names.policies_ref_map, str(policy_obj.reference_id), policy_obj.policy_id)


def get_policies(client, policy_ids):
    '''
    Get Policy objects from cache given their policy IDs.
    :param client: cache client
    :param policy_ids: (list) of policy IDs
    :return: (dict) -> {policy ID: Policy, ...}
    '''
    policy_ids = helpers.get_int_list(policy_ids)
    pol_json = client.hmget(cache_names.policies, policy_ids)
    data = dict()
    if pol_json is not None:
        for item in pol_json:
            if item is not None:
                pol_obj = Policy.create_policy(json.loads(item))
                data[pol_obj.policy_id] = pol_obj
    return data


def get_policy_ref_maps(client, policy_ref_ids):
    '''
    Gets policy reference maps.
    :param client: cache client
    :param policy_ref_ids: (list) of policy ref ids
    :return: (dict) -> {policy ref ID: policy ID, ...}
    '''
    policy_ref_ids = [str(x) for x in policy_ref_ids]
    pol_ids = client.hmget(cache_names.policies_ref_map, policy_ref_ids)
    data = dict()
    if pol_ids is not None:
        for i in range(0, len(policy_ref_ids)):
            if pol_ids[i] is not None:
                data[uuid.UUID(policy_ref_ids[i])] = pol_ids[i]
    return data


def get_all_policies(client):
    '''
    Gets all the Policy(s) that are stored in the cache.
    :param client: cache client
    :return: (dict) -> {policy ID: Policy, ...}
    '''
    pol_json = client.hgetall(cache_names.policies)
    data = dict()
    if pol_json is not None:
        for id_ in pol_json:
            pol_obj = Policy.create_policy(json.loads(pol_json[id_]))
            data[id_] = pol_obj
    return data


def get_all_policies_ref_map(client):
    '''
    Gets all policy reference maps that are stored in cache.
    :param client: cache client
    :return: (dict) -> {ref ID: policy ID, ...}
    '''
    ref_map = client.hgetall(cache_names.policies_ref_map)
    new_ref_map = dict()
    if ref_map is not None:
        for ref_id in ref_map:
            new_ref_map[uuid.UUID(ref_id)] = int(ref_map[ref_id])
    return new_ref_map


def remove_policies(client, policy_ids, remove_refs=True):
    '''
    Removes policies from cache given their policy IDs.
    :param client: cache client
    :param policy_ids: (list) IDs of the policies to remove
    :param remove_refs: (boolean) True if reference map should also be removed
    '''
    policy_ids = helpers.get_int_list(policy_ids)
    if len(policy_ids) > 0:
        client.hdel(cache_names.policies, *policy_ids)

        if remove_refs:
            cached_refs = get_all_policies_ref_map(client)
            refs_to_remove = []
            for ref_id in cached_refs:
                if cached_refs[ref_id] in policy_ids:
                    refs_to_remove.append(ref_id)
            if len(refs_to_remove) > 0:
                refs_to_remove = [str(x) for x in refs_to_remove]
                remove_policies_ref_map(client, refs_to_remove)


def remove_policies_ref_map(client, policy_ref_ids):
    '''
    Removes policy reference ID maps from the cache.
    :param client: cache client
    :param policy_ref_ids: (list of str) of policy reference IDs
    '''
    if len(policy_ref_ids) > 0:
        client.hdel(cache_names.policies_ref_map, *policy_ref_ids)


def remove_all_policies(client):
    '''
    Removes all policies and their reference maps from cache.
    :param client: cache client
    '''
    client.delete(cache_names.policies)
    client.delete(cache_names.policies_ref_map)


def is_policy_in_cache(client, policy_id):
    '''
    Checks if a certain policy is in cache or not.
    :param client: cache client
    :param policy_id: the policy ID to check for
    :return: (boolean) True if it is in cache; False otherwise
    '''
    assert isinstance(policy_id, int)
    return client.hexists(cache_names.policies, policy_id)


def get_cached_policy_ids(client):
    '''
    Get the list of IDs of the policies that have been cached.
    :param client: cache client
    :return: (list) of policy IDs
    '''
    pol_ids = client.hkeys(cache_names.policies)
    return [int(x) for x in pol_ids] if pol_ids is not None else []


def store_hand_off_on_call_roles(client, on_call_roles):
    '''
    Store the on-call hand off roles.
    :param client: cache client
    :param on_call_roles: (list of dict) of hand off roles
    '''
    assert isinstance(on_call_roles, list) and len(on_call_roles) > 0
    data = []
    for item in on_call_roles:
        new_item = dict()
        for key in item:
            if key == var_names.routines:
                new_item[key] = item[var_names.routines].to_dict()
            else:
                new_item[key] = item[key]
        data.append(new_item)
    if len(data) > 0:
        client.set(cache_names.policies_hand_off_roles, json.dumps(data, default=helpers.jsonify_unserializable))


def get_hand_off_on_call_roles(client):
    '''
    Get the list of hand off roles.
    :param client: cache client
    :return: (list of dict) of hand off roles
    '''
    json_data = client.get(cache_names.policies_hand_off_roles)
    data = []
    if json_data is not None:
        data = json.loads(json_data)
        for item in data:
            item[var_names.routines] = Routine.create_routine(item[var_names.routines])
            item[var_names.handoff_timestamp] = times.get_timestamp_from_string(item[var_names.handoff_timestamp])
    return data


def remove_all_hand_off_on_call_roles(client):
    '''
    Removes all hand off on call roles from the cache.
    :param client: cache client
    '''
    client.delete(cache_names.policies_hand_off_roles)
