/// A decrypted inbound chat message ready for the SPA.
#[derive(Debug, Clone)]
pub struct ChatMessageEvent {
    pub peer_account_id: [u8; 32],
    pub message_id: String,
    pub timestamp: u64,
    pub content_type: ContentType,
    pub text: Option<String>,
    pub request_id: Option<String>,
    pub referenced_message_id: Option<String>,
    pub emoji: Option<String>,
    pub amount: Option<String>,
}

/// Content type discriminant from the chat-v1 wire format.
#[derive(Debug, Clone, PartialEq)]
pub enum ContentType {
    Text,
    ChatAccepted,
    ContactAdded,
    Reacted,
    ReactionRemoved,
    Reply,
    Edited,
    LeftChat,
    RichText,
    Payment,
    Token,
    DataChannelOffer,
    DataChannelAnswer,
    DataChannelIceCandidates,
    DataChannelClosed,
    /// Unrecognised discriminant — preserved for forward compatibility.
    Unknown(u8),
}

impl ContentType {
    /// Decode from the on-wire u8 discriminant.
    pub fn from_wire(byte: u8) -> Self {
        match byte {
            0x01 => Self::Text,
            0x02 => Self::ChatAccepted,
            0x03 => Self::ContactAdded,
            0x04 => Self::Reacted,
            0x05 => Self::ReactionRemoved,
            0x06 => Self::Reply,
            0x07 => Self::Edited,
            0x08 => Self::LeftChat,
            0x09 => Self::RichText,
            0x0A => Self::Payment,
            0x0B => Self::Token,
            0x0C => Self::DataChannelOffer,
            0x0D => Self::DataChannelAnswer,
            0x0E => Self::DataChannelIceCandidates,
            0x0F => Self::DataChannelClosed,
            other => Self::Unknown(other),
        }
    }

    /// Encode to the on-wire u8 discriminant.
    pub fn to_wire(&self) -> u8 {
        match self {
            Self::Text => 0x01,
            Self::ChatAccepted => 0x02,
            Self::ContactAdded => 0x03,
            Self::Reacted => 0x04,
            Self::ReactionRemoved => 0x05,
            Self::Reply => 0x06,
            Self::Edited => 0x07,
            Self::LeftChat => 0x08,
            Self::RichText => 0x09,
            Self::Payment => 0x0A,
            Self::Token => 0x0B,
            Self::DataChannelOffer => 0x0C,
            Self::DataChannelAnswer => 0x0D,
            Self::DataChannelIceCandidates => 0x0E,
            Self::DataChannelClosed => 0x0F,
            Self::Unknown(b) => *b,
        }
    }
}

/// Outbound delivery acknowledgement for a sent message.
#[derive(Debug, Clone)]
pub struct DeliveryStatusEvent {
    pub message_id: String,
    pub status: DeliveryStatus,
}

/// Current delivery state of a sent message.
#[derive(Debug, Clone, PartialEq)]
pub enum DeliveryStatus {
    Sent,
    Acknowledged,
    Failed,
}

/// Incoming chat request from a peer.
#[derive(Debug, Clone)]
pub struct ChatRequestEvent {
    pub peer_account_id: [u8; 32],
    pub request_id: String,
    pub welcome_message: Option<String>,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_content_type_roundtrips_all_known_variants() {
        let variants: &[(ContentType, u8)] = &[
            (ContentType::Text, 0x01),
            (ContentType::ChatAccepted, 0x02),
            (ContentType::ContactAdded, 0x03),
            (ContentType::Reacted, 0x04),
            (ContentType::ReactionRemoved, 0x05),
            (ContentType::Reply, 0x06),
            (ContentType::Edited, 0x07),
            (ContentType::LeftChat, 0x08),
            (ContentType::RichText, 0x09),
            (ContentType::Payment, 0x0A),
            (ContentType::Token, 0x0B),
            (ContentType::DataChannelOffer, 0x0C),
            (ContentType::DataChannelAnswer, 0x0D),
            (ContentType::DataChannelIceCandidates, 0x0E),
            (ContentType::DataChannelClosed, 0x0F),
        ];
        for (ct, wire) in variants {
            assert_eq!(ct.to_wire(), *wire, "to_wire mismatch for {ct:?}");
            assert_eq!(
                ContentType::from_wire(*wire),
                *ct,
                "from_wire mismatch for {ct:?}"
            );
        }
    }

    #[test]
    fn test_content_type_unknown_preserves_byte() {
        let unknown = ContentType::from_wire(0xFF);
        assert_eq!(unknown, ContentType::Unknown(0xFF));
        assert_eq!(unknown.to_wire(), 0xFF);
    }

    #[test]
    fn test_content_type_zero_is_unknown() {
        // 0x00 is unassigned — must not silently map to a known variant.
        assert_eq!(ContentType::from_wire(0x00), ContentType::Unknown(0x00));
    }
}
