import json
from enum import Enum
from typing_extensions import Literal
from typing import Any, Dict, Type, Optional

from pydantic import Field, BaseModel, ValidationError

from nonebot.log import logger
from nonebot.typing import overrides
from nonebot.utils import escape_tag
from nonebot.adapters import Event as BaseEvent
from nonebot.adapters import Message as BaseMessage


class UserPermission(str, Enum):
    """
    :说明:

      用户权限枚举类

      * ``OWNER``: 群主
      * ``ADMINISTRATOR``: 群管理
      * ``MEMBER``: 普通群成员
    """
    OWNER = 'OWNER'
    ADMINISTRATOR = 'ADMINISTRATOR'
    MEMBER = 'MEMBER'


class NudgeSubjectKind(str, Enum):
    """
    :说明:

      戳一戳类型枚举类

      * ``Group``: 群
      * ``Friend``: 好友
    """
    Group = 'Group'
    Friend = 'Friend'


class GroupInfo(BaseModel):
    id: int
    name: str
    permission: UserPermission


class GroupChatInfo(BaseModel):
    id: int
    name: str = Field(alias='memberName')
    permission: UserPermission
    group: GroupInfo


class PrivateChatInfo(BaseModel):
    id: int
    nickname: str
    remark: str


class NudgeSubject(BaseModel):
    id: int
    kind: NudgeSubjectKind


class Event(BaseEvent):
    """
    mirai-api-http 协议事件,字段与 mirai-api-http 一致。各事件字段参考 `mirai-api-http 事件类型`_

    .. _mirai-api-http 事件类型:
        https://github.com/project-mirai/mirai-api-http/blob/master/docs/EventType.md
    """
    self_id: int
    type: str

    @classmethod
    def new(cls, data: Dict[str, Any]) -> "Event":
        """
        此事件类的工厂函数, 能够通过事件数据选择合适的子类进行序列化
        """
        type = data['type']

        def all_subclasses(cls: Type[Event]):
            return set(cls.__subclasses__()).union(
                [s for c in cls.__subclasses__() for s in all_subclasses(c)])

        event_class: Optional[Type[Event]] = None
        for subclass in all_subclasses(cls):
            if subclass.__name__ != type:
                continue
            event_class = subclass

        if event_class is None:
            return Event.parse_obj(data)

        while event_class and issubclass(event_class, Event):
            try:
                return event_class.parse_obj(data)
            except ValidationError as e:
                logger.info(
                    f'Failed to parse {data} to class {event_class.__name__}: '
                    f'{e.errors()!r}. Fallback to parent class.')
                event_class = event_class.__base__  # type: ignore

        raise ValueError(f'Failed to serialize {data}.')

    @overrides(BaseEvent)
    def get_type(self) -> Literal["message", "notice", "request", "meta_event"]:
        from . import meta, notice, message, request
        if isinstance(self, message.MessageEvent):
            return 'message'
        elif isinstance(self, notice.NoticeEvent):
            return 'notice'
        elif isinstance(self, request.RequestEvent):
            return 'request'
        else:
            return 'meta_event'

    @overrides(BaseEvent)
    def get_event_name(self) -> str:
        return self.type

    @overrides(BaseEvent)
    def get_event_description(self) -> str:
        return escape_tag(str(self.normalize_dict()))

    @overrides(BaseEvent)
    def get_message(self) -> BaseMessage:
        raise ValueError("Event has no message!")

    @overrides(BaseEvent)
    def get_plaintext(self) -> str:
        raise ValueError("Event has no message!")

    @overrides(BaseEvent)
    def get_user_id(self) -> str:
        raise ValueError("Event has no message!")

    @overrides(BaseEvent)
    def get_session_id(self) -> str:
        raise ValueError("Event has no message!")

    @overrides(BaseEvent)
    def is_tome(self) -> bool:
        return False

    def normalize_dict(self, **kwargs) -> Dict[str, Any]:
        """
        返回可以被json正常反序列化的结构体
        """
        return json.loads(self.json(**kwargs))