from enum import Enum
from typing import List, Optional
from typing_extensions import Literal

from pydantic import BaseModel, root_validator

from nonebot.typing import overrides
from nonebot.adapters import Event as BaseEvent

from .message import Message


class Event(BaseEvent):
    """
    钉钉协议事件。各事件字段参考 `钉钉文档`_

    .. _钉钉文档:
        https://ding-doc.dingtalk.com/document#/org-dev-guide/elzz1p
    """

    chatbotUserId: str

    @overrides(BaseEvent)
    def get_type(self) -> Literal["message", "notice", "request", "meta_event"]:
        raise ValueError("Event has no type!")

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

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

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

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

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

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

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


class TextMessage(BaseModel):
    content: str


class AtUsersItem(BaseModel):
    dingtalkId: str
    staffId: Optional[str]


class ConversationType(str, Enum):
    private = "1"
    group = "2"


class MessageEvent(Event):
    """消息事件"""
    msgtype: str
    text: TextMessage
    msgId: str
    createAt: int  # ms
    conversationType: ConversationType
    conversationId: str
    senderId: str
    senderNick: str
    senderCorpId: Optional[str]
    sessionWebhook: str
    sessionWebhookExpiredTime: int
    isAdmin: bool

    message: Message

    @root_validator(pre=True)
    def gen_message(cls, values: dict):
        assert "msgtype" in values, "msgtype must be specified"
        # 其实目前钉钉机器人只能接收到 text 类型的消息
        assert values[
            "msgtype"] in values, f"{values['msgtype']} must be specified"
        content = values[values['msgtype']]['content']
        # 如果是被 @，第一个字符将会为空格，移除特殊情况
        if content[0] == ' ':
            content = content[1:]
        values["message"] = content
        return values

    @overrides(Event)
    def get_type(self) -> Literal["message", "notice", "request", "meta_event"]:
        return "message"

    @overrides(Event)
    def get_event_name(self) -> str:
        return f"{self.get_type()}.{self.conversationType.name}"

    @overrides(Event)
    def get_event_description(self) -> str:
        return f'Message[{self.msgtype}] {self.msgId} from {self.senderId} "{self.text.content}"'

    @overrides(Event)
    def get_message(self) -> Message:
        return self.message

    @overrides(Event)
    def get_plaintext(self) -> str:
        return self.text.content

    @overrides(Event)
    def get_user_id(self) -> str:
        return self.senderId

    @overrides(Event)
    def get_session_id(self) -> str:
        return self.senderId


class PrivateMessageEvent(MessageEvent):
    """私聊消息事件"""
    chatbotCorpId: str
    senderStaffId: Optional[str]
    conversationType: ConversationType = ConversationType.private


class GroupMessageEvent(MessageEvent):
    """群消息事件"""
    atUsers: List[AtUsersItem]
    conversationType: ConversationType = ConversationType.group
    conversationTitle: str
    isInAtList: bool

    @overrides(MessageEvent)
    def is_tome(self) -> bool:
        return self.isInAtList
