from __future__ import annotations

"""Retry classification helpers.

These helpers are shared by higher-level layers (e.g. reasoners provider wiring) when composing
retry/backoff middleware around gateway-owned execution.
"""

def _is_retryable_http_status(status: object) -> bool:
    return isinstance(status, int) and (status == 429 or 500 <= status <= 599)


def _is_retryable_snowflake_error(exc: Exception) -> bool:
    """Best-effort retry classification for transient Snowflake/Snowpark failures.

    This is intentionally conservative: we only retry errors that look like connection/login retryable failures.
    """
    msg = str(exc).lower()
    needles = (
        "could not connect to snowflake backend",
        "login request is retryable",
        "will be handled by authenticator",
        "handle_timeout",
        # conservative network-ish fallbacks
        "connection was closed",
        "temporarily unavailable",
        "timed out",
        "timeout",
    )
    if any(n in msg for n in needles):
        return True
    # Common connector codes surfaced in messages
    if "250001" in msg or "251012" in msg:
        return True
    return False


def _default_retryable_http_error(exc: Exception, req: object) -> bool:  # noqa: ARG001
    """Best-effort retry classification for transient HTTP request failures."""
    # If the exception carries an HTTP status, treat 429/5xx as transient.
    status = getattr(exc, "status", None)
    if _is_retryable_http_status(status):
        return True
    try:
        import importlib

        requests = importlib.import_module("requests")
        if isinstance(exc, (requests.exceptions.Timeout, requests.exceptions.ConnectionError)):
            return True
        # Some codepaths raise HTTPError with a response attached.
        if isinstance(exc, requests.exceptions.HTTPError):
            resp = getattr(exc, "response", None)
            code = getattr(resp, "status_code", None)
            if _is_retryable_http_status(code):
                return True
    except Exception:
        # No requests module in environment (e.g. lightweight unit-test run).
        pass
    msg = str(exc).lower()
    return any(s in msg for s in ("temporarily unavailable", "timeout", "timed out"))


def _default_retryable_http_error_async(exc: Exception, req: object) -> bool:  # noqa: ARG001
    """Best-effort retry classification for transient async HTTP failures (aiohttp)."""
    status = getattr(exc, "status", None)
    if _is_retryable_http_status(status):
        return True
    try:
        import asyncio

        if isinstance(exc, asyncio.TimeoutError):
            return True
    except Exception:
        pass
    try:
        import aiohttp

        if isinstance(exc, (aiohttp.ClientConnectionError, aiohttp.ClientPayloadError)):
            return True
        if isinstance(exc, aiohttp.ClientResponseError):
            if _is_retryable_http_status(exc.status):
                return True
    except Exception:
        # aiohttp might not be installed in lightweight environments.
        pass
    msg = str(exc).lower()
    return any(s in msg for s in ("temporarily unavailable", "timeout", "timed out"))

