from typing import Dict, List

from pkg_resources import resource_string

from synthetic_agents.common.constants import PROJECT_DIR_NAME


def safe_placeholder_mapping(template: str, placeholder_values: Dict[str, str]) -> str:
    """
    Replaces placeholders in a template with values from a dictionary preserving placeholders for
    keys not present in the values dictionary.

    For instance, suppose we have the template and placeholder_values below:

    template = "My name is {name} and I am a {age} years old {nationality}."
    placeholder_values = {"name": Alex, "age": 20}

    This function will return the following:

    "My name is Alex and I am a 20 years old {nationality}."

    :param template: template text with placeholders surrounded by curly braces.
    :param placeholder_values: a dictionary of placeholder keys and values.
    :return: template with filled placeholders for keys present in the dictionary of values.
    """

    class SafeDict(dict):
        def __missing__(self, key):
            """
            If a key is missing, we preserve the placeholder representation.

            :param key: placeholder key.
            :return: placeholder representation.
            """
            return "{" + key + "}"

    return template.format_map(SafeDict(**placeholder_values))


def load_asset_file(asset_path: str) -> str:
    """
    Reads the content of a file in the project's asset folder (synthetic_agents/asset). It assumes
    the file contains textual data, and it is not binary.

    :param asset_path: path to the asset file under synthetic_agents/asset. For instance,
        demographics/genders.txt.
    :raise ValueError: if there was an error when reading the asset.
    :return: the textual content of the file.
    """

    source = f"asset/{asset_path}"
    try:
        return resource_string(PROJECT_DIR_NAME, source).decode("utf-8")
    except Exception as ex:
        raise ValueError(f"Error trying to read the asset ({source}). {ex}")


def get_unique_nonempty_items_from_asset(asset_path: str) -> List[str]:
    """
    Returns the content of an asset file as a list of unique, non-empty items.

    :param asset_path: path to the asset file under synthetic_agents/asset. For instance,
        demographics/genders.txt.
    :return: list of unique, non-empty textual items.
    """
    items = load_asset_file(asset_path).split("\n")

    # Remove blank items and comments (lines starting with #)
    items = [item for item in items if item != "" and item[0] != "#"]

    # Remove duplicates
    return list(set(items))
