"""BlueFirmament TopLevel Exceptions

TODO exception factory to manage logger automatically
"""

__all__ = [
    "BlueFirmamentException",
    "BFExceptionTV",
    "InternalError",
    "ClientError",
    "RequestFailed",
    "ExternalError",
    "Retryable",
    "MaxRetriesExceeded",
    "ParamsInvalid",
    "AtLeastOne",
    "NotImplemented",
    "NotFound",
    "TaskHandlerNotFound",
    "DuplicateOrConflict",
    "Duplicate",
    "Conflict",
    "InvalidStatusTransition",
    "Unauthorized",
    "Forbidden",
    "TooManyRequests",
]

import enum
import abc
import typing
from typing import Optional as Opt
from .utils.typing_ import JsonDumpable
from .task import TaskStatus, TaskID
from .log.main import get_logger

LOGGER = get_logger(__name__)
"""默认日志记录器
"""

if typing.TYPE_CHECKING:
    from .scheme import BaseScheme


BFExceptionTV = typing.TypeVar("BFExceptionTV", bound="BlueFirmamentException")
"""碧霄异常类型变量"""


class BlueFirmamentException(Exception, abc.ABC):
    """碧霄异常基类

    :ivar errmsg: 错误描述
    :ivar

    """

    def __init__(self, errmsg: str | dict = "Exception occured", *args, **kwargs):
        if isinstance(errmsg, str):
            errmsg = {"errmsg": errmsg, **kwargs}

        self.errmsg: dict = errmsg

        super().__init__(errmsg, *args)

    def apply_to_task_result(self, response) -> None:
        """将异常序列化为响应体

        直接修改已有的响应对象的数据

        :param response: 响应对象
        """

    def dump_to_jsonable(self) -> JsonDumpable:
        """序列化为可JSON序列化的类型"""
        return self.errmsg

    @property
    @abc.abstractmethod
    def task_status(self) -> TaskStatus: ...

    @classmethod
    def from_python_exception(cls, exception: Exception) -> "BlueFirmamentException":
        """从Python异常创建碧霄异常

        :param exception: Python异常
        """
        if isinstance(exception, ValueError):
            return ParamsInvalid()

        return InternalError()

    @property
    def name(self) -> str:
        """异常名称"""
        return self.__class__.__name__

    def dump_details_to_str(self) -> str:
        """将详细信息序列化为字符串"""
        return "\n".join([f"{k}: {v}" for k, v in self.errmsg.items()])

    def dump_details_to_dict(self) -> dict:
        """将详细信息序列化为字典"""
        return self.errmsg

    def __str__(self) -> str:
        return f"{self.name}: \n{self.dump_details_to_str()}"


class InternalError(BlueFirmamentException):
    """Exception raised cause of server's fault."""

    @property
    def task_status(self) -> TaskStatus:
        return TaskStatus.INTERNAL_SERVER_ERROR


class ClientError(BlueFirmamentException):
    """Exception raised cause of client's fault"""

    @property
    def task_status(self) -> TaskStatus:
        return TaskStatus.BAD_REQUEST


class RequestFailed(InternalError):
    """请求失败

    请求没有到达目标服务
    """

    def __init__(self, response, *args, **kwargs):
        super().__init__({"response": response}, *args, **kwargs)


class ExternalError(BlueFirmamentException):
    """外部错误

    任何外部应用程序与服务导致的异常都将转换为该异常
    """

    def __init__(
        self,
        service_name: str,
        logger=LOGGER,
        **errinfo: typing.Any,
    ):
        """
        :param service_name: 外部服务名称
        :param errcode: 错误码

            可以是错误码、错误信息等
        """

        super().__init__({"service_name": service_name, **errinfo}, logger)

    def __getitem__(self, key: str) -> typing.Any:
        """获取错误信息"""
        return self.errmsg[key]

    @property
    def task_status(self):
        return TaskStatus.SERVICE_UNAVAILABLE


class Retryable(BlueFirmamentException):
    """The exception can be eliminated by retrying the operation."""

    def __init__(self, delay: Opt[float] = None):
        """
        :param delay: retry after this many seconds
        """
        super().__init__("please retry after %s seconds" % delay)
        self.__delay = delay

    @property
    def delay(self) -> Opt[float]:
        return self.__delay

    @property
    def task_status(self):
        return TaskStatus.SERVICE_UNAVAILABLE


class MaxRetriesExceeded(BlueFirmamentException):
    def __init__(self):
        super().__init__("max retries exceeded")

    @property
    def task_status(self) -> TaskStatus:
        return TaskStatus.SERVICE_UNAVAILABLE


