"""错误码定义（前缀 DATA_ANA_*，对齐 02-architecture-design.md §6.2）.

错误码语义：
- 4xx 客户端错误（权限/参数/资源不存在）
- 5xx 服务端错误（内部异常）
- 200 + degraded:true 降级模式（外部依赖不可用但服务仍可响应骨架数据）
"""

from enum import StrEnum


class ErrorCode(StrEnum):
    """data-ana 错误码枚举."""

    # 客户端错误
    UNAUTHORIZED = "DATA_ANA_UNAUTHORIZED"  # 401 缺失 x-user-id 或 token 无效
    FORBIDDEN = "DATA_ANA_FORBIDDEN"  # 403 角色无对应权限
    DATASCOPE_VIOLATION = "DATA_ANA_DATASCOPE_VIOLATION"  # 403 查询目标超出 DataScope
    INVALID_DATE_RANGE = "DATA_ANA_INVALID_DATE_RANGE"  # 400 start_date > end_date
    STUDENT_NOT_FOUND = "DATA_ANA_STUDENT_NOT_FOUND"  # 404 student_id 不存在
    CLASS_NOT_FOUND = "DATA_ANA_CLASS_NOT_FOUND"  # 404 class_id 不存在
    WARNING_NOT_FOUND = "DATA_ANA_WARNING_NOT_FOUND"  # 404 预警 ID 不存在
    RATE_LIMITED = "DATA_ANA_RATE_LIMITED"  # 429 触发限流

    # 降级模式（200 + degraded:true）
    CLICKHOUSE_UNAVAILABLE = "DATA_ANA_CLICKHOUSE_UNAVAILABLE"  # ClickHouse 不可达
    KAFKA_UNAVAILABLE = "DATA_ANA_KAFKA_UNAVAILABLE"  # Kafka producer 不可达
    REDIS_UNAVAILABLE = "DATA_ANA_REDIS_UNAVAILABLE"  # Redis 不可达
    IAM_GRPC_UNAVAILABLE = "DATA_ANA_IAM_GRPC_UNAVAILABLE"  # iam gRPC 不可达

    # 服务端错误
    INTERNAL_ERROR = "DATA_ANA_INTERNAL_ERROR"  # 500 未捕获异常


# HTTP 状态码映射
HTTP_STATUS: dict[ErrorCode, int] = {
    ErrorCode.UNAUTHORIZED: 401,
    ErrorCode.FORBIDDEN: 403,
    ErrorCode.DATASCOPE_VIOLATION: 403,
    ErrorCode.INVALID_DATE_RANGE: 400,
    ErrorCode.STUDENT_NOT_FOUND: 404,
    ErrorCode.CLASS_NOT_FOUND: 404,
    ErrorCode.WARNING_NOT_FOUND: 404,
    ErrorCode.RATE_LIMITED: 429,
    ErrorCode.CLICKHOUSE_UNAVAILABLE: 200,  # 降级模式
    ErrorCode.KAFKA_UNAVAILABLE: 200,  # 降级模式
    ErrorCode.REDIS_UNAVAILABLE: 200,  # 降级模式
    ErrorCode.IAM_GRPC_UNAVAILABLE: 200,  # 降级模式
    ErrorCode.INTERNAL_ERROR: 500,
}


class DataAnaError(Exception):
    """data-ana 业务异常（携带错误码）."""

    def __init__(
        self,
        code: ErrorCode,
        message: str,
        *,
        details: dict[str, str] | None = None,
    ) -> None:
        super().__init__(message)
        self.code = code
        self.message = message
        self.details = details
