use serde::{Deserialize, Deserializer, Serialize};
use chrono::{DateTime, Utc, TimeZone};

// Вспомогательная функция для конвертации миллисекунд в DateTime<Utc>
fn ms_to_datetime(ms: i64) -> DateTime<Utc> {
    Utc.timestamp_millis_opt(ms).unwrap()
}

// Вспомогательная функция для форматирования DateTime<Utc> в русский формат
fn format_datetime(dt: DateTime<Utc>) -> String {
    dt.format("%d.%m.%Y %H:%M:%S").to_string()
}

/// Пользователь MAX.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
    /// Уникальный идентификатор пользователя.
    pub user_id: i64,
    /// Имя.
    pub first_name: String,
    /// Фамилия (может отсутствовать).
    pub last_name: Option<String>,
    /// Пользовательское имя (может отсутствовать).
    pub username: Option<String>,
    /// Является ли пользователь ботом.
    pub is_bot: bool,
    /// Время последней активности (Unix timestamp в миллисекундах).
    pub last_activity_time: Option<i64>,
}

impl User {
    /// Возвращает время последней активности как `DateTime<Utc>`.
    pub fn last_activity_datetime(&self) -> Option<DateTime<Utc>> {
        self.last_activity_time.map(ms_to_datetime)
    }

    /// Возвращает время последней активности в формате `ДД.ММ.ГГГГ ЧЧ:ММ:СС`.
    pub fn last_activity_time_formatted(&self) -> Option<String> {
        self.last_activity_datetime().map(format_datetime)
    }
}

/// Информация о боте.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BotInfo {
    #[serde(flatten)]
    pub user: User,
    /// Описание бота.
    pub description: Option<String>,
    /// URL уменьшенного аватара.
    pub avatar_url: Option<String>,
    /// URL полноразмерного аватара.
    pub full_avatar_url: Option<String>,
    /// Список команд бота.
    #[serde(default)]
    pub commands: Vec<BotCommand>,
}

/// Команда бота.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BotCommand {
    /// Название команды (например, "/start").
    pub name: String,
    /// Описание команды.
    pub description: String,
}

/// Тип чата.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ChatTypeKind {
    /// Групповой чат.
    Chat,
    /// Диалог (личная переписка).
    Dialog,
    /// Неизвестный тип (для обратной совместимости).
    Unknown(String),
}

/// Статус чата.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ChatStatusKind {
    /// Бот является активным участником.
    Active,
    /// Бот был удалён из чата.
    Removed,
    /// Бот покинул чат.
    Left,
    /// Чат был закрыт.
    Closed,
    /// Неизвестный статус (для обратной совместимости).
    Unknown(String),
}

/// Права администратора чата.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ChatAdminPermissionKind {
    /// Читать все сообщения.
    ReadAllMessages,
    /// Добавлять/удалять участников.
    AddRemoveMembers,
    /// Добавлять администраторов.
    AddAdmins,
    /// Изменять информацию о чате.
    ChangeChatInfo,
    /// Закреплять сообщения.
    PinMessage,
    /// Писать сообщения.
    Write,
    /// Совершать звонки.
    CanCall,
    /// Изменять ссылку на чат.
    EditLink,
    /// Публиковать, редактировать и удалять сообщения.
    PostEditDeleteMessage,
    /// Редактировать сообщения.
    EditMessage,
    /// Удалять сообщения.
    DeleteMessage,
    /// Просматривать статистику.
    ViewStats,
    /// Удалять (устаревшее).
    Delete,
    /// Редактировать (устаревшее).
    Edit,
    /// Неизвестное право (для обратной совместимости).
    Unknown(String),
}

impl ChatTypeKind {
    pub fn from_str(s: &str) -> Self {
        match s {
            "chat" => ChatTypeKind::Chat,
            "dialog" => ChatTypeKind::Dialog,
            other => ChatTypeKind::Unknown(other.to_string()),
        }
    }

    pub fn as_str(&self) -> &str {
        match self {
            ChatTypeKind::Chat => "chat",
            ChatTypeKind::Dialog => "dialog",
            ChatTypeKind::Unknown(s) => s.as_str(),
        }
    }
}

