from typing import List, Union, Any, Dict, get_origin, Optional, TypedDict


def set_dict_path_value(dict_value: dict, path: Union[str, List[str]], value: Any):
    used_path = path
    if not isinstance(used_path, list):
        used_path = [used_path]

    pivot = dict_value
    last_index = len(used_path) - 1
    for i, p in enumerate(used_path):
        if i == last_index:
            pivot[p] = value
        else:
            if pivot.get(p) is None:
                pivot[p] = {}
            pivot = pivot[p]


def get_dict_path_value(
    dict_value: dict,
    path: Union[str, List[str]],
    default_value: Any = None
) -> Any:
    used_path = path
    if not isinstance(used_path, list):
        used_path = [used_path]
    result = default_value
    current_value = dict_value

    for field in used_path:
        if current_value is not None and field in current_value:
            current_value = current_value[field]
            result = current_value
        else:
            result = default_value
            break

    if result is None:
        return default_value
    return result


def build_ref(path: Union[str, List[str]], name: str) -> str:
    used_path = path
    if not isinstance(used_path, list):
        used_path = [used_path]
    return '/'.join([
        '#',
        *used_path,
        name
    ])


def _get_alias_origin(type_param, _default=None) -> Optional[Any]:
    return getattr(type_param, "__origin__", _default)


def is_optional(type_param):
    return (
        _get_alias_origin(type_param) == Union and
        len(getattr(type_param, "__args__") or []) == 2 and
        (
            get_origin(type_param.__args__[0]) is None
            or get_origin(type_param.__args__[1]) is None
        )
    )


def is_typing_alias(type_param):
    return _get_alias_origin(type_param) is not None


def is_list(type_param):
    return type_param == list or _get_alias_origin(type_param) in [list, List]


def is_dict(type_param):
    return type_param == dict or _get_alias_origin(type_param) in [dict, Dict]


def is_union(type_param):
    return _get_alias_origin(type_param) in [Union]


def is_type(cls):
    return isinstance(_get_alias_origin(cls, cls), type)


def get_type_params(type_param):
    return getattr(type_param, "__args__", []) or []


def get_inner_optional_type(type_param):
    type_params = get_type_params(type_param)
    if len(type_params) > 0:
        if len(type_params) == 1 or isinstance(None, type_params[1]):
            return type_params[0]
        else:
            return type_params[1]
    return None

class AliasType(TypedDict):
    origin_type: Optional[type]
    subtypes: Optional[list[type]]
    optional: bool


def get_alias_type(type_param) -> AliasType:
    if type_param is None:
        return {
            "origin_type": None,
            "subtypes": None,
            "optional": True,
        }
    origin_type = type_param
    type_params = None
    if is_typing_alias(type_param):
        if is_optional(type_param):
            result = get_alias_type(get_inner_optional_type(type_param))
            result["optional"] = True
            return result
        if is_union(type_param):
            for inner_type in get_type_params(type_param):
                result = get_alias_type(inner_type)
                if result is not None:
                    return result
            return {
                "origin_type": None,
                "subtypes": None,
                "optional": True,
            }

        origin_type = get_origin(type_param)
        type_params = get_type_params(type_param)

    sub_types = None
    if type_params is not None:
        sub_types = [get_alias_type(st) for st in type_params]
        sub_types = [sta["origin_type"] for sta in sub_types]
    return {
        "origin_type": origin_type,
        "subtypes": sub_types,
        "optional": False
    }


def to_camel_case(text):
    text = text.replace("-", "_")
    words = text.split("_")
    return words[0].lower() + ''.join(word.capitalize() for word in words[1:])
