"""ActionState 统一响应信封（004 §11.5 强制约束）.

降级采用方案 B（总裁裁决 §2.6）：
  success=true + error=null + data 内 degraded 字段
"""

from typing import Any, Generic, TypeVar

from pydantic import BaseModel, ConfigDict, Field

T = TypeVar("T")


class ErrorDetail(BaseModel):
    """错误详情."""

    model_config = ConfigDict(populate_by_name=True)

    code: str
    message: str
    details: dict[str, Any] | None = None
    trace_id: str | None = Field(None, alias="traceId")


class ActionState(BaseModel, Generic[T]):  # noqa: UP046 - Pydantic v2 需 Generic[T] 语法
    """统一响应信封.

    所有 HTTP 端点 + gRPC RPC 返回值必须使用此结构.
    降级时 success=true, error=null, data 内含 degraded=true + degraded_reason.
    """

    success: bool
    data: T | None = None
    error: ErrorDetail | None = None

    @classmethod
    def ok(cls, data: T) -> "ActionState[T]":
        """成功响应."""
        return cls(success=True, data=data, error=None)

    @classmethod
    def degraded(cls, data: T, reason: str) -> "ActionState[T]":
        """降级响应（方案 B：success=true + data 内 degraded 字段）.

        data 必须是含 degraded + degraded_reason 字段的模型.
        """
        if hasattr(data, "degraded"):
            data.degraded = True  # type: ignore[attr-defined]
        if hasattr(data, "degraded_reason"):
            data.degraded_reason = reason  # type: ignore[attr-defined]
        return cls(success=True, data=data, error=None)

    @classmethod
    def error_response(
        cls,
        code: str,
        message: str,
        details: dict[str, Any] | None = None,
        trace_id: str | None = None,
    ) -> "ActionState[Any]":
        """错误响应."""
        return cls(
            success=False,
            data=None,
            error=ErrorDetail(
                code=code,
                message=message,
                details=details,
                trace_id=trace_id,
            ),
        )
