"""Feature-flag helpers built on top of punq container resolution."""
from functools import wraps
from typing import Any, Callable

from punq import Container

from .decorators import _resolve_container
from .exceptions import FeatureDisabledError

ERR_FMT = "feature_enabled requires /feature/<context>/<use_case>"

SettingNameFactory = Callable[[str], Any]

_settings_interface: type[Any] | None = None
_setting_name_factory: SettingNameFactory | None = None
_disabled_exception: type[Exception] = FeatureDisabledError


def configure_feature_flags(
    settings_interface: type[Any],
    *,
    setting_name_factory: SettingNameFactory | None = None,
    disabled_exception: type[Exception] | None = None,
) -> None:
    """Configure global feature-flag integration."""

    if not isinstance(settings_interface, type):
        raise TypeError("settings_interface must be a type used for container resolution")
    if setting_name_factory is not None and not callable(setting_name_factory):
        raise TypeError("setting_name_factory must be callable")
    if disabled_exception is not None and not issubclass(disabled_exception, Exception):
        raise TypeError("disabled_exception must be an Exception subclass")

    global _settings_interface
    global _setting_name_factory
    global _disabled_exception

    _settings_interface = settings_interface
    _setting_name_factory = setting_name_factory
    if disabled_exception is not None:
        _disabled_exception = disabled_exception


def clear_feature_flag_config() -> None:
    """Reset global feature-flag integration settings."""

    global _settings_interface
    global _setting_name_factory
    global _disabled_exception

    _settings_interface = None
    _setting_name_factory = None
    _disabled_exception = FeatureDisabledError


def _resolve_settings_interface(local_settings_interface: type[Any] | None) -> type[Any]:
    if local_settings_interface is not None:
        return local_settings_interface
    if _settings_interface is not None:
        return _settings_interface
    raise RuntimeError(
        "Feature flags are not configured. "
        "Call configure_feature_flags(settings_interface=...) or pass settings_interface=... to feature_enabled(...)."
    )


def _resolve_setting_name_factory(local_factory: SettingNameFactory | None) -> SettingNameFactory:
    if local_factory is not None:
        return local_factory
    if _setting_name_factory is not None:
        return _setting_name_factory
    return lambda value: value


def _resolve_disabled_exception(local_exception: type[Exception] | None) -> type[Exception]:
    if local_exception is not None:
        if not issubclass(local_exception, Exception):
            raise TypeError("disabled_exception must be an Exception subclass")
        return local_exception
    return _disabled_exception


def _feature_setting_name(feature_path: str, setting_name_factory: SettingNameFactory) -> Any:
    path = feature_path.strip()
    if not path:
        raise ValueError("feature_enabled requires a non-empty feature path")

    normalized = path if path.startswith("/") else f"/{path}"
    if not normalized.startswith("/feature/"):
        raise ValueError("feature_enabled requires a path starting with /feature/")

    parts = [part for part in normalized.strip("/").split("/") if part]
    parts.pop(0)
    if len(parts) != 2:
        raise ValueError(ERR_FMT)

    context, use_case = parts
    return setting_name_factory(f"/feature/{context}/{use_case}/enabled")


def feature_enabled(
    feature_path: str,
    default: bool = True,
    message: str | None = None,
    *,
    settings_interface: type[Any] | None = None,
    setting_name_factory: SettingNameFactory | None = None,
    disabled_exception: type[Exception] | None = None,
    container: Container | None = None,
) -> Callable[[type[Any]], type[Any]]:
    """Guard class __call__ with a feature flag resolved from the container."""

    resolved_settings_interface = _resolve_settings_interface(settings_interface)
    resolved_setting_name_factory = _resolve_setting_name_factory(setting_name_factory)
    resolved_disabled_exception = _resolve_disabled_exception(disabled_exception)
    resolved_setting_name = _feature_setting_name(feature_path, resolved_setting_name_factory)

    def decorator(cls: type[Any]) -> type[Any]:
        original_call = getattr(cls, "__call__", None)
        if original_call is None:
            raise TypeError("feature_enabled can only decorate classes with a __call__ method")

        @wraps(original_call)
        def wrapped(self: Any, *args: Any, **kwargs: Any) -> Any:
            active_container = _resolve_container(container)
            try:
                settings = active_container.resolve(resolved_settings_interface)
            except Exception as exc:
                raise RuntimeError(
                    f"Could not resolve feature settings interface '{resolved_settings_interface.__name__}' "
                    "from the container."
                ) from exc

            if not settings.is_feature_enabled(resolved_setting_name, default=default):
                raise resolved_disabled_exception(message or f"Feature disabled: {resolved_setting_name}")
            return original_call(self, *args, **kwargs)

        setattr(cls, "__call__", wrapped)
        return cls

    return decorator


__all__ = [
    "ERR_FMT",
    "clear_feature_flag_config",
    "configure_feature_flags",
    "feature_enabled",
]

