import re
import time

from huaweicloudsdkcore.exceptions.exceptions import (
    CallTimeoutException,
    ClientRequestException,
    ConnectionException,
    HostUnreachableException,
    RequestTimeoutException,
    RetryOutageException,
    SdkException,
    SslHandShakeException,
)


TRANSIENT_STATUS_CODES = {408, 429, 500, 502, 503, 504}
TRANSIENT_TEXT_MARKERS = (
    "timed out",
    "timeout",
    "max retries exceeded",
    "connection reset",
    "connection aborted",
    "connection refused",
    "temporarily unavailable",
    "unexpected_eof",
    "eof occurred in violation of protocol",
    "ssl",
)


def is_transient_cloud_error(exc):
    """判断华为云 SDK 临时网络/服务抖动，避免把普通参数错误也重试。"""
    if isinstance(
        exc,
        (
            CallTimeoutException,
            ConnectionException,
            HostUnreachableException,
            RequestTimeoutException,
            RetryOutageException,
            SslHandShakeException,
        ),
    ):
        return True
    status_code = getattr(exc, "status_code", None) or getattr(exc, "status", None)
    try:
        status_code = int(status_code)
    except (TypeError, ValueError):
        status_code = None
    if status_code in TRANSIENT_STATUS_CODES:
        return True
    text = str(exc).lower()
    if isinstance(exc, ClientRequestException):
        match = re.search(r"(?:status_code\s*[:=]\s*|http\s+)(\d{3})\b", text)
        return bool(match and int(match.group(1)) in TRANSIENT_STATUS_CODES)
    if isinstance(exc, SdkException):
        return any(marker in text for marker in TRANSIENT_TEXT_MARKERS)
    return any(marker in text for marker in TRANSIENT_TEXT_MARKERS)


def retry_cloud_call(label, func, attempts=3, delay_seconds=3):
    """华为云 API 统一重试入口；只重试连接超时、SSL EOF、5xx/429 等临时异常。"""
    last_exc = None
    for attempt in range(1, max(1, attempts) + 1):
        try:
            return func()
        except Exception as exc:
            last_exc = exc
            if attempt >= attempts or not is_transient_cloud_error(exc):
                raise
            print(f"步骤: 云API临时异常，重试 {attempt + 1}/{attempts} {label}: {exc}")
            time.sleep(delay_seconds * attempt)
    raise last_exc