impl ChatStatusKind {
    pub fn from_str(s: &str) -> Self {
        match s {
            "active" => ChatStatusKind::Active,
            "removed" => ChatStatusKind::Removed,
            "left" => ChatStatusKind::Left,
            "closed" => ChatStatusKind::Closed,
            other => ChatStatusKind::Unknown(other.to_string()),
        }
    }

    pub fn as_str(&self) -> &str {
        match self {
            ChatStatusKind::Active => "active",
            ChatStatusKind::Removed => "removed",
            ChatStatusKind::Left => "left",
            ChatStatusKind::Closed => "closed",
            ChatStatusKind::Unknown(s) => s.as_str(),
        }
    }
}

impl ChatAdminPermissionKind {
    pub fn from_str(s: &str) -> Self {
        match s {
            "read_all_messages" => ChatAdminPermissionKind::ReadAllMessages,
            "add_remove_members" => ChatAdminPermissionKind::AddRemoveMembers,
            "add_admins" => ChatAdminPermissionKind::AddAdmins,
            "change_chat_info" => ChatAdminPermissionKind::ChangeChatInfo,
            "pin_message" => ChatAdminPermissionKind::PinMessage,
            "write" => ChatAdminPermissionKind::Write,
            "can_call" => ChatAdminPermissionKind::CanCall,
            "edit_link" => ChatAdminPermissionKind::EditLink,
            "post_edit_delete_message" => ChatAdminPermissionKind::PostEditDeleteMessage,
            "edit_message" => ChatAdminPermissionKind::EditMessage,
            "delete_message" => ChatAdminPermissionKind::DeleteMessage,
            "view_stats" => ChatAdminPermissionKind::ViewStats,
            "delete" => ChatAdminPermissionKind::Delete,
            "edit" => ChatAdminPermissionKind::Edit,
            other => ChatAdminPermissionKind::Unknown(other.to_string()),
        }
    }

    pub fn as_str(&self) -> &str {
        match self {
            ChatAdminPermissionKind::ReadAllMessages => "read_all_messages",
            ChatAdminPermissionKind::AddRemoveMembers => "add_remove_members",
            ChatAdminPermissionKind::AddAdmins => "add_admins",
            ChatAdminPermissionKind::ChangeChatInfo => "change_chat_info",
            ChatAdminPermissionKind::PinMessage => "pin_message",
            ChatAdminPermissionKind::Write => "write",
            ChatAdminPermissionKind::CanCall => "can_call",
            ChatAdminPermissionKind::EditLink => "edit_link",
            ChatAdminPermissionKind::PostEditDeleteMessage => "post_edit_delete_message",
            ChatAdminPermissionKind::EditMessage => "edit_message",
            ChatAdminPermissionKind::DeleteMessage => "delete_message",
            ChatAdminPermissionKind::ViewStats => "view_stats",
            ChatAdminPermissionKind::Delete => "delete",
            ChatAdminPermissionKind::Edit => "edit",
            ChatAdminPermissionKind::Unknown(s) => s.as_str(),
        }
    }
}

/// Групповой чат.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Chat {
    /// Идентификатор чата.
    pub chat_id: i64,
    /// Тип чата.
    pub r#type: String,
    /// Статус чата.
    pub status: String,
    /// Название чата.
    pub title: Option<String>,
    /// Иконка чата.
    pub icon: Option<Image>,
    /// Время последнего события (Unix timestamp в миллисекундах).
    pub last_event_time: i64,
    /// Количество участников.
    pub participants_count: i32,
    /// ID владельца чата.
    pub owner_id: Option<i64>,
    /// Является ли чат публичным.
    pub is_public: bool,
    /// Ссылка на чат.
    pub link: Option<String>,
    /// Описание чата.
    pub description: Option<String>,
    /// Данные пользователя для диалога (только для чатов типа "dialog").
    pub dialog_with_user: Option<User>,
    /// ID сообщения с кнопкой, через которое был начат чат.
    pub chat_message_id: Option<String>,
    /// Закреплённое сообщение.
    pub pinned_message: Option<serde_json::Value>,
}