class ParamsInvalid(ClientError, ValueError):
    """参数无效

    场景
    ------
    - 类型不合法
    - 值不合法
    - 缺少参数
    - 通过参数获得（计算、数据库访问）的值不合法
    """

    def __init__(self, msg: str = "", **params):
        super().__init__(
            {"message": "Invalid parameter(s), %s" % msg, "parameters": params}
        )

    @property
    def task_status(self):
        return TaskStatus.UNPROCESSABLE_ENTITY


class ParamRequired(ClientError, ValueError):
    """Exception for not giving required parameter."""

    def __init__(self, param_name: str, **kwargs):
        super().__init__("parameter required", param=param_name)


class AtLeastOne(ParamsInvalid):
    """至少需要一个参数

    在这些参数中，至少提供一个（有效）
    """

    def __init__(
        self,
        *params: typing.Any,
    ):
        """
        :param params: 参数名称
        """
        self.__params = params

    def __str__(self) -> str:
        return (
            f"{self.name}: at least one of following parameters should be provided:\n"
            + "\n    ".join(self.__params)
        )


class NotImplemented(InternalError, NotImplementedError):
    def __init__(self, service_name: str, *args, **kwargs):
        """
        :param service_name: 未实现的服务名
        """
        errmsg = "service %s is not implemented" % service_name
        super().__init__(errmsg, *args, **kwargs)

    @property
    def task_status(self) -> TaskStatus:
        return TaskStatus.NOT_IMPLEMENTED


class NotFound(ClientError, KeyError):
    """未找到"""

    @property
    def task_status(self) -> TaskStatus:
        return TaskStatus.NOT_FOUND


class TaskHandlerNotFound(NotFound):
    """没有该任务的处理器"""

    def __init__(self, task_id: TaskID):
        super().__init__("task handler not found", task_id=task_id)

    @property
    def task_id(self):
        return self.errmsg.get("task_id", None)


class DuplicateOrConflict(InternalError):
    """冲突或重复"""

    def __init__(
        self,
        *resource: tuple[typing.Any, typing.Any],
        operation: str,
        msg: str = "",
    ):
        """
        :param resource: 资源的的元组
            用（资源名，资源标识符）表示一个资源实例
        :param operation: 正在对资源进行的操作
        :param errmsg: 附加描述
        """

        super().__init__(
            f"Cannot perform {operation} on {resource[0]}: {resource[1]} cause: \n{msg}"
        )

    @classmethod
    def from_scheme(
        cls,
        scheme: "BaseScheme",
        operation: str,
        errmsg: str = "",
    ) -> "DuplicateOrConflict":
        """从数据模型 实例创建冲突异常"""
        return cls(
            (scheme.dal_path(), scheme[scheme._get_key_field()]),
            operation=operation,
            msg=errmsg,
        )

    @property
    def task_status(self) -> TaskStatus:
        return TaskStatus.CONFLICT


class Duplicate(DuplicateOrConflict):
    def __init__(self, msg: str):
        self.__msg = msg

    def dump_details_to_str(self) -> str:
        return self.__msg


class Conflict(DuplicateOrConflict):
    def __init__(self, msg: str, **kwargs):
        self.__msg = msg
        self.__context = kwargs

    def dump_details_to_str(self) -> str:
        return self.__msg


class InvalidStatusTransition(Conflict):
    """无效的状态转换

    不可以从当前状态切换到目标状态
    """

    def __init__(self, status_name: str, current_status, target_status):
        self.__status_name = status_name
        self.__current_status = current_status
        self.__target_status = target_status

    @classmethod
    def from_enum_member(cls, current: enum.Enum, target: enum.Enum) -> typing.Self:
        """从枚举成员创建"""
        return cls(current.__class__.__name__, current.value, target.value)

    def dump_details_to_str(self) -> str:
        return f"cannot transist {self.__status_name} from {self.__current_status} to {self.__target_status}"


class Unauthorized(InternalError):
    """未认证"""

    def __init__(
        self, msg: Opt[str] = None, identity: Opt[str] = None, *args, **kwargs
    ):
        """
        :param identity: 导致未认证时使用的凭证（如果有）
        """
        super().__init__("you are using identity (token): \n%s" % identity)

    @property
    def task_status(self) -> TaskStatus:
        return TaskStatus.UNAUTHORIZED


class Forbidden(InternalError):
    """无权操作"""

    def __init__(
        self,
        msg: str,
    ):
        super().__init__(msg)

    @property
    def task_status(self):
        return TaskStatus.FORBIDDEN


class TooManyRequests(InternalError):
    pass


# class UnavailableForLegalReasons(BlueFirmamentException):

#     def __init__(self, errmsg, *args, **kwargs) -> None:

#         super().__init__(451, "Unavailable For Legal Reasons, %s" % errmsg, *args, **kwargs)


# class TooManyRequests(BlueFirmamentException):

#     def __init__(self, errmsg, *args, **kwargs) -> None:

#         super().__init__(429, "Too many request, limit reached. %s" % errmsg, *args, **kwargs)
