import uuid
from . import config

valid_types = ['counter', 'text', 'boolean', 'decimal', 'double', 'int', 'list', 'set', 'map', 'bigint', 'blob',
               'tuple', 'dict', 'float', 'timestamp', 'time', 'date', 'numpy.ndarray']

basic_types = valid_types[:-1]


def storage_id_from_name(name):
    return uuid.uuid3(uuid.NAMESPACE_DNS, name)


def process_path(module_path):
    """
    Method to obtain module and class_name from a module path
    Args:
        module_path(String): path in the format module.class_name
    Returns:
        tuple containing class_name and module
    """

    if module_path == 'numpy.ndarray':
        return 'StorageNumpy', 'hecuba.hnumpy'
    if module_path == 'StorageDict':
        return 'StorageDict', 'hecuba.hdict'
    last = 0
    for key, i in enumerate(module_path):
        if i == '.' and key > last:
            last = key
    module = module_path[:last]
    class_name = module_path[last + 1:]
    return class_name, module


"""
 Cassandra related methods
"""

_size_estimates = config.session.prepare(("SELECT mean_partition_size, partitions_count "
                                          "FROM system.size_estimates WHERE keyspace_name=? and table_name=?"))
_max_token = int(((2 ** 63) - 1))  # type: int
_min_token = int(-2 ** 63)  # type: int

_select_istorage_meta = config.session.prepare("SELECT * FROM hecuba.istorage WHERE storage_id = ?")


def extract_ks_tab(name):
    """
    Method used to obtain keyspace and table from a given name
    Args:
        name: a string containing keyspace name and table name, or only table name
    Returns:
        a tuple containing keyspace name and table name
    """
    if not name:
        return None, None

    sp = name.split(".")
    if len(sp) == 2:
        ksp = sp[0]
        table = sp[1]
    else:
        ksp = config.execution_name
        table = name
    return ksp.lower(), table.lower()


def tokens_partitions(ksp, table, tokens_ranges):
    """
    Method that calculates the new token partitions for a given object
    Args:
        tokens: current number of tokens of the object
        min_tokens_per_worker: defined minimum number of tokens
        number_of_workers: defined
    Returns:
        a partition every time it's called
        :type tokens_ranges: list[(long,long)]
    """
    from collections import defaultdict
    from bisect import bisect_right
    from cassandra.metadata import Murmur3Token

    splits_per_node = config.splits_per_node
    token_range_size = config.token_range_size
    target_token_range_size = config.target_token_range_size

    tm = config.cluster.metadata.token_map
    tmap = tm.tokens_to_hosts_by_ks.get(ksp, None)

    tokens_murmur3 = map(lambda a: (Murmur3Token(a[0]), a[1]), tokens_ranges)
    if not tmap:
        tm.rebuild_keyspace(ksp, build_if_absent=True)
        tmap = tm.tokens_to_hosts_by_ks[ksp]

    tokens_per_node = defaultdict(list)
    for tmumur, t_to in tokens_murmur3:
        point = bisect_right(tm.ring, tmumur)
        if point == len(tm.ring):
            tokens_per_node[tmap[tm.ring[0]][0]].append((tmumur.value, t_to))
        else:
            tokens_per_node[tmap[tm.ring[point]][0]].append((tmumur.value, t_to))

    n_nodes = len(tokens_per_node)
    step_size = _max_token // (splits_per_node * n_nodes)
    if token_range_size:
        step_size = token_range_size
    elif target_token_range_size:
        one = config.session.execute(_size_estimates, [ksp, table]).one()
        if one:
            (mean_p_size, p_count) = one
            estimated_size = mean_p_size * p_count
            if estimated_size > 0:
                step_size = _max_token // int(
                    max(estimated_size / target_token_range_size,
                        splits_per_node * n_nodes)
                )

    for tokens_in_node in tokens_per_node.values():
        partition = []
        for fraction, to in tokens_in_node: