"""
Utilities to operate Snakemake workflows
"""

import re
import pprint
import typing
from collections.abc import Callable
from logging import Logger


def update_scope(target: dict, default: dict):
    """
    Recursive function to update a sub dictionnary
    """
    mod = {}
    for key in default.keys():
        if key not in target:
            target[key] = default[key]
            mod[key] = default[key]
        elif isinstance(default[key], dict):
            changes = update_scope(target[key], default[key])
            if len(changes) != 0:
                mod[key] = changes
        else:
            continue
    return mod


def set_default_config(
    default: dict,
    config: dict,
    scope: str = "<not specified>",
    logger: typing.Optional[Logger] = None,
):
    """
    Function taking a dictionnary representing the default
    configuration and updating the missing config with the default
    values
    """
    updated = update_scope(config, default)

    if logger and updated:
        logger.warning(
            f"Missing config values added in {scope}. Run with --verbose to see which"
        )
        logger.debug("Config key:values updated:")
        logger.debug(pprint.pformat(updated, width=1, indent=2))

    return updated


def parse_runs_list(runslist_file):
    """
    Parse runs list file

    :parameter: - ``runslist_file`` containing detid:run pairs, space separated, 8 digits numbers

    :return: - ``dict`` with detid:runs pairs
    """

    runs = {}
    with open(runslist_file) as fin:
        for line in fin:
            match = re.match(r"(?P<detid>\d{8})\s+(?P<run>\d{8}).*", line)
            if match is None:
                continue
            detid = match.group("detid")
            run = match.group("run")

            if detid not in runs:
                runs[detid] = []

            runs[detid].append(run)

    return runs
