import fred.src.utils as utils
import fred.src.web_interface.yaml_to_wi_object as yto
import fred.src.web_interface.factors_and_conditions as fac_cond
import fred.src.web_interface.wi_utils as wi_utils
import copy

disabled_fields = []

# TODO: code refactoring + documentation


def edit_wi_object(path, key_yaml, read_in_whitelists):
    """
    This function fills an empty wi object with the information of a metadata
    file
    :param key_yaml:
    :param path: the path containing the metadata file
    :return: wi_object: the filled wi object
    """
    # TODO: as Parameter at get_info
    meta_yaml = utils.read_in_yaml(path)
    whitelist_object = {}

    if meta_yaml is not None:
        if "path" in meta_yaml:
            meta_yaml.pop("path")
        empty_object = yto.get_empty_wi_object(key_yaml, read_in_whitelists)
        wi_object = {}
        wi_object["all_factors"], real_val = get_all_factors(meta_yaml, key_yaml)
        wi_object["publication_records"] = get_publication_records(meta_yaml)
        for part in empty_object:
            if part not in ["all_factors", "publication_records"]:
                wi_object[part], whitelist_object = new_fill(
                    meta_yaml[part],
                    empty_object[part],
                    key_yaml[part],
                    whitelist_object,
                    real_val,
                    read_in_whitelists,
                )

    else:
        wi_object = yto.get_empty_wi_object(key_yaml, read_in_whitelists)

    wi_object["whitelists"] = whitelist_object
    wi_object = get_filenames(wi_object, meta_yaml)
    return wi_object


def get_publication_records(meta_yaml):
    publications = list(utils.find_keys(meta_yaml, "publication"))
    publication_records = {}
    for elem in publications:
        for pub in elem:
            if pub["pubmed_id"] not in publication_records:
                publication_records[pub["pubmed_id"]] = pub
    return publication_records


def get_filenames(wi_object, meta_yaml):
    old_filenames = {}
    old_sample_names = {}
    settings = list(utils.find_keys(meta_yaml, "experimental_setting"))
    if len(settings) > 0:
        for setting in settings:
            for stg in setting:
                if "setting_id" in stg:
                    setting_id = stg["setting_id"]
                    bio_reps = list(utils.find_keys(stg, "samples"))
                    if len(bio_reps) > 0:
                        for elem in bio_reps:
                            for sample in elem:
                                if "sample_name" in sample:
                                    key = f'{setting_id}_{sample["sample_name"]}'
                                    tech_rep = list(
                                        utils.find_keys(sample, "technical_replicates")
                                    )
                                    if len(tech_rep) > 0:
                                        if "filenames" in tech_rep[0]:
                                            old_filenames[key] = tech_rep[0][
                                                "filenames"
                                            ]
                                        if "sample_name" in tech_rep[0]:
                                            old_sample_names[key] = tech_rep[0][
                                                "sample_name"
                                            ]
    wi_object["old_sample_names"] = old_sample_names
    wi_object["old_filenames"] = old_filenames
    return wi_object


def new_fill(
    meta_yaml, wi_object, key_yaml, whitelist_object, real_val, read_in_whitelists
):

    if isinstance(meta_yaml, dict):

        if "headers" in wi_object:
            fill_key = "value"
            filled_value = ""
            for header in wi_object["headers"].split(" "):
                filled_value = filled_value + " " + meta_yaml[header]
            filled_value = filled_value.lstrip(" ").rstrip(" ")
        else:
            if wi_object["position"].split(":")[-1] == "experimental_setting":
                fill_key = "input_fields"
                filled_value, whitelist_object = fill_experimental_setting(
                    wi_object,
                    meta_yaml,
                    key_yaml["value"],
                    whitelist_object,
                    real_val,
                    read_in_whitelists,
                )
            else:
                fill_key = "input_fields"
                filled_value = copy.deepcopy(wi_object["input_fields"])
                for i in range(len(filled_value)):
                    if filled_value[i]["position"].split(":")[-1] in meta_yaml:
                        filled_value[i], whitelist_object = new_fill(
                            meta_yaml[filled_value[i]["position"].split(":")[-1]],
                            filled_value[i],
                            key_yaml["value"][
                                filled_value[i]["position"].split(":")[-1]
                            ],
                            whitelist_object,
                            real_val,
                            read_in_whitelists,
                        )

    elif isinstance(meta_yaml, list):
        fill_key = "list_value"
        filled_value = []
        for i in range(len(meta_yaml)):
            f_val, whitelist_object = new_fill(
                meta_yaml[i],
                copy.deepcopy(wi_object),
                key_yaml,
                whitelist_object,
                real_val,
                read_in_whitelists,
            )

            # TODO: WTF?
            if "input_fields" in f_val:
                f_val = f_val["input_fields"]
                if (
                    "position" in f_val
                    and f_val["position"].split(":")[-1] == "experimental_setting"
                ):
                    f_val["input_disabled"] = True
            else:
                f_val = f_val["value"]
            filled_value.append(f_val)

    else:
        fill_key = "value"
        if wi_object["input_type"] == "date":
            filled_value = wi_utils.str_to_date(meta_yaml)
        else:
            filled_value = meta_yaml

    if "input_type" in wi_object and wi_object["input_type"] == "single_autofill":
        fill_key = "list_value"

    wi_object[fill_key] = filled_value

    if "special_case" in key_yaml and "edit" in key_yaml["special_case"]:
        if key_yaml["special_case"]["edit"] == "not editable":
            wi_object["input_disabled"] = True
            wi_object["delete_disabled"] = True
        elif key_yaml["special_case"]["edit"] == "not removable":
            wi_object["delete_disabled"] = True
            if key_yaml["list"]:
                wi_object["fixed_length"] = (
                    len(filled_value) if filled_value is not None else None
                )
                if "input_type" in wi_object and wi_object["input_type"] in [
                    "select",
                    "group_select",
                ]:
                    wi_object["non_editable_val"] = filled_value
    return wi_object, whitelist_object


def fill_experimental_setting(
    wi_object, meta_yaml, key_yaml, whitelist_object, real_val, read_in_whitelists
):
    organism = ""
    filled_object = []
    for j in range(len(wi_object["input_fields"])):
        f = copy.deepcopy(wi_object["input_fields"][j])
        for key in meta_yaml:

            if wi_object["input_fields"][j]["position"].split(":")[-1] == key:
                if key == "experimental_factors":
                    pass
                elif key == "conditions":
                    sample_keys = list(utils.find_keys(key_yaml, "samples"))
                    if len(sample_keys) > 0:

                        sample, whitelists = yto.parse_empty(
                            sample_keys[0],
                            "experimental_setting:conditions:biological_"
                            "replicates:samples",
                            key_yaml,
                            {"organism": organism},
                            read_in_whitelists,
                            get_whitelist_object=True,
                        )
                        sample = sample["input_fields"]

                        conditions = []

                        for cond in meta_yaml[key]:
                            samples = []
                            split_cond = utils.split_cond(cond["condition_name"])
                            sample_name = utils.get_short_name(
                                cond["condition_name"],
                                {},
                                key_yaml,
                                read_in_whitelists=read_in_whitelists,
                            )
                            input_fields = fac_cond.get_samples(
                                split_cond,
                                copy.deepcopy(sample),
                                real_val,
                                key_yaml,
                                sample_name,
                                organism,
                                read_in_whitelists,
                            )
                            if "samples" in cond["biological_replicates"]:
                                for s in cond["biological_replicates"]["samples"]:
                                    filled_keys = []
                                    for k in s:
                                        if s[k] is not None:
                                            # TODO: dict (disease usw.)
                                            # TODO: real_val
                                            if isinstance(s[k], list):
                                                for elem in s[k]:
                                                    if (
                                                        k,
                                                        elem,
                                                    ) not in filled_keys and (
                                                        k,
                                                        elem,
                                                    ) not in split_cond:
                                                        filled_keys.append((k, elem))
                                            elif (k, s[k]) not in split_cond:
                                                filled_keys.append((k, s[k]))
                                    sample_index = int(
                                        s["sample_name"].split("_")[-1].replace("b", "")
                                    )
                                    cond_sample_name = (
                                        f"{sample_name}_" f"{sample_index}"
                                    )
                                    filled_sample = copy.deepcopy(input_fields)
                                    filled_sample = fac_cond.get_samples(
                                        filled_keys,
                                        filled_sample,
                                        real_val,
                                        key_yaml,
                                        cond_sample_name,
                                        organism,
                                        read_in_whitelists,
                                        is_factor=False,
                                    )
                                    samples.append(copy.deepcopy(filled_sample))
                            title, readd = fac_cond.get_condition_title(split_cond)
                            d = {
                                "correct_value": cond["condition_name"],
                                "title": title,
                                "readd": readd,
                                "input_disabled": False,
                                "position": "experimental_setting:condition",
                                "list": True,
                                "mandatory": True,
                                "list_value": samples,
                                "desc": "",
                                "input_fields": input_fields,
                            }
                            if (
                                "special_case" in sample_keys[0]
                                and "edit" in sample_keys[0]["special_case"]
                            ):
                                if (
                                    sample_keys[0]["special_case"]["edit"]
                                    == "not editable"
                                ):
                                    d["input_disabled"] = True
                                    d["delete_disabled"] = True
                                elif (
                                    sample_keys[0]["special_case"]["edit"]
                                    == "not removable"
                                ):
                                    d["delete_disabled"] = True
                                    if sample_keys[0]["list"]:
                                        d["fixed_length"] = (
                                            len(samples)
                                            if samples is not None
                                            else None
                                        )
                            conditions.append(d)
                        # TODO: not editable and not removable for conditions?
                        f["list_value"] = conditions
                        whitelist_object[organism] = whitelists

                else:

                    if "headers" in f and isinstance(meta_yaml[key], dict):
                        new_val = parse_headers(meta_yaml[key], f["headers"])
                    else:
                        new_val = meta_yaml[key]

                    if key == "organism":
                        organism = new_val

                    if "whitelist_keys" in f:
                        new_val = parse_whitelist_keys(
                            meta_yaml[key],
                            f["whitelist_keys"],
                            utils.get_whitelist(
                                key,
                                {"organism": organism},
                                whitelist_object=read_in_whitelists,
                            ),
                        )

                    if "list" in f and f["list"]:
                        f["list_value"] = new_val
                    else:
                        f["value"] = new_val
                filled_object.append(f)
    return filled_object, whitelist_object


