//! WebSocket wire protocol for chat realtime transport.
//!
//! Client commands and server events mirror [`openapi-wss.yaml`](../../../openapi-wss.yaml):
//! - [`ClientWsCommand`] ↔ `ChatWsClientCommand` / `ChatWs*Command` schemas
//! - [`ServerWsEvent`] ↔ `ChatWsServerEvent` / `ChatWs*Event` schemas
//!
//! Durable chat writes remain on HTTP [`openapi.yaml`](../../../openapi.yaml) `/chat/*`; this
//! module covers subscription, resume, typing, presence, and read-cursor convenience signals.

use athena_chat::dto::results::{MessageView, ReactionSummary, RoomView};
use athena_chat::events::{ChatEventEnvelope, ChatEventPayload};
use athena_chat::ids::{RoomId, UserId};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

/// Inbound JSON command envelope sent by clients on `/wss/gateway`.
///
/// Each variant's `type` field matches a `ChatWs*Command` schema in `openapi-wss.yaml`.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum ClientWsCommand {
    #[serde(rename = "auth.hello")]
    AuthHello {
        token: Option<String>,
        room_subscriptions: Option<Vec<String>>,
    },
    #[serde(rename = "chat.subscribe")]
    ChatSubscribe {
        room_id: RoomId,
        from_seq: Option<i64>,
    },
    #[serde(rename = "chat.unsubscribe")]
    ChatUnsubscribe { room_id: RoomId },
    #[serde(rename = "chat.resume")]
    ChatResume { rooms: Vec<ResumeRoomCursor> },
    #[serde(rename = "chat.typing.start")]
    ChatTypingStart { room_id: RoomId },
    #[serde(rename = "chat.typing.stop")]
    ChatTypingStop { room_id: RoomId },
    #[serde(rename = "chat.presence.heartbeat")]
    ChatPresenceHeartbeat { active_room_id: Option<RoomId> },
    #[serde(rename = "chat.read.up_to")]
    ChatReadUpTo {
        room_id: RoomId,
        message_id: Option<Uuid>,
        seq: Option<i64>,
    },
    #[serde(rename = "ping")]
    Ping { at: String },
}

/// Per-room sequence cursor supplied in `chat.resume` (`ChatWsResumeCommand`).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResumeRoomCursor {
    pub room_id: RoomId,
    pub last_seq: i64,
}

/// Single user entry in a presence snapshot (`ChatWsPresenceUpdatedEvent`).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PresenceUser {
    pub user_id: UserId,
    pub state: String,
}

/// Aggregated presence for a room at a point in time.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct PresenceSnapshot {
    pub room_id: Option<RoomId>,
    pub users: Vec<PresenceUser>,
}

