import inspect
import logging
from typing import Any, TypeVar

from app.library.Singleton import Singleton

T = TypeVar("T")
LOG: logging.Logger = logging.getLogger(__name__)


class Services(metaclass=Singleton):
    _dct: dict[str, T] = {}

    _instance = None
    """The instance of the class."""

    @staticmethod
    def get_instance() -> "Services":
        if Services._instance is None:
            Services._instance = Services()

        return Services._instance

    def add(self, name: str, service: T):
        self._dct[name] = service

    def add_all(self, services: dict[str, T]):
        for name, service in services.items():
            self.add(name, service)

    def get(self, name: str) -> T | None:
        return self._dct.get(name)

    def has(self, name: str) -> bool:
        return name in self._dct

    def remove(self, name: str):
        if name not in self._dct:
            return

        self._dct.pop(name, None)

    def clear(self):
        self._dct.clear()

    def get_all(self) -> dict[str, T]:
        return self._dct.copy()

    async def handle_async(self, handler: callable, **kwargs) -> Any:
        context = {**self.get_all(), **kwargs}

        sig = inspect.signature(handler)
        expected_args = sig.parameters.keys()
        filtered = {k: v for k, v in context.items() if k in expected_args}

        if missing_args := expected_args - filtered.keys():
            LOG.error(f"Missing arguments for handler '{handler.__name__}': {missing_args}")

        return await handler(**filtered)

    def handle_sync(self, handler: callable, **kwargs) -> Any:
        context = {**self.get_all(), **kwargs}

        sig = inspect.signature(handler)
        expected_args = sig.parameters.keys()
        filtered = {k: v for k, v in context.items() if k in expected_args}

        if missing_args := expected_args - filtered.keys():
            LOG.error(f"Missing arguments for handler '{handler.__name__}': {missing_args}")

        return handler(**filtered)
