import json
import os
import re
from copy import deepcopy
from distutils.util import strtobool
from pathlib import Path

import yaml



def build_from_file(file_path: str):
    path = Path(file_path)
    workers_path = path / "workers"
    if not workers_path.exists():
        return []

    # init config dict
    worker_configs = {}
    other_configs = {}
    file_names = {}

    for file in path.rglob("*"):
        if file.suffix not in [".json", ".yaml", ".yml"]:
            continue

        key = file.name.split(".", 1)[0]

        if key in file_names:
            raise Exception(
                f"Duplicate file name [{key}] found:\n"
                f"File 1: {file_names[key]}\n"
                f"File 2: {file}"
            )
        file_names[key] = file

        try:
            with open(file, "r") as f:
                if file.suffix == ".json":
                    content = json.load(f)
                else:  # .yaml or .yml
                    content = yaml.load(f, Loader=yaml.FullLoader)
        except Exception as e:
            raise Exception(f"Error loading file {file}: {str(e)}")

        if workers_path in file.parents:
            worker_configs[key] = content
        else:
            other_configs[key] = content

    for conf in worker_configs.values():
        prep_config(conf, other_configs, [])

    worker_configs_list = []
    for worker_config in worker_configs.values():
        if isinstance(worker_config, list):
            worker_configs_list.extend(worker_config)
        else:
            worker_configs_list.append(worker_config)

    return worker_configs_list


def prep_config(sub_config: dict | list, config: dict, forbid_keys: list):
    if isinstance(sub_config, dict):
        for key, conf in sub_config.items():
            if isinstance(conf, str):
                if match := re.search(r"\$\{sub\|([^}]+)\}", conf):
                    module_key = match.group(1).strip()
                    if module_key not in config:
                        raise Exception(
                            "Incomplete configuration, lack module [{}]".format(
                                module_key
                            )
                        )
                    elif module_key in forbid_keys:
                        raise Exception(
                            "Can't reference submodule recursively. [{}]".format(
                                module_key
                            )
                        )
                    sub_config[key] = deepcopy(config[module_key])
                    prep_config(sub_config[key], config, forbid_keys + [module_key])
                elif match := re.search(r"\$\{env\|([^,}]+)(?:,([^}]+))?\}", conf):
                    env_key = match.group(1).strip()
                    default_value = match.group(2)
                    env_value = os.getenv(env_key)
                    if env_value:
                        sub_config[key] = env_value
                    elif not env_value and default_value:
                        sub_config[key] = default_value.strip()
                        if sub_config[key] == "null" or sub_config[key] == "~":
                            sub_config[key] = None
                    else:
                        raise ValueError(
                            f"Environmental variable {env_key} need to be set."
                        )

            elif isinstance(conf, dict):
                prep_config(sub_config[key], config, forbid_keys)

            elif isinstance(conf, list):
                for i, item in enumerate(conf):
                    if isinstance(item, dict):
                        prep_config(sub_config[key][i], config, forbid_keys)
    elif isinstance(sub_config, list):
        for item in sub_config:
            prep_config(item, config, forbid_keys)