import typing as t


def str_join(
    *items: t.Any,
    sep: str = ", ",
    kv_sep: str = "=",
    str_func: t.Callable[[t.Any], str] = str,
    kvs: bool = False,
) -> str:
    if kvs:
        items = tuple(f"{k!s}{kv_sep}{str_func(v)}" for k, v in items)
        str_func = str
    return sep.join(str_func(item) for item in items)


def clsqualname(obj: object) -> str:
    if isinstance(obj, type):
        return obj.__qualname__
    return obj.__class__.__qualname__


def to_repr(
    obj: object,
    *,
    args: str | t.Iterable | None = None,
    kwargs: str | t.Mapping | None = None,
) -> str:
    params = []
    if args:
        if not isinstance(args, str):
            args = str_join(*args, str_func=repr)
        params.append(args)
    if kwargs:
        if not isinstance(kwargs, str):
            kwargs = str_join(*kwargs.items(), str_func=repr, kvs=True)
        params.append(kwargs)
    return f"{clsqualname(obj)}({str_join(*params)})"


def camel_to_snake(string: str) -> str:
    out = []
    for i, ch in enumerate(string):
        if ch.isupper() and i:
            out.append("_")
        out.append(ch.lower())
    return "".join(out)