impl Chat {
    /// Возвращает тип чата как enum `ChatTypeKind`.
    pub fn type_kind(&self) -> ChatTypeKind {
        ChatTypeKind::from_str(&self.r#type)
    }

    /// Возвращает статус чата как enum `ChatStatusKind`.
    pub fn status_kind(&self) -> ChatStatusKind {
        ChatStatusKind::from_str(&self.status)
    }

    /// Возвращает время последнего события как `DateTime<Utc>`.
    pub fn last_event_datetime(&self) -> DateTime<Utc> {
        ms_to_datetime(self.last_event_time)
    }

    /// Возвращает время последнего события в формате `ДД.ММ.ГГГГ ЧЧ:ММ:СС`.
    pub fn last_event_time_formatted(&self) -> String {
        format_datetime(self.last_event_datetime())
    }
}

/// Изображение (аватар, иконка).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Image {
    /// URL изображения.
    pub url: String,
}

/// Участник чата (пользователь или бот).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatMember {
    #[serde(flatten)]
    pub user: User,
    /// Описание (для ботов).
    pub description: Option<String>,
    /// URL уменьшенного аватара.
    pub avatar_url: Option<String>,
    /// URL полноразмерного аватара.
    pub full_avatar_url: Option<String>,
    /// Время последнего доступа к чату (Unix timestamp в миллисекундах).
    pub last_access_time: i64,
    /// Является ли владельцем чата.
    pub is_owner: bool,
    /// Является ли администратором.
    pub is_admin: bool,
    /// Дата присоединения к чату (Unix timestamp в миллисекундах).
    pub join_time: i64,
    /// Права администратора (может быть пустым).
    #[serde(default)]
    pub permissions: Vec<String>,
    /// Псевдоним в чате.
    pub alias: Option<String>,
}

impl ChatMember {
    /// Возвращает права как вектор enum `ChatAdminPermissionKind`.
    pub fn permission_kinds(&self) -> Vec<ChatAdminPermissionKind> {
        self.permissions.iter().map(|s| ChatAdminPermissionKind::from_str(s)).collect()
    }

    /// Возвращает время последнего доступа как `DateTime<Utc>`.
    pub fn last_access_datetime(&self) -> DateTime<Utc> {
        ms_to_datetime(self.last_access_time)
    }

    /// Возвращает время последнего доступа в формате `ДД.ММ.ГГГГ ЧЧ:ММ:СС`.
    pub fn last_access_time_formatted(&self) -> String {
        format_datetime(self.last_access_datetime())
    }

    /// Возвращает дату присоединения как `DateTime<Utc>`.
    pub fn join_datetime(&self) -> DateTime<Utc> {
        ms_to_datetime(self.join_time)
    }

    /// Возвращает дату присоединения в формате `ДД.ММ.ГГГГ ЧЧ:ММ:СС`.
    pub fn join_time_formatted(&self) -> String {
        format_datetime(self.join_datetime())
    }
}

/// Сообщение.
#[derive(Debug, Clone, Default, Deserialize)]
pub struct Message {
    /// Отправитель.
    pub sender: Option<User>,
    /// Получатель.
    pub recipient: Recipient,
    /// Время отправки (Unix timestamp в миллисекундах).
    pub timestamp: i64,
    /// Тело сообщения.
    pub body: Option<MessageBody>,
    /// URL сообщения (для пересылки).
    pub url: Option<String>,
}

impl Message {
    /// Возвращает время отправки как `DateTime<Utc>`.
    pub fn timestamp_datetime(&self) -> DateTime<Utc> {
        ms_to_datetime(self.timestamp)
    }

    /// Возвращает время отправки в формате `ДД.ММ.ГГГГ ЧЧ:ММ:СС`.
    pub fn timestamp_formatted(&self) -> String {
        format_datetime(self.timestamp_datetime())
    }
}

/// Получатель сообщения.
#[derive(Debug, Clone, Serialize, Default, Deserialize)]
pub struct Recipient {
    /// ID чата (если сообщение в группу).
    pub chat_id: Option<i64>,
    /// ID пользователя (если личное сообщение).
    pub user_id: Option<i64>,
    /// Тип чата.
    pub chat_type: String,
}

/// Тело сообщения.
#[derive(Debug, Clone, Default, Deserialize)]
pub struct MessageBody {
    /// Уникальный идентификатор сообщения.
    pub mid: String,
    /// Порядковый номер сообщения в чате.
    pub seq: i64,
    /// Текст сообщения.
    pub text: Option<String>,
    /// Вложения (изображения, видео, стикеры, клавиатуры и т.д.)
    #[serde(default, deserialize_with = "deserialize_attachments")]
    pub attachments: Vec<AttachmentData>,
}

/// Вложение из API MAX.
#[derive(Debug, Clone, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum AttachmentData {
    Image { payload: MediaPayload },
    Video { payload: MediaPayload },
    Audio { payload: MediaPayload },
    File { payload: FilePayload },
    Sticker { payload: StickerPayload },
    InlineKeyboard { payload: KeyboardPayload },
    Location { payload: LocationPayload },
    Contact { payload: ContactPayload },
    #[serde(other)]
    Unknown,
}

/// Полезная нагрузка для медиа-вложений (изображение, видео, аудио).
#[derive(Debug, Clone, Deserialize)]
pub struct MediaPayload {
    pub token: Option<String>,
    pub url: Option<String>,
}

/// Полезная нагрузка для файлов.
#[derive(Debug, Clone, Deserialize)]
pub struct FilePayload {
    pub token: Option<String>,
    pub url: Option<String>,
    pub filename: Option<String>,
    pub size: Option<i64>,
}

/// Полезная нагрузка для стикеров.
#[derive(Debug, Clone, Deserialize)]
pub struct StickerPayload {
    pub code: String,
    pub url: Option<String>,
}

/// Полезная нагрузка для inline-клавиатуры.
#[derive(Debug, Clone, Deserialize)]
pub struct KeyboardPayload {
    pub buttons: Vec<Vec<serde_json::Value>>,
}

/// Полезная нагрузка для геолокации.
#[derive(Debug, Clone, Deserialize)]
pub struct LocationPayload {
    pub latitude: f64,
    pub longitude: f64,
}

/// Полезная нагрузка для контакта.
#[derive(Debug, Clone, Deserialize)]
pub struct ContactPayload {
    pub name: Option<String>,
    pub contact_id: Option<i64>,
    pub vcf_info: Option<String>,
    pub vcf_phone: Option<String>,
}

/// Десериализатор для поля `attachments`, который игнорирует некорректные элементы.
fn deserialize_attachments<'de, D>(deserializer: D) -> Result<Vec<AttachmentData>, D::Error>
where
    D: Deserializer<'de>,
{
    let raw: Option<Vec<serde_json::Value>> = Deserialize::deserialize(deserializer)?;
    match raw {
        Some(vec) => {
            let mut result = Vec::new();
            for value in vec {
                if let Ok(att) = serde_json::from_value(value) {
                    result.push(att);
                }
            }
            Ok(result)
        }
        None => Ok(Vec::new()),
    }
}

/// Контакт (устаревшее поле, оставлено для совместимости).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Contact {
    /// Имя контакта.
    pub name: String,
    /// Телефон.
    pub phone: String,
    /// VCF-информация.
    pub vcf_info: Option<String>,
}

/// Геолокация.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Location {
    /// Широта.
    pub latitude: f64,
    /// Долгота.
    pub longitude: f64,
}

/// Запрос функции обратного вызова при нажатии на строковую кнопку.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CallbackQuery {
    /// Уникальный идентификатор функции обратного вызова.
    pub callback_id: String,
    /// Время нажатия (Unix timestamp в миллисекундах).
    pub timestamp: i64,
    /// Пользователь, нажавший кнопку.
    pub user: User,
    /// Полезная нагрузка кнопки.
    pub payload: String,
}

impl CallbackQuery {
    /// Возвращает время нажатия как `DateTime<Utc>`.
    pub fn timestamp_datetime(&self) -> DateTime<Utc> {
        ms_to_datetime(self.timestamp)
    }

    /// Возвращает время нажатия в формате `ДД.ММ.ГГГГ ЧЧ:ММ:СС`.
    pub fn timestamp_formatted(&self) -> String {
        format_datetime(self.timestamp_datetime())
    }
}

/// Подписка на webhook.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Subscription {
    /// URL, на который отправляются обновления.
    pub url: String,
    /// Время создания подписки.
    pub time: i64,
    /// Типы обновлений, на которые оформлена подписка.
    pub update_types: Vec<String>,
}

impl Subscription {
    /// Возвращает время создания подписки как `DateTime<Utc>`.
    pub fn time_datetime(&self) -> DateTime<Utc> {
        ms_to_datetime(self.time)
    }

