import asyncio
import inspect

from collections.abc import Callable
from typing import Any

from logger import logger

from .constants import DEFAULT_HOOK_TIMEOUT


_hooks: dict[str, list[tuple[Callable[..., Any], str | None]]] = {}


def owner(func: Callable[..., Any]) -> str | None:
    m = getattr(func, "__module__", "") or ""
    if m.startswith("modules."):
        parts = m.split(".")
        return parts[1] if len(parts) > 1 else None
    return None


def register_hook(name: str, func: Callable[..., Any] | None = None):
    if func is None:

        def deco(f: Callable[..., Any]):
            _hooks.setdefault(name, []).append((f, owner(f)))
            logger.info(f"[Hook] Зарегистрирован хук '{name}': {f.__name__}")
            return f

        return deco
    _hooks.setdefault(name, []).append((func, owner(func)))
    logger.info(f"[Hook] Зарегистрирован хук '{name}': {func.__name__}")


def unregister_module_hooks(module_name: str):
    for k, lst in list(_hooks.items()):
        filtered = [(f, owner) for (f, owner) in lst if owner != module_name]
        if filtered:
            _hooks[k] = filtered
        else:
            _hooks.pop(k, None)


async def run_hooks(name: str, require_enabled: bool = True, **kwargs) -> list[Any]:
    """Вызывает зарегистрированные хуки и собирает результаты."""
    results: list[Any] = []
    for func, owner in _hooks.get(name, []):
        if require_enabled and owner:
            try:
                from utils.modules_manager import manager

                if not manager.is_enabled(owner):
                    continue
            except Exception:
                pass
        try:
            if inspect.iscoroutinefunction(func):
                coro = func(**kwargs)
            else:

                async def _run_sync():
                    return func(**kwargs)

                coro = _run_sync()

            result = await asyncio.wait_for(coro, timeout=DEFAULT_HOOK_TIMEOUT)
            if result:
                results.append(result)
        except TimeoutError:
            logger.error(
                f"[HOOK:{name}] Таймаут в {getattr(func, '__name__', func)} при timeout={DEFAULT_HOOK_TIMEOUT}",
                exc_info=True,
            )
        except Exception as e:
            logger.error(
                f"[HOOK:{name}] Ошибка в {getattr(func, '__name__', func)}: {e}",
                exc_info=True,
            )
    return results
