from six import iteritems

from collections import defaultdict


def nested_lookup(key, document, wild=False, with_keys=False):
    """Lookup a key in a nested document, return a list of values"""
    if with_keys:
        d = defaultdict(list)
        for k, v in _nested_lookup(
            key, document, wild=wild, with_keys=with_keys
        ):
            d[k].append(v)
        return d
    return list(_nested_lookup(key, document, wild=wild, with_keys=with_keys))


def _nested_lookup(key, document, wild=False, with_keys=False):
    """Lookup a key in a nested document, yield a value"""
    if isinstance(document, list):
        for d in document:
            for result in _nested_lookup(
                key, d, wild=wild, with_keys=with_keys
            ):
                yield result

    if isinstance(document, dict):
        for k, v in iteritems(document):
            if key == k or (wild and key.lower() in k.lower()):
                if with_keys:
                    yield k, v
                else:
                    yield v
            if isinstance(v, dict):
                for result in _nested_lookup(
                    key, v, wild=wild, with_keys=with_keys
                ):
                    yield result
            elif isinstance(v, list):
                for d in v:
                    for result in _nested_lookup(
                        key, d, wild=wild, with_keys=with_keys
                    ):
                        yield result


def get_all_keys(dictionary):
    """
        Method to get all keys from a nested dictionary as a List
        Args:
            dictionary: Nested dictionary
        Returns:
            List of keys in the dictionary
    """
    result_list = []

    def recrusion(dictionary):
        for key, value in iteritems(dictionary):
            if isinstance(value, dict):
                result_list.append(key)
                recrusion(dictionary=value)
            elif isinstance(value, list):
                result_list.append(key)
                for list_items in value:
                    # Make sure the items inside the list is iterable
                    if hasattr(list_items, 'items'):
                        recrusion(dictionary=list_items)
            else:
                result_list.append(key)

    recrusion(dictionary=dictionary)
    return result_list


def get_occurrence_of_key(dictionary, key):
    """
    Method to get occurrence of a key in a nested dictionary

    Args:
        dictionary: Nested dictionary
        key: Key to search for the occurrences
    Return:
        Number of occurrence (Integer)
    """
    return _get_occurrence(dictionary=dictionary, item='key', keyword=key)


def get_occurrence_of_value(dictionary, value):
    """
    Method to get occurrence of a value in a nested dictionary

    Args:
        dictionary: Nested dictionary
        value: Value to search for the occurrences
    Return:
        Number of occurrence (Integer)
    """
    return _get_occurrence(dictionary=dictionary, item='value', keyword=value)


def _get_occurrence(dictionary, item, keyword):
    """
    Method to get occurrence of a key or value in a nested dictionary

    Args:
        dictionary: Nested dictionary
        item: Mostly (key or value)
        keyword: key word to find occurrence
    Return:
        Number of occurrence of the given keyword in the dict
    """
    occurrence = [0]

    def recrusion(dictionary):
        if item == 'key':
            occurrence[0] += 1 if dictionary.get(keyword) else 0
        elif keyword in dictionary.values():
            occurrence[0] += dictionary.values().count(keyword)
        for key, value in iteritems(dictionary):
            if isinstance(value, dict):
                recrusion(dictionary=value)
            elif isinstance(value, list):
                for list_items in value:
                    if hasattr(list_items, 'items'):
                        recrusion(dictionary=list_items)
                    elif list_items == keyword:
                        occurrence[0] += 1 if item == 'value' else 0

    recrusion(dictionary=dictionary)
    return occurrence[0]