/// Outbound JSON event envelope pushed to subscribed WebSocket connections.
///
/// Each variant's `type` field matches a `ChatWs*Event` schema in `openapi-wss.yaml`.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum ServerWsEvent {
    #[serde(rename = "hello.ok")]
    HelloOk {
        connection_id: Uuid,
        server_time: DateTime<Utc>,
    },
    #[serde(rename = "chat.subscribed")]
    ChatSubscribed {
        room_id: RoomId,
        from_seq: Option<i64>,
    },
    #[serde(rename = "chat.room.created")]
    ChatRoomCreated {
        room_id: RoomId,
        event_id: Uuid,
        room: RoomView,
    },
    #[serde(rename = "chat.room.updated")]
    ChatRoomUpdated {
        room_id: RoomId,
        event_id: Uuid,
        room: RoomView,
    },
    #[serde(rename = "chat.room.archived")]
    ChatRoomArchived {
        room_id: RoomId,
        event_id: Uuid,
        room: RoomView,
    },
    #[serde(rename = "chat.message.created")]
    ChatMessageCreated {
        room_id: RoomId,
        seq: i64,
        event_id: Uuid,
        message: MessageView,
    },
    #[serde(rename = "chat.message.updated")]
    ChatMessageUpdated {
        room_id: RoomId,
        seq: i64,
        event_id: Uuid,
        message: MessageView,
    },
    #[serde(rename = "chat.message.deleted")]
    ChatMessageDeleted {
        room_id: RoomId,
        seq: i64,
        event_id: Uuid,
        message_id: Uuid,
    },
    #[serde(rename = "chat.read.updated")]
    ChatReadUpdated {
        room_id: RoomId,
        user_id: UserId,
        seq: i64,
        read_seq: i64,
    },
    #[serde(rename = "chat.members.updated")]
    ChatMembersUpdated {
        room_id: RoomId,
        seq: Option<i64>,
        version: i64,
    },
    #[serde(rename = "chat.typing.updated")]
    ChatTypingUpdated {
        room_id: RoomId,
        user_id: UserId,
        state: String,
    },
    #[serde(rename = "chat.presence.updated")]
    ChatPresenceUpdated {
        room_id: RoomId,
        users: Vec<PresenceUser>,
    },
    #[serde(rename = "chat.sync.required")]
    ChatSyncRequired {
        room_id: RoomId,
        reason: String,
        expected_from_seq: Option<i64>,
    },
    #[serde(rename = "chat.reaction.updated")]
    ChatReactionUpdated {
        room_id: RoomId,
        seq: Option<i64>,
        event_id: Uuid,
        summary: ReactionSummary,
    },
    #[serde(rename = "pong")]
    Pong { at: String },
    #[serde(rename = "error")]
    Error { code: String, message: String },
}

/// Maps a committed [`ChatEventEnvelope`] to the corresponding WSS server event, if relayable.
///
/// Returns `None` for payload types that have no direct WebSocket representation (for example
/// ephemeral-only signals).
pub fn server_event_from_chat_event(event: &ChatEventEnvelope) -> Option<ServerWsEvent> {
    match &event.payload {
        ChatEventPayload::RoomCreated { room } => Some(ServerWsEvent::ChatRoomCreated {
            room_id: event.room_id,
            event_id: event.event_id,
            room: room.clone(),
        }),
        ChatEventPayload::RoomUpdated { room } => Some(ServerWsEvent::ChatRoomUpdated {
            room_id: event.room_id,
            event_id: event.event_id,
            room: room.clone(),
        }),
        ChatEventPayload::RoomArchived { room } => Some(ServerWsEvent::ChatRoomArchived {
            room_id: event.room_id,
            event_id: event.event_id,
            room: room.clone(),
        }),
        ChatEventPayload::MessageCreated { message } => Some(ServerWsEvent::ChatMessageCreated {
            room_id: event.room_id,
            seq: event.room_seq.unwrap_or(message.room_seq),
            event_id: event.event_id,
            message: message.clone(),
        }),
        ChatEventPayload::MessageUpdated { message } => Some(ServerWsEvent::ChatMessageUpdated {
            room_id: event.room_id,
            seq: event.room_seq.unwrap_or(message.room_seq),
            event_id: event.event_id,
            message: message.clone(),
        }),
        ChatEventPayload::MessageDeleted { message_id } => {
            Some(ServerWsEvent::ChatMessageDeleted {
                room_id: event.room_id,
                seq: event.room_seq.unwrap_or_default(),
                event_id: event.event_id,
                message_id: *message_id,
            })
        }
        ChatEventPayload::ReadUpdated { user_id, read_seq } => {
            Some(ServerWsEvent::ChatReadUpdated {
                room_id: event.room_id,
                user_id: user_id.clone(),
                seq: event.room_seq.unwrap_or(*read_seq),
                read_seq: *read_seq,
            })
        }
        ChatEventPayload::MembersUpdated { version } => Some(ServerWsEvent::ChatMembersUpdated {
            room_id: event.room_id,
            seq: event.room_seq,
            version: *version,
        }),
        ChatEventPayload::ReactionUpdated { summary } => Some(ServerWsEvent::ChatReactionUpdated {
            room_id: event.room_id,
            seq: event.room_seq,
            event_id: event.event_id,
            summary: summary.clone(),
        }),
        _ => None,
    }
}
