# Path: rep0st/framework/data/database.py
# B = TypeVar('B')
# class DatabaseModule(Module):
#   def __init__(self, url: URL, base: B):
#   def configure(self, binder: Binder):
#   def provide_database_engine(self) -> DatabaseEngine:
#   def provide_database_session_factory(
#       self, database_engine: DatabaseEngine) -> DatabaseSessionFactory:
#   def provide_database_session(
#       self, database_session_factory: DatabaseSessionFactory) -> Session:
# 
# Path: rep0st/framework/decorator.py
# class DecoratorProcessor(ABC):
# 
#   @abstractmethod
#   def process(self, bindings: List[Any]) -> None:
#     pass
# 
#   def methods_by_decorated_name(
#       self, bindings: List[Any],
#       search_name: str) -> Iterator[Tuple[Type, T, Callable[[Any], Any]]]:
#     for binding in bindings:
#       interface = binding
#       for name, fun in inspect.getmembers(interface):
#         data = getattr(fun, '__' + search_name + '__', None)
#         if data is not None:
#           yield (interface, data, fun)
# 
#   @staticmethod
#   def wrap_function(instance: Any, fun: Callable,
#                     injector: Injector) -> Callable:
#     fun = types.MethodType(fun, instance)
# 
#     @functools.wraps(fun)
#     def wrapper(*args: Any, **kwargs: Any) -> Any:
#       return injector.call_with_injection(
#           callable=fun, args=args, kwargs=kwargs)
# 
#     return wrapper  # type: ignore


import functools
import logging
from threading import local
from typing import Any, Callable, List
from injector import Binder, Module, ProviderOf, inject, multiprovider, singleton
from sqlalchemy.orm import Session
from rep0st.framework.data.database import DatabaseSessionFactory
from rep0st.framework.decorator import DecoratorProcessor
  database_session_provider: ProviderOf[Session] = None
  database_session_factory_provider: ProviderOf[DatabaseSessionFactory] = None


transactional_state = local()


def transactional() -> Any:

  def decorator(fun: Callable):
    transactional_configuration = _TransactionalConfiguration()

    @functools.wraps(fun)
    def wrapper(*args: Any, **kwargs: Any) -> Any:
      if not hasattr(transactional_state, 'depth'):
        transactional_state.depth = 0
      session = transactional_configuration.database_session_provider.get()
      if not transactional_state.depth:
        log.debug(
            f'Starting transaction on method {transactional_configuration.name}'
        )
        # Session start. Implicit with transactional_configuration.injector.get(DatabaseSession)
        pass
      transactional_state.depth += 1
      try:
        ret = fun(*args, **kwargs)
        transactional_state.depth -= 1
        if transactional_state.depth == 0:
          log.debug(
              f'Committing transaction on method {transactional_configuration.name}'

# Path: rep0st/framework/data/database.py
# B = TypeVar('B')
# class DatabaseModule(Module):
#   def __init__(self, url: URL, base: B):
#   def configure(self, binder: Binder):
#   def provide_database_engine(self) -> DatabaseEngine:
#   def provide_database_session_factory(
#       self, database_engine: DatabaseEngine) -> DatabaseSessionFactory:
#   def provide_database_session(
#       self, database_session_factory: DatabaseSessionFactory) -> Session:
# 
# Path: rep0st/framework/decorator.py
# class DecoratorProcessor(ABC):
# 
#   @abstractmethod
#   def process(self, bindings: List[Any]) -> None:
#     pass
# 
#   def methods_by_decorated_name(
#       self, bindings: List[Any],
#       search_name: str) -> Iterator[Tuple[Type, T, Callable[[Any], Any]]]:
#     for binding in bindings:
#       interface = binding
#       for name, fun in inspect.getmembers(interface):
#         data = getattr(fun, '__' + search_name + '__', None)
#         if data is not None:
#           yield (interface, data, fun)
# 
#   @staticmethod
#   def wrap_function(instance: Any, fun: Callable,
#                     injector: Injector) -> Callable:
#     fun = types.MethodType(fun, instance)
# 
#     @functools.wraps(fun)
#     def wrapper(*args: Any, **kwargs: Any) -> Any:
#       return injector.call_with_injection(
#           callable=fun, args=args, kwargs=kwargs)
# 
#     return wrapper  # type: ignore


# Path: rep0st/framework/data/transaction.py
import functools
import logging
from threading import local
from typing import Any, Callable, List
from injector import Binder, Module, ProviderOf, inject, multiprovider, singleton
from sqlalchemy.orm import Session
from rep0st.framework.data.database import DatabaseSessionFactory
from rep0st.framework.decorator import DecoratorProcessor

  database_session_provider: ProviderOf[Session] = None
  database_session_factory_provider: ProviderOf[DatabaseSessionFactory] = None


transactional_state = local()


def transactional() -> Any:

  def decorator(fun: Callable):
    transactional_configuration = _TransactionalConfiguration()

    @functools.wraps(fun)
    def wrapper(*args: Any, **kwargs: Any) -> Any:
      if not hasattr(transactional_state, 'depth'):
        transactional_state.depth = 0
      session = transactional_configuration.database_session_provider.get()
      if not transactional_state.depth:
        log.debug(
            f'Starting transaction on method {transactional_configuration.name}'
        )
        # Session start. Implicit with transactional_configuration.injector.get(DatabaseSession)
        pass
      transactional_state.depth += 1
      try:
        ret = fun(*args, **kwargs)
        transactional_state.depth -= 1
        if transactional_state.depth == 0:
          log.debug(
              f'Committing transaction on method {transactional_configuration.name}'

          )