//! API Objects used to [get updates](https://core.telegram.org/bots/api#getting-updates).

use serde::{Deserialize, Serialize};

use super::{
    inline_mode::{ChosenInlineResult, InlineQuery},
    macros::{apistruct, apply},
    payments::{PaidMediaPurchased, PreCheckoutQuery, ShippingQuery},
    types::{
        AllowedUpdate, BusinessConnection, BusinessMessagesDeleted, CallbackQuery,
        ChatBoostRemoved, ChatBoostUpdated, ChatJoinRequest, ChatMemberUpdated, ManagedBotUpdated,
        Message, MessageReactionCountUpdated, MessageReactionUpdated, Poll, PollAnswer,
    },
};

/// Represents an incoming update from telegram.
/// [Official documentation.](https://core.telegram.org/bots/api#update)
#[apply(apistruct!)]
pub struct Update {
    pub update_id: u32,

    /// Maps to exactly one of the many optional fields
    /// from [the official documentation](https://core.telegram.org/bots/api#update).
    #[serde(flatten)]
    pub content: UpdateContent,
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum UpdateContent {
    Message(Box<Message>),
    EditedMessage(Box<Message>),
    ChannelPost(Box<Message>),
    EditedChannelPost(Box<Message>),
    BusinessConnection(BusinessConnection),
    BusinessMessage(Box<Message>),
    EditedBusinessMessage(Box<Message>),
    DeletedBusinessMessages(BusinessMessagesDeleted),
    GuestMessage(Box<Message>),
    MessageReaction(MessageReactionUpdated),
    MessageReactionCount(MessageReactionCountUpdated),
    InlineQuery(InlineQuery),
    ChosenInlineResult(ChosenInlineResult),
    CallbackQuery(Box<CallbackQuery>),
    ShippingQuery(ShippingQuery),
    PreCheckoutQuery(PreCheckoutQuery),
    Poll(Box<Poll>),
    PollAnswer(PollAnswer),
    MyChatMember(ChatMemberUpdated),
    ChatMember(ChatMemberUpdated),
    ChatJoinRequest(ChatJoinRequest),
    ChatBoost(ChatBoostUpdated),
    RemovedChatBoost(ChatBoostRemoved),
    PurchasedPaidMedia(PaidMediaPurchased),
    ManagedBot(ManagedBotUpdated),
}

#[apply(apistruct!)]
#[derive(Eq)]
pub struct WebhookInfo {
    pub url: String,
    pub has_custom_certificate: bool,
    pub pending_update_count: u32,
    pub ip_address: Option<String>,
    pub last_error_date: Option<u64>,
    pub last_error_message: Option<String>,
    pub last_synchronization_error_date: Option<u64>,
    pub max_connections: Option<u16>,
    pub allowed_updates: Option<Vec<AllowedUpdate>>,
}

#[cfg(test)]
mod serde_tests {
    use super::*;
    use crate::types::{Chat, ChatType, User};

    #[test]
    pub fn update_content_is_flattened() {
        let update_content = r#"{
            "update_id": 2341,
            "message": {
                "message_id": 2746,
                "from": {
                    "id": 1276618370,
                    "is_bot": true,
                    "first_name": "test_el_bot",
                    "username": "el_mon_test_bot"
                },
                "date": 1618207352,
                "chat": {
                    "id": 275808073,
                    "type": "private",
                    "username": "Ayrat555",
                    "first_name": "Ayrat",
                    "last_name": "Badykov"
                },
                "text": "Hello!"
            }
        }"#;

        let update: Update = serde_json::from_str(update_content).unwrap();

        let message = Message::builder()
            .message_id(2746)
            .from(
                User::builder()
                    .id(1276618370)
                    .is_bot(true)
                    .first_name("test_el_bot")
                    .username("el_mon_test_bot")
                    .build(),
            )
            .date(1618207352)
            .chat(
                Chat::builder()
                    .id(275808073)
                    .type_field(ChatType::Private)
                    .username("Ayrat555")
                    .first_name("Ayrat")
                    .last_name("Badykov")
                    .build(),
            )
            .text("Hello!")
            .build();

        let expected = Update {
            update_id: 2341,
            content: UpdateContent::Message(Box::new(message)),
        };

        assert_eq!(update, expected);
    }
}
