import inspect
from functools import cached_property
from typing import Any
from typing import Dict
from typing import List
from typing import Optional
from typing import Type

LEVEL_CHARS: Dict[int, str] = {0: "=", 1: "-", 2: "~", 3: "^"}


def section(name: str, level: int, as_code: bool = False) -> str:
    if as_code:
        name = f"``{name}``"
    return f"\n{name}\n{len(name) * LEVEL_CHARS[level]}\n"


def properties(obj: Type[Any]) -> List[str]:
    return sorted(prop for prop in dir(obj) if isinstance(getattr(obj, prop), property))


def cached_properties(obj: Type[Any]) -> List[str]:
    return sorted(prop for prop in dir(obj) if isinstance(getattr(obj, prop), cached_property))


def static_methods(obj: Type[Any]) -> List[str]:
    return sorted(
        name for name in dir(obj) if isinstance(inspect.getattr_static(obj, name), staticmethod)
    )


def camel_case_to_human(string: str) -> str:
    output_str = string[0]
    for char in string[1:]:
        if char.islower():
            output_str += char
        else:
            output_str += f" {char}"

    return output_str


def get_function_docs(
    function_name: str,
    obj: Any,
    level: int,
    display_function_name: Optional[str] = None,
    pre_docstring: Optional[str] = None,
) -> str:
    display_function_name = display_function_name if display_function_name else function_name

    docs = section(display_function_name, level=level)
    docs += pre_docstring or ""
    docs += inspect.cleandoc(getattr(obj, function_name).__doc__)
    docs += "\n"
    return docs


def line() -> str:
    return "\n" + ("-" * 100) + "\n"


def line_section(section_idx: int) -> str:
    if section_idx > 0:
        return line()
    return ""
