pub const WS_PROTOCOL_VERSION: u32 = 1;

use chrono::{DateTime, Utc};
use serde::Deserialize;
use uuid::Uuid;

use crate::event::presence::UserPresenceStatus;

#[derive(Debug, Clone, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum WsInMessage {
    Ping,
    Subscribe {
        room: Uuid,
    },
    Unsubscribe {
        room: Uuid,
    },
    TypingStart {
        room: Uuid,
    },
    TypingStop {
        room: Uuid,
    },
    ReadReceipt {
        room: Uuid,
        last_read_seq: i64,
    },
    MessageList {
        room: Uuid,
        before_seq: Option<i64>,
        after_seq: Option<i64>,
        limit: Option<u64>,
    },
    MessageAround {
        room: Uuid,
        seq: i64,
        limit: Option<u64>,
        thread: Option<Uuid>,
    },
    MessageCreate {
        room: Uuid,
        content: String,
        content_type: Option<String>,
        thread: Option<Uuid>,
        in_reply_to: Option<Uuid>,
    },
    MessageUpdate {
        message: Uuid,
        content: String,
    },
    MessageRevoke {
        message: Uuid,
    },
    RoomGet {
        room: Uuid,
    },
    RoomCreate {
        workspace: Uuid,
        room_name: String,
        public: bool,
        category: Option<Uuid>,
        ai_enabled: Option<bool>,
        channel_type: Option<String>,
    },
    RoomUpdate {
        room: Uuid,
        room_name: Option<String>,
        public: Option<bool>,
        category: Option<Uuid>,
        ai_enabled: Option<bool>,
    },
    RoomDelete {
        room: Uuid,
    },
    CategoryCreate {
        workspace: Uuid,
        name: String,
        position: Option<i32>,
    },
    CategoryUpdate {
        id: Uuid,
        name: Option<String>,
        position: Option<i32>,
    },
    CategoryDelete {
        id: Uuid,
    },
    AccessGrant {
        room: Uuid,
        user: Uuid,
    },
    AccessRevoke {
        room: Uuid,
        user: Uuid,
    },
    StateSetReadSeq {
        room: Uuid,
        last_read_seq: i64,
    },
    MissedMessages {
        room: Uuid,
        after_seq: i64,
        limit: Option<i64>,
    },
    CsrfToken,
    StateUpdateDnd {
        room: Uuid,
        do_not_disturb: Option<bool>,
        dnd_start_hour: Option<i16>,
        dnd_end_hour: Option<i16>,
    },
    ReactionAdd {
        room: Uuid,
        message: Uuid,
        emoji: String,
    },
    ReactionRemove {
        room: Uuid,
        message: Uuid,
        emoji: String,
    },
    ThreadCreate {
        room: Uuid,
        parent: i64,
    },
    ThreadList {
        room: Uuid,
    },
    ThreadResolve {
        thread_id: Uuid,
    },
    ThreadArchive {
        thread_id: Uuid,
    },
    PinAdd {
        room: Uuid,
        message: Uuid,
    },
    PinRemove {
        room: Uuid,
        message: Uuid,
    },
    DraftSave {
        room: Uuid,
        content: String,
    },
    DraftClear {
        room: Uuid,
    },
    Search {
        q: String,
        room: Option<Uuid>,
        start_time: Option<DateTime<Utc>>,
        end_time: Option<DateTime<Utc>>,
        sender_id: Option<Uuid>,
        content_type: Option<String>,
        limit: Option<u64>,
        offset: Option<u64>,
    },
    NotificationMarkRead {
        id: Uuid,
    },
    NotificationMarkAllRead {
        workspace_id: Option<Uuid>,
    },
    NotificationArchive {
        id: Uuid,
    },
    PresenceUpdate {
        status: UserPresenceStatus,
    },
    CustomStatusUpdate {
        emoji: Option<String>,
        text: Option<String>,
        expires_at: Option<DateTime<Utc>>,
    },
    InviteCreate {
        workspace: Uuid,
        room: Option<Uuid>,
        max_uses: Option<i32>,
        expires_at: Option<DateTime<Utc>>,
    },
    InviteAccept {
        code: String,
    },
    InviteRevoke {
        id: Uuid,
    },
    BanCreate {
        workspace: Uuid,
        user: Uuid,
        reason: Option<String>,
        expires_at: Option<DateTime<Utc>>,
    },
    BanRemove {
        workspace: Uuid,
        user: Uuid,
    },
    VoiceJoin {
        room: Uuid,
    },
    VoiceLeave {
        room: Uuid,
    },
    VoiceMute {
        room: Uuid,
        muted: bool,
    },
    VoiceDeaf {
        room: Uuid,
        deafened: bool,
    },
    ScreenShare {
        room: Uuid,
        start: bool,
    },
    UserSummary {
        username: String,
    },
    ConversationPin {
        room: Uuid,
        pin: bool,
    },
    ConversationMute {
        room: Uuid,
        mute: bool,
    },
    ConversationNotifyLevel {
        room: Uuid,
        notify_level: String,
    },
    ConversationList,
    MessageMarkRead {
        room: Uuid,
        message_ids: Vec<Uuid>,
    },
    MessageGetReaders {
        message_id: Uuid,
    },
    MessageStar {
        room: Uuid,
        message: Uuid,
        star: bool,
    },
    StarredList {
        room: Option<Uuid>,
        limit: Option<u64>,
    },
    MessageForward {
        source_message_id: Uuid,
        target_room: Uuid,
    },
    ArticleCreate {
        channel: Uuid,
        title: String,
        cover_url: Option<String>,
        content: String,
        content_type: Option<String>,
        summary: Option<String>,
        tags: Option<Vec<String>>,
        status: Option<String>,
    },
    ArticleUpdate {
        article_id: Uuid,
        title: Option<String>,
        cover_url: Option<String>,
        content: Option<String>,
        content_type: Option<String>,
        summary: Option<String>,
        tags: Option<Vec<String>>,
        is_pinned: Option<bool>,
        status: Option<String>,
    },
    ArticleDelete {
        article_id: Uuid,
    },
    ArticleList {
        channel: Uuid,
        before: Option<Uuid>,
        limit: Option<i64>,
    },
    ArticleGet {
        article_id: Uuid,
    },
    ArticleLike {
        article_id: Uuid,
        like: bool,
    },
    ArticleCommentCreate {
        article_id: Uuid,
        content: String,
        parent: Option<Uuid>,
    },
    ArticleCommentDelete {
        comment_id: Uuid,
    },
    ArticleCommentList {
        article_id: Uuid,
        before: Option<Uuid>,
        limit: Option<i64>,
    },
    ArticleLikedUsers {
        article_id: Uuid,
        before: Option<Uuid>,
        limit: Option<i64>,
    },
}

