from typing import Any

from ..exceptions import ConfigurationError
from ..models import Message
from ..providers.registry import PROVIDERS
from ..providers.types import ProviderSpec


def _is_enabled(value: Any) -> bool:
    """Return True if a provider config value enables the provider.

    A provider is considered enabled unless the value is falsy.

    Args:
        value: The configuration value passed for a provider.

    Returns:
        bool: True if the provider should be used, False otherwise.
    """
    return bool(value)


def _resolve_kwargs(name: str, value: Any, *, string_key: str | None = None) -> dict:
    """Normalise provider configuration into keyword arguments.

    Args:
        name: Provider name (used for error messages).
        value: User-supplied configuration value.
        string_key: Optional key name to map string shorthand to.

    Returns:
        Normalised keyword arguments for the provider.

    Raises:
        ConfigurationError: If the value type is unsupported or if string
            shorthand is used but not supported by the provider.
    """
    if value is True:
        return {}

    if isinstance(value, dict):
        return value

    if isinstance(value, str):
        if string_key:
            return {string_key: value}
        raise ConfigurationError(
            f"Provider '{name}' does not support string shorthand. Pass a dict or True instead."
        )

    raise ConfigurationError(f"Invalid {name} config: {value!r}")


def _resolve_provider(name: str, value: Any) -> ProviderSpec | None:
    """Resolve a provider specification from its name and config value.

    Args:
        name: Provider name.
        value: User-supplied configuration value.

    Returns:
        The provider specification if enabled and found, or None.
    """
    if not _is_enabled(value):
        return None

    return PROVIDERS.get(name)


def _build_message(base: Message, provider_kwargs: dict[str, Any]) -> Message:
    """Build a message instance for a provider.

    If the provider kwargs include a `message` override, a new Message
    instance is created using that value. Otherwise, the original message
    is returned unchanged.

    Args:
        base: The base Message instance.
        provider_kwargs: Provider-specific keyword arguments.

    Returns:
        Message: The message to send for this provider.
    """
    if "message" in provider_kwargs:
        return Message(text=provider_kwargs.pop("message"))
    return base


def iter_providers(message: Message, kwargs: dict[str, Any]):
    """Yield resolved providers with their message and kwargs.

    Args:
        message: The base Message instance.
        kwargs: Raw provider keyword arguments from the caller.

    Yields:
        tuple[ProviderSpec, Message, dict[str, Any]]:
            The provider spec, message instance, and provider kwargs.
    """
    for name, value in kwargs.items():
        spec: ProviderSpec | None = _resolve_provider(name, value)
        if spec is None:
            continue

        provider_kwargs = _resolve_kwargs(name, value, string_key=spec.string_key)
        msg = _build_message(message, provider_kwargs)

        yield spec, msg, provider_kwargs
