from __future__ import annotations

import asyncio
import logging
from collections.abc import Awaitable, Callable
from typing import Any

logger = logging.getLogger(__name__)


class FallbackChain:
    """Chained fallback executor for degraded resilience paths."""

    def __init__(self, name: str) -> None:
        self.name = name
        self._chain: list[tuple[str, Callable[..., Awaitable[Any]]]] = []
        self._degraded_response: Any = None
        self._has_degraded = False

    def add(self, fn: Callable[..., Awaitable[Any]], label: str = "") -> FallbackChain:
        """Append a callable to the chain and return self."""
        self._chain.append((label or getattr(fn, "__name__", "fallback"), fn))
        return self

    def set_degraded_response(self, response: Any) -> FallbackChain:
        """Set terminal degraded response if all callables fail."""
        self._degraded_response = response
        self._has_degraded = True
        return self

    async def execute(self, *args, **kwargs) -> tuple[Any, str]:
        """Execute chain until one callable succeeds."""
        last_exc: Exception | None = None
        for label, fn in self._chain:
            try:
                return await fn(*args, **kwargs), label
            except Exception as exc:
                last_exc = exc
                logger.warning("Fallback step '%s' failed in chain '%s': %s", label, self.name, exc)
                continue

        if self._has_degraded:
            return self._degraded_response, "degraded"
        if last_exc is not None:
            raise last_exc
        raise RuntimeError(f"FallbackChain '{self.name}' has no callables and no degraded response")
