"""UI 自动化错误码分类与异常体系。

定义可重试/不可重试错误码集合，以及 Flow 异常体系。
供 RetryPolicy.should_retry / CircuitBreaker / Flow 状态机使用。

错误码分类（spec 第十节）：
- RETRYABLE_CODES（4 类）：瞬时故障，最多重试 1 次
- NON_RETRYABLE_CODES（7 类）：永久故障，不重试
"""

from __future__ import annotations

from typing import Optional


# 可重试错误码：瞬时故障，RetryPolicy.should_retry 返回 True
RETRYABLE_CODES: frozenset[str] = frozenset({
    "RATE_LIMITED",          # 限流，需退避后重试
    "SEND_TIMEOUT",          # 发送超时，可能下次成功
    "STATE_DIRTY",           # UI 状态脏，重置后可重试
    "X11_TEMP_UNAVAILABLE",  # X11 临时不可用，重试可能恢复
})

# 不可重试错误码：永久故障，重试无意义
NON_RETRYABLE_CODES: frozenset[str] = frozenset({
    "WECHAT_NOT_LOGGED_IN",  # 未登录，需人工介入
    "WINDOW_NOT_FOUND",      # 窗口未找到，需检查微信运行
    "ELEMENT_NOT_FOUND",     # 元素未找到，需检查 UI 布局
    "DB_VERIFY_FAILED",      # DB 校验失败（防串号），需人工排查
    "AMBIGUOUS_CONTACT",     # 联系人歧义，需修正 display_name
    "X11_UNAVAILABLE",       # X11 持续不可用，需检查容器
    "BRIDGE_CIRCUITED",      # bridge 熔断，需等待恢复
})


def is_retryable(code: str) -> bool:
    """判断错误码是否可重试。"""
    return code in RETRYABLE_CODES


class FlowError(Exception):
    """Flow 执行异常基类。

    Attributes:
        code: 错误码（如 WINDOW_NOT_FOUND / SEND_TIMEOUT）
        message: 人类可读错误信息
        retryable: 是否可重试（根据 code 自动推断，可显式覆盖）
        details: 额外上下文（dict，可选）
    """

    def __init__(
        self,
        code: str,
        message: str = "",
        *,
        retryable: Optional[bool] = None,
        details: Optional[dict] = None,
    ) -> None:
        self.code = code
        self.message = message or code
        # 未显式指定时按 code 推断
        self.retryable = (
            retryable if retryable is not None else is_retryable(code)
        )
        self.details = details or {}
        super().__init__(self.message)

    def __repr__(self) -> str:
        return (
            f"FlowError(code={self.code!r}, retryable={self.retryable}, "
            f"message={self.message!r})"
        )


class TransientError(FlowError):
    """瞬时错误（可重试）。

    用于 RETRYABLE_CODES 中的错误码，或显式指定 retryable=True。
    """

    def __init__(
        self,
        code: str,
        message: str = "",
        *,
        details: Optional[dict] = None,
    ) -> None:
        super().__init__(code, message, retryable=True, details=details)


class PermanentError(FlowError):
    """永久错误（不可重试）。

    用于 NON_RETRYABLE_CODES 中的错误码，或显式指定 retryable=False。
    """

    def __init__(
        self,
        code: str,
        message: str = "",
        *,
        details: Optional[dict] = None,
    ) -> None:
        super().__init__(code, message, retryable=False, details=details)


class ElementNotFoundError(FlowError):
    """元素未找到（图像匹配失败且 require_image=True，或几何规格缺失）。

    默认 code=ELEMENT_NOT_FOUND，retryable=False。
    """

    def __init__(
        self,
        message: str = "",
        *,
        code: str = "ELEMENT_NOT_FOUND",
        details: Optional[dict] = None,
    ) -> None:
        super().__init__(code, message, retryable=False, details=details)
