from typing import Optional


def PublicAPI(*args, **kwargs):
    """Decorator to mark a function or class as a public API.

    Args:
        stability: The stability of the API. Can be "alpha", "beta" or "stable".
            If "alpha", the API is in alpha may come breaking changes before becoming beta.
            If "beta", the API is in beta and may change before becoming stable.
            If "stable", the API will remain backwards compatible with the current major version.
            Defaults to "stable".
    Examples:
        >>> from gptdb.util.annotations import PublicAPI
        >>> @PublicAPI
        ... def foo():
        ...     pass

        >>> @PublicAPI(stability="beta")
        ... def bar():
        ...     pass

    """
    if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
        return PublicAPI(stability="stable")(args[0])
    stability = None
    if "stability" in kwargs:
        stability = kwargs["stability"]
    if not stability:
        stability = "stable"
    assert stability in ["alpha", "beta", "stable"]

    def decorator(obj):
        if stability in ["alpha", "beta"]:
            _modify_docstring(
                obj,
                f"**PublicAPI ({stability}):** This API is in {stability} and may change before becoming stable.",
            )
            _modify_annotation(obj, stability)
        return obj

    return decorator


def DeveloperAPI(*args, **kwargs):
    """Decorator to mark a function or class as a developer API.

    Developer APIs are low-level APIs for advanced users and may change cross major versions.

    Examples:
        >>> from gptdb.util.annotations import DeveloperAPI
        >>> @DeveloperAPI
        ... def foo():
        ...     pass

    """
    if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
        return DeveloperAPI()(args[0])

    def decorator(obj):
        _modify_docstring(
            obj,
            "**DeveloperAPI:** This API is for advanced users and may change cross major versions.",
        )
        return obj

    return decorator


def mutable(func):
    """Decorator to mark a method of an instance will change the instance state.

    Examples:
        >>> from gptdb.util.annotations import mutable
        >>> class Foo:
        ...     def __init__(self):
        ...         self.a = 1
        ...
        ...     @mutable
        ...     def change_a(self):
        ...         self.a = 2
        ...

    """
    _modify_mutability(func, mutability=True)
    return func


def immutable(func):
    """Decorator to mark a method of an instance will not change the instance state.

    Examples:
        >>> from gptdb.util.annotations import immutable
        >>> class Foo:
        ...     def __init__(self):
        ...         self.a = 1
        ...
        ...     @immutable
        ...     def get_a(self):
        ...         return self.a
        ...

    """
    _modify_mutability(func, mutability=False)
    return func


def _modify_docstring(obj, message: Optional[str] = None):
    if not message:
        return
    if not obj.__doc__:
        obj.__doc__ = ""
    original_doc = obj.__doc__

    lines = original_doc.splitlines()

    min_indent = float("inf")
    for line in lines[1:]:
        stripped = line.lstrip()
        if stripped:
            min_indent = min(min_indent, len(line) - len(stripped))

    if min_indent == float("inf"):
        min_indent = 0
    min_indent = int(min_indent)
    indented_message = message.rstrip() + "\n" + (" " * min_indent)
    obj.__doc__ = indented_message + original_doc


def _modify_annotation(obj, stability) -> None:
    if stability:
        obj._public_stability = stability
    if hasattr(obj, "__name__"):
        obj._annotated = obj.__name__


def _modify_mutability(obj, mutability) -> None:
    obj._mutability = mutability