    /// Возвращает время создания подписки в формате `ДД.ММ.ГГГГ ЧЧ:ММ:СС`.
    pub fn time_formatted(&self) -> String {
        format_datetime(self.time_datetime())
    }
}

/// Параметры для обновления информации о чате.
#[derive(Debug, Clone, Default)]
pub struct UpdateChatInfo {
    /// Новое название чата.
    pub title: Option<String>,
    /// Новое описание.
    pub description: Option<String>,
    /// Токен загруженного изображения для иконки.
    pub icon_token: Option<String>,
    /// URL изображения для иконки.
    pub icon_url: Option<String>,
    /// Новая ссылка на чат.
    pub link: Option<String>,
    /// Изменить публичность чата.
    pub is_public: Option<bool>,
    /// MID сообщения для закрепления.
    pub pin: Option<String>,
    /// Отправлять ли уведомление об изменении.
    pub notify: Option<bool>,
}

// -----------------------------------------------------------------------------
// Обновление (событие).
// -----------------------------------------------------------------------------

/// Тип обновления (события), получаемого через long polling или webhook.
#[derive(Debug, Clone, Deserialize)]
#[serde(tag = "update_type", rename_all = "snake_case")]
pub enum Update {
    /// Новое сообщение.
    MessageCreated {
        timestamp: i64,
        message: Message,
    },
    /// Сообщение отредактировано.
    MessageEdited {
        timestamp: i64,
        message: Message,
    },
    /// Нажатие на inline-кнопку.
    MessageCallback {
        timestamp: i64,
        callback: CallbackQuery,
        message: Option<Message>,
        user_locale: Option<String>,
    },
    /// Бот запущен (команда /start или переход в диалог).
    BotStarted {
        timestamp: i64,
        chat_id: i64,
        user: User,
        payload: Option<String>,
        user_locale: Option<String>,
    },
    /// Бот добавлен в чат.
    BotAdded {
        timestamp: i64,
        chat_id: i64,
        user: User,
        is_channel: Option<bool>,
    },
    /// Бот удалён из чата.
    BotRemoved {
        timestamp: i64,
        chat_id: i64,
        user: User,
        is_channel: Option<bool>,
    },
    /// Пользователь добавлен в чат.
    UserAdded {
        timestamp: i64,
        chat_id: i64,
        user: User,
        inviter_id: Option<i64>,
        is_channel: Option<bool>,
    },
    /// Пользователь удалён из чата.
    UserRemoved {
        timestamp: i64,
        chat_id: i64,
        user: User,
        admin_id: Option<i64>,
        is_channel: Option<bool>,
    },
    /// Изменено название чата.
    ChatTitleChanged {
        timestamp: i64,
        chat_id: i64,
        user: User,
        title: String,
    },
    /// Неизвестный тип (для обратной совместимости).
    #[serde(other)]
    Unknown,
}

impl Update {
    pub fn timestamp(&self) -> i64 {
        match self {
            Update::MessageCreated { timestamp, .. } => *timestamp,
            Update::MessageEdited { timestamp, .. } => *timestamp,
            Update::MessageCallback { timestamp, .. } => *timestamp,
            Update::BotStarted { timestamp, .. } => *timestamp,
            Update::BotAdded { timestamp, .. } => *timestamp,
            Update::BotRemoved { timestamp, .. } => *timestamp,
            Update::UserAdded { timestamp, .. } => *timestamp,
            Update::UserRemoved { timestamp, .. } => *timestamp,
            Update::ChatTitleChanged { timestamp, .. } => *timestamp,
            Update::Unknown => 0,
        }
    }

    /// Возвращает время обновления как `DateTime<Utc>`.
    pub fn timestamp_datetime(&self) -> DateTime<Utc> {
        ms_to_datetime(self.timestamp())
    }

    /// Возвращает время обновления в формате `ДД.ММ.ГГГГ ЧЧ:ММ:СС`.
    pub fn timestamp_formatted(&self) -> String {
        format_datetime(self.timestamp_datetime())
    }
}

/// Тип обновления (строковый enum) – оставлен для обратной совместимости.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UpdateType {
    MessageCreated,
    MessageCallback,
    #[serde(other)]
    Unknown,
}
