# =============================================================================
# 企微IT智能服务台 — 统一响应格式工具
# =============================================================================
# 说明：定义所有 API 的统一响应格式和异常处理
# 格式：{code: 0, data: {}, message: "success"}
# code=0 表示成功，非0表示错误（1000+通用/2000+企微/3000+业务）
# =============================================================================

from typing import Any, Dict, Optional

from fastapi import Request
from fastapi.responses import JSONResponse


# --------------------------------------------------------------------------
# 统一响应函数
# --------------------------------------------------------------------------
def success_response(data: Any = None, message: str = "success") -> Dict[str, Any]:
    """构建成功响应。

    所有 API 成功时都应使用此函数返回统一格式。

    Args:
        data: 业务数据（可以是字典、列表、None等）
        message: 成功消息（默认 "success"）

    Returns:
        Dict[str, Any]: 统一格式的响应字典
        示例: {"code": 0, "data": {...}, "message": "success"}
    """
    return {
        "code": 0,
        "data": data,
        "message": message,
    }


def error_response(code: int, message: str, data: Any = None) -> Dict[str, Any]:
    """构建错误响应。

    所有 API 错误时都应使用此函数返回统一格式。

    Args:
        code: 错误码（1000+通用/2000+企微/3000+业务）
        message: 错误消息
        data: 附加数据（可选，如验证错误详情）

    Returns:
        Dict[str, Any]: 统一格式的错误响应字典
        示例: {"code": 1001, "data": null, "message": "参数错误"}
    """
    return {
        "code": code,
        "data": data,
        "message": message,
    }


# --------------------------------------------------------------------------
# 业务异常类
# --------------------------------------------------------------------------
class AppException(Exception):
    """业务异常基类。

    在业务逻辑中抛出此异常，全局异常处理器会自动转换为统一响应格式。
    避免在每个路由函数中重复写 try/except 和响应构造代码。

    Attributes:
        code: 错误码
        message: 错误消息
        data: 附加数据
    """

    def __init__(self, code: int, message: str, data: Any = None):
        """初始化业务异常。

        Args:
            code: 错误码
            message: 错误消息
            data: 附加数据
        """
        self.code = code
        self.message = message
        self.data = data
        super().__init__(self.message)


# --------------------------------------------------------------------------
# 预定义错误常量
# --------------------------------------------------------------------------
# 错误码规范:
# 0     = 成功
# 1000+ = 通用错误（参数错误、未授权等）
# 2000+ = 企微 API 错误
# 3000+ = 业务逻辑错误

# --- 通用错误 (1000+) ---
ERR_PARAMS = AppException(1001, "参数错误")
ERR_UNAUTHORIZED = AppException(1002, "未授权")
ERR_NOT_FOUND = AppException(1003, "资源不存在")
ERR_FORBIDDEN = AppException(1004, "无权限访问")
ERR_INTERNAL = AppException(1005, "服务器内部错误")

# --- 企微 API 错误 (2000+) ---
ERR_WECOM_TOKEN = AppException(2001, "企微 access_token 获取失败")
ERR_WECOM_SEND = AppException(2002, "企微消息发送失败")
ERR_WECOM_DECRYPT = AppException(2003, "企微消息解密失败")
ERR_WECOM_ENCRYPT = AppException(2004, "企微消息加密失败")
ERR_WECOM_VERIFY = AppException(2005, "企微回调签名验证失败")
ERR_WECOM_USER_INFO = AppException(2006, "企微用户信息获取失败")

# --- 业务逻辑错误 (3000+) ---
ERR_AGENT_OFFLINE = AppException(3001, "坐席不在线")
ERR_CONVERSATION_RESOLVED = AppException(3002, "会话已结单")
ERR_CONVERSATION_NOT_FOUND = AppException(3003, "会话不存在")
ERR_AGENT_NOT_FOUND = AppException(3004, "坐席不存在")
ERR_AGENT_BUSY = AppException(3005, "坐席已满负荷，无法接单")
ERR_DUPLICATE_ASSIGN = AppException(3006, "会话已分配坐席")
ERR_GRAB_NO_AGENT = AppException(3011, "该会话尚未分配坐席，请使用接单功能")
ERR_GRAB_SELF = AppException(3012, "不能接手自己的会话")
ERR_GRAB_NOT_SERVING = AppException(3013, "只能接手服务中的会话")


# --------------------------------------------------------------------------
# 全局异常处理器
# --------------------------------------------------------------------------
async def app_exception_handler(request: Request, exc: AppException) -> JSONResponse:
    """AppException 全局异常处理器。

    当业务逻辑抛出 AppException 时，FastAPI 自动调用此处理器，
    将异常转换为统一响应格式返回给前端。

    Args:
        request: 请求对象（FastAPI 自动传入）
        exc: 业务异常对象（FastAPI 自动传入）

    Returns:
        JSONResponse: 统一格式的错误响应
    """
    return JSONResponse(
        status_code=200,  # 业务错误仍返回 HTTP 200，通过 code 区分
        content=error_response(exc.code, exc.message, exc.data),
    )
