import logging
import os
from typing import NamedTuple, Optional

from .. import ConfigProvider
from ..builder import ProviderBuilder
from ._CachingProviderBuilder import CachingProviderBuilder
from ._RedisCachingConfigProvider import RedisCachingConfigProvider, _DEFAULT_NAMESPACE

logger = logging.getLogger(__name__)

_REDIS = "redis"
_DEFAULT_TTL_SECONDS = 21600  # 6 hours
_DEFAULT_PORT = 6379
_DEFAULT_DB = 0
_DEFAULT_SOCKET_TIMEOUT_SECONDS = 1.5


class CacheConfig(NamedTuple):
    client: object
    ttl_seconds: int
    namespace: str


def _env_int(name: str, default: int) -> int:
    value = os.environ.get(name)
    if value is None or value.strip() == "":
        return default
    try:
        return int(value)
    except ValueError:
        logger.warning("Invalid integer for %s=%r; using default %d", name, value, default)
        return default


def _env_bool(name: str, default: bool = False) -> bool:
    value = os.environ.get(name)
    if value is None:
        return default
    return value.strip().lower() in ("true", "yes", "1")


def build_cache_from_env() -> Optional[CacheConfig]:
    """Build a Redis cache configuration from environment variables.

    Returns ``None`` (caching disabled) unless ``CONFIG_CACHE_TYPE=redis``. When
    enabled but ``redis`` is not installed or the client cannot be created, logs a
    warning and returns ``None`` so config retrieval continues uncached.
    """
    if os.environ.get("CONFIG_CACHE_TYPE", "").strip().lower() != _REDIS:
        return None

    try:
        import redis  # imported lazily; optional dependency
    except ImportError:
        logger.warning(
            "CONFIG_CACHE_TYPE=redis but the 'redis' package is not installed; "
            "install edpconfigprovider[redis]. Continuing without caching."
        )
        return None

    ttl = _env_int("CONFIG_CACHE_TTL_SECONDS", _DEFAULT_TTL_SECONDS)
    namespace = os.environ.get("CONFIG_CACHE_NAMESPACE", _DEFAULT_NAMESPACE)

    client_kwargs = dict(
        host=os.environ.get("CONFIG_CACHE_REDIS_HOST", "localhost"),
        port=_env_int("CONFIG_CACHE_REDIS_PORT", _DEFAULT_PORT),
        db=_env_int("CONFIG_CACHE_REDIS_DB", _DEFAULT_DB),
        decode_responses=True,
        socket_timeout=_DEFAULT_SOCKET_TIMEOUT_SECONDS,
        socket_connect_timeout=_DEFAULT_SOCKET_TIMEOUT_SECONDS,
    )
    password = os.environ.get("CONFIG_CACHE_REDIS_PASSWORD")
    if password:
        client_kwargs["password"] = password
    if _env_bool("CONFIG_CACHE_REDIS_SSL"):
        client_kwargs["ssl"] = True

    try:
        client = redis.Redis(**client_kwargs)
    except Exception:
        logger.warning("Failed to create Redis client; continuing without caching", exc_info=True)
        return None

    return CacheConfig(client=client, ttl_seconds=ttl, namespace=namespace)


def wrap_provider(provider: ConfigProvider, cache: Optional[CacheConfig]) -> ConfigProvider:
    """Decorate a leaf provider with read-through caching when ``cache`` is set."""
    if cache is None:
        return provider
    return RedisCachingConfigProvider(
        inner=provider,
        redis_client=cache.client,
        ttl_seconds=cache.ttl_seconds,
        namespace=cache.namespace,
    )


def wrap_builder(builder: ProviderBuilder, cache: Optional[CacheConfig]) -> ProviderBuilder:
    """Decorate a provider builder so its built providers are cache-backed."""
    if cache is None:
        return builder
    return CachingProviderBuilder(
        inner_builder=builder,
        redis_client=cache.client,
        ttl_seconds=cache.ttl_seconds,
        namespace=cache.namespace,
    )