def parse_headers(value, headers):

    if isinstance(headers, dict):
        header = None
        for k in headers:
            if sorted(headers[k].split(" ")) == sorted(list(value.keys())):
                header = headers[k].split(" ")
                break
    else:
        header = headers.split(" ")

    if header is not None:
        val = ""
        for h in header:
            val = f'{val}{" " if val != "" else ""}{value[h]}'
        value = val

    return value


def parse_whitelist_keys(value, whitelist_keys, whitelist):

    for key in whitelist_keys:
        if f"{value} ({key})" in whitelist:
            value = f"{value} ({key})"
            break

    return value


# TODO: value unit?
def get_all_factors(meta_yaml, real_val):
    """
    This function creates an object containing all experimental factors from a
    metadata yaml to be stored in a wi object
    :param real_val:
    :param meta_yaml: the read in metadata file
    :return: all_factors: an object containing all experimental factors
    """
    all_factors = []
    for setting in meta_yaml["experimental_setting"]:
        setting_factors = []
        for factors in setting["experimental_factors"]:
            setting_fac = {"factor": factors["factor"]}
            w = utils.get_whitelist(factors["factor"], setting)
            setting_fac["values"] = []
            for elem in factors["values"]:
                value = elem
                if w and "headers" in w:
                    if "headers" not in setting_fac:
                        setting_fac["headers"] = w["headers"]
                    if isinstance(elem, dict):
                        value = parse_headers(value, w["headers"])

                if w and "whitelist_keys" in w:
                    if "whitelist_keys" not in setting_fac:
                        setting_fac["whitelist_keys"] = w["whitelist_keys"]
                    if "whitelist_type" in w and w["whitelist_type"] == "plain_group":
                        value = parse_whitelist_keys(
                            value, w["whitelist_keys"], w["whitelist"]
                        )

                if (
                    isinstance(elem, dict)
                    and len(list(elem.keys())) == 2
                    and "unit" in elem
                    and "value" in elem
                ):
                    value = f'{elem["value"]}{elem["unit"]}'

                if value != elem:
                    if isinstance(elem, dict):
                        # rewrite the value into a string
                        val = "|".join([f'{key}:"{elem[key]}"' for key in elem])
                        val = f'{factors["factor"]}:{"{"}{val}{"}"}'
                        real_val[val] = value
                    else:
                        real_val[elem] = value
                # TODO: nach oben rücken + rekursiv (eigene Funktion)
                elif isinstance(factors["values"], dict):
                    value = {value: factors["values"][value]}
                setting_fac["values"].append(value)
            setting_factors.append(setting_fac)
        all_factors.append(setting_factors)
    return all_factors, real_val