macro_rules! room_variants {
    ($self:expr, $($variant:ident),* $(,)?) => {
        match $self {
            $( Self::$variant { room, .. } => Some(*room), )*
            _ => None,
        }
    };
}

impl WsInMessage {
    pub fn room_id(&self) -> Option<Uuid> {
        room_variants!(
            self,
            Subscribe,
            Unsubscribe,
            TypingStart,
            TypingStop,
            ReadReceipt,
            MessageCreate,
            MessageList,
            RoomUpdate,
            RoomDelete,
            RoomGet,
            AccessGrant,
            AccessRevoke,
            StateSetReadSeq,
            StateUpdateDnd,
            MissedMessages,
            ReactionAdd,
            ReactionRemove,
            ThreadCreate,
            ThreadList,
            PinAdd,
            PinRemove,
            DraftSave,
            DraftClear,
            VoiceJoin,
            VoiceLeave,
            VoiceMute,
            VoiceDeaf,
            ScreenShare,
            ConversationPin,
            ConversationMute,
            ConversationNotifyLevel,
            MessageMarkRead,
            MessageStar,
        )
        .or_else(|| match self {
            Self::Search {
                room: Some(room), ..
            } => Some(*room),
            Self::MessageForward { target_room, .. } => Some(*target_room),
            _ => None,
        })
    }
}
