# The Lekt plugin system is licensed under the terms of the Apache 2.0 license.
__license__ = "Apache 2.0"

import typing as t
from contextlib import contextmanager


class Context:
    CURRENT: t.List[str] = []

    def __init__(self, name: str):
        self.name = name

    @contextmanager
    def enter(self) -> t.Iterator[None]:
        try:
            Context.CURRENT.append(self.name)
            yield
        finally:
            Context.CURRENT.pop()


class ContextTemplate:
    """
    Context templates are for filters for which the name needs to be formatted
    before the filter can be applied.
    """

    def __init__(self, name: str):
        self.template = name

    def __repr__(self) -> str:
        return f"{self.__class__.__name__}('{self.template}')"

    def __call__(self, *args: t.Any, **kwargs: t.Any) -> Context:
        return Context(self.template.format(*args, **kwargs))


class Contextualized:
    """
    This is a simple class to store the current context in hooks.

    The current context is stored as a static variable.
    """

    def __init__(self) -> None:
        self.contexts = Context.CURRENT[:]

    def is_in_context(self, context: t.Optional[str]) -> bool:
        return context is None or context in self.contexts


def enter(name: str) -> t.ContextManager[None]:
    """
    Identify created hooks with one or multiple context strings.

    :param name: name of the context that will be attached to hooks.
    :rtype t.ContextManager[None]:

    Usage::

        from lekt import hooks

        with hooks.contexts.enter("my-context"):
            # declare new actions and filters
            ...

        # Later on, actions and filters can be disabled with:
        hooks.actions.clear_all(context="my-context")
        hooks.filters.clear_all(context="my-context")

    This is a context manager that will attach a context name to all hook callbacks
    created within its scope. The purpose of contexts is to solve an issue that
    is inherent to pluggable hooks: it is difficult to track in which part of the
    code each hook callback was created. This makes things hard to debug when a single
    hook callback goes wrong. It also makes it impossible to disable some hook callbacks after
    they have been created.

    We resolve this issue by storing the current contexts in a static list.
    Whenever a hook is created, the list of current contexts is copied as a
    ``contexts`` attribute. This attribute can be later examined, either for
    removal or for limiting the set of hook callbacks that should be applied.
    """
    return Context(name).enter()
