from abc import ABC, abstractmethod
from typing import Generic, Optional, TypeVar

from pydantic import BaseModel, Field

from algo_backend.exception import BasicException, BasicStatusCode, CommonStatusCode

T = TypeVar("T")


class AbstractRespVo(BaseModel, ABC):
    @classmethod
    @abstractmethod
    def success(cls, result: Optional[T]) -> "AbstractRespVo": ...

    @classmethod
    @abstractmethod
    def from_exception(
        cls,
        e: BasicException,
        *,
        api_name: Optional[str] = None,
        request_id: Optional[str] = None,
    ) -> "AbstractRespVo":
        """
        将异常转换为vo
        """
        ...

    @abstractmethod
    def set_request_id(self, request_id: Optional[str]) -> "AbstractRespVo":
        """
        增加requestId，以帮助追踪问题
        """
        ...


class BaseRespVo(AbstractRespVo, Generic[T]):
    """
    默认vo实现
    """

    code: int
    message: str
    result: Optional[T] = Field(None, description="结果")
    requestId: Optional[str] = None

    @classmethod
    def success(cls, result: Optional[T]) -> "BaseRespVo":
        return BaseRespVo(
            code=CommonStatusCode.SUCCESS.code,
            result=result,
            message=CommonStatusCode.SUCCESS.msg,
        )

    @classmethod
    def from_status_code(cls, status_code: BasicStatusCode, **kwargs) -> "BaseRespVo":
        return BaseRespVo(
            code=status_code.code,
            message=status_code.msg.format(**kwargs),
            result=None,
        )

    @classmethod
    def from_exception(
        cls,
        e: BasicException,
        *,
        api_name: Optional[str] = None,
        request_id: Optional[str] = None,
    ) -> "BaseRespVo":
        vo = BaseRespVo(
            code=e.code,
            message=e.msg,
            result=None,
        )
        vo.set_request_id(request_id)
        vo.message = f"接口{api_name}报错:{vo.message}" if api_name else vo.message
        return vo

    def set_request_id(self, request_id: Optional[str]) -> "BaseRespVo":
        self.requestId = request_id
        return self
