use crate::attachments::{Attachment, AttachmentSource, InlineKeyboard};
use crate::client::MaxClient;
use crate::error::Result;
use crate::utils;
use serde_json::json;
use std::time::Duration;

// -----------------------------------------------------------------------------
// Параметры отправки сообщения (обычная структура)
// -----------------------------------------------------------------------------

/// Параметры для отправки сообщения.
///
/// Содержит все возможные настройки: получатель, текст, форматирование,
/// вложения, уведомления и т.д. Обычно создаётся через [`SendMessageParamsBuilder`].
#[derive(Debug, Clone, Default)]
pub struct SendMessageParams {
    /// Идентификатор чата (если сообщение в групповой чат).
    pub chat_id: Option<i64>,
    /// Идентификатор пользователя (если личное сообщение).
    pub user_id: Option<i64>,
    /// Текст сообщения.
    pub text: String,
    /// Формат текста: `"markdown"` или `"html"`.
    pub format: Option<String>,
    /// Отправлять ли уведомление (по умолчанию `true`).
    pub notify: Option<bool>,
    /// Отключить предпросмотр ссылок.
    pub disable_link_preview: Option<bool>,
    /// Идентификатор сообщения, на которое нужно ответить (`mid`).
    pub reply_mid: Option<String>,
    /// Список вложений.
    pub attachments: Vec<Attachment>,
    /// Максимальное количество повторных попыток при ошибках (по умолчанию 3).
    pub max_retries: usize,
}

// -----------------------------------------------------------------------------
// Builder для SendMessageParams
// -----------------------------------------------------------------------------

/// Построитель параметров для отправки сообщения.
///
/// Позволяет удобно задавать параметры цепочкой методов.
///
/// # Пример
///
/// ```no_run
/// use maxbot::{SendMessageParamsBuilder, Attachment};
///
/// let params = SendMessageParamsBuilder::new()
///     .text("Привет!")
///     .chat_id(123456789)
///     .format_markdown()
///     .attachment(Attachment::image_local("cat.jpg"))
///     .build();
/// ```
pub struct SendMessageParamsBuilder {
    chat_id: Option<i64>,
    user_id: Option<i64>,
    text: String,
    format: Option<String>,
    notify: Option<bool>,
    disable_link_preview: Option<bool>,
    reply_mid: Option<String>,
    attachments: Vec<Attachment>,
    max_retries: usize,
}

impl SendMessageParamsBuilder {
    /// Создаёт новый пустой построитель.
    pub fn new() -> Self {
        Self {
            text: String::new(),
            chat_id: None,
            user_id: None,
            format: None,
            notify: Some(true),
            disable_link_preview: None,
            reply_mid: None,
            attachments: vec![],
            max_retries: 3,
        }
    }

    /// Устанавливает текст сообщения.
    pub fn text(mut self, text: impl Into<String>) -> Self {
        self.text = text.into();
        self
    }

    /// Указывает идентификатор чата-получателя.
    pub fn chat_id(mut self, id: i64) -> Self {
        self.chat_id = Some(id);
        self
    }

    /// Указывает идентификатор пользователя-получателя (для личных сообщений).
    pub fn user_id(mut self, id: i64) -> Self {
        self.user_id = Some(id);
        self
    }

    /// Включает Markdown-форматирование текста.
    pub fn format_markdown(mut self) -> Self {
        self.format = Some("markdown".to_string());
        self
    }

    /// Включает HTML-форматирование текста.
    pub fn format_html(mut self) -> Self {
        self.format = Some("html".to_string());
        self
    }

    /// Отключает звуковое уведомление у получателя.
    pub fn silent(mut self) -> Self {
        self.notify = Some(false);
        self
    }

    /// Отключает предпросмотр ссылок в сообщении.
    pub fn disable_preview(mut self) -> Self {
        self.disable_link_preview = Some(true);
        self
    }

    /// Устанавливает сообщение, на которое нужно ответить.
    ///
    /// # Аргументы
    ///
    /// * `mid` - строковый идентификатор сообщения.
    pub fn reply_to(mut self, mid: impl Into<String>) -> Self {
        self.reply_mid = Some(mid.into());
        self
    }

    /// Добавляет одно вложение к сообщению.
    pub fn attachment(mut self, att: Attachment) -> Self {
        self.attachments.push(att);
        self
    }

    /// Заменяет все вложения переданным вектором.
    pub fn attachments(mut self, atts: Vec<Attachment>) -> Self {
        self.attachments = atts;
        self
    }

    /// Устанавливает максимальное число повторных попыток при ошибках отправки.
    pub fn max_retries(mut self, retries: usize) -> Self {
        self.max_retries = retries;
        self
    }

    /// Строит финальную структуру [`SendMessageParams`].
    pub fn build(self) -> SendMessageParams {
        SendMessageParams {
            chat_id: self.chat_id,
            user_id: self.user_id,
            text: self.text,
            format: self.format,
            notify: self.notify,
            disable_link_preview: self.disable_link_preview,
            reply_mid: self.reply_mid,
            attachments: self.attachments,
            max_retries: self.max_retries,
        }
    }
}

impl Default for SendMessageParamsBuilder {
    fn default() -> Self {
        Self::new()
    }
}

// -----------------------------------------------------------------------------
// Параметры для получения списка сообщений
// -----------------------------------------------------------------------------

/// Параметры для получения списка сообщений чата.
#[derive(Debug, Clone, Default)]
pub struct GetMessagesParams {
    /// Идентификатор чата.
    pub chat_id: i64,
    /// Маркер для постраничного вывода (обычно `mid` последнего сообщения из предыдущего ответа).
    pub marker: Option<String>,
    /// Количество сообщений (1–100).
    pub count: Option<u32>,
}

// -----------------------------------------------------------------------------
// Параметры для редактирования сообщения
// -----------------------------------------------------------------------------

/// Параметры для редактирования сообщения.
#[derive(Debug, Clone, Default)]
pub struct EditMessageParams {
    /// Новый текст сообщения.
    pub text: Option<String>,
    /// Формат текста: `"markdown"` или `"html"`.
    pub format: Option<String>,
    /// Новый список вложений (полностью заменяет существующие).
    pub attachments: Vec<Attachment>,
    /// Новая inline-клавиатура (если требуется заменить).
    pub inline_keyboard: Option<InlineKeyboard>,
    /// Отключить предпросмотр ссылок.
    pub disable_link_preview: Option<bool>,
}

// -----------------------------------------------------------------------------
// Вспомогательная функция для подготовки вложений
// -----------------------------------------------------------------------------

/// Подготавливает вложения для включения в JSON тела запроса.
/// Для файловых типов, если передан локальный путь, файл загружается и токен получается.
/// Возвращает JSON-массив вложений.
pub(crate) async fn prepare_attachments_json(
    client: &MaxClient,
    attachments: &[Attachment],
) -> Result<serde_json::Value> {
    let mut attachments_json = Vec::new();

    for att in attachments {
        match att {
            Attachment::Image { source, .. }
            | Attachment::Video { source, .. }
            | Attachment::Audio { source, .. }
            | Attachment::File { source, .. } => {
                let token = match source {
                    AttachmentSource::LocalFile(path) => {
                        let typ = att.get_type();
                        let tok = client.upload_file(path, typ).await?;
                        tokio::time::sleep(Duration::from_millis(500)).await;
                        tok
                    }
                    AttachmentSource::Token(tok) => tok.clone(),
                };
                attachments_json.push(serde_json::json!({
                    "type": att.get_type(),
                    "payload": { "token": token }
                }));
            }
            Attachment::Sticker { code } => {
                attachments_json.push(serde_json::json!({
                    "type": "sticker",
                    "payload": { "code": code }
                }));
            }
            Attachment::Contact(data) => {
                let mut payload = serde_json::Map::new();
                if let Some(name) = &data.name {
                    payload.insert("name".into(), name.clone().into());
                }
                if let Some(contact_id) = data.contact_id {
                    payload.insert("contact_id".into(), contact_id.into());
                }
                if let Some(vcf_info) = &data.vcf_info {
                    payload.insert("vcf_info".into(), vcf_info.clone().into());
                }
                if let Some(vcf_phone) = &data.vcf_phone {
                    payload.insert("vcf_phone".into(), vcf_phone.clone().into());
                }
                attachments_json.push(serde_json::json!({
                    "type": "contact",
                    "payload": payload
                }));
            }
            Attachment::InlineKeyboard(keyboard) => {
                let mut buttons_json = Vec::new();
                for row in &keyboard.buttons {
                    let mut row_json = Vec::new();
                    for btn in row {
                        let mut btn_json = serde_json::Map::new();
                        btn_json.insert("type".into(), btn.r#type.clone().into());
                        btn_json.insert("text".into(), btn.text.clone().into());
                        if let Some(payload) = &btn.payload {
                            btn_json.insert("payload".into(), payload.clone().into());
                        }
                        if let Some(url) = &btn.url {
                            btn_json.insert("url".into(), url.clone().into());
                        }
                        if let Some(quick) = btn.quick {
                            btn_json.insert("quick".into(), quick.into());
                        }
                        if let Some(web_app) = &btn.web_app {
                            btn_json.insert("web_app".into(), web_app.clone().into());
                        }
                        if let Some(contact_id) = btn.contact_id {
                            btn_json.insert("contact_id".into(), contact_id.into());
                        }
                        if let Some(app_payload) = &btn.app_payload {
                            btn_json.insert("payload".into(), app_payload.clone().into());
                        }
                        if let Some(msg_text) = &btn.message_text {
                            btn_json.insert("text".into(), msg_text.clone().into());
                        }
                        row_json.push(serde_json::Value::Object(btn_json));
                    }
                    buttons_json.push(serde_json::Value::Array(row_json));
                }
                attachments_json.push(serde_json::json!({
                    "type": "inline_keyboard",
                    "payload": { "buttons": buttons_json }
                }));
            }
            Attachment::Location { latitude, longitude } => {
                attachments_json.push(serde_json::json!({
                    "type": "location",
                    "payload": { "latitude": latitude, "longitude": longitude }
                }));
            }
            Attachment::Share(data) => {
                let mut payload = serde_json::Map::new();
                if let Some(url) = &data.url {
                    payload.insert("url".into(), url.clone().into());
                }
                if let Some(token) = &data.token {
                    payload.insert("token".into(), token.clone().into());
                }
                attachments_json.push(serde_json::json!({
                    "type": "share",
                    "payload": payload
                }));
            }
        }
    }

    Ok(serde_json::Value::Array(attachments_json))
}

// -----------------------------------------------------------------------------
// Реализация методов MaxClient
// -----------------------------------------------------------------------------

impl MaxClient {
    /// Отправляет сообщение с разбиением длинного текста, поддержкой вложений и
    /// автоматическим откатом в случае ошибки.
    ///
    /// Если текст превышает [`MAX_MESSAGE_LEN`][crate::utils::MAX_MESSAGE_LEN], он
    /// разбивается на несколько фрагментов. Вложения (изображения, видео и т.д.)
    /// прикрепляются только к первому фрагменту, inline‑клавиатура — к последнему.
    ///
    /// # Возвращаемое значение
    ///
    /// Вектор идентификаторов (`mid`) всех отправленных фрагментов сообщения.
    ///
    /// # Ошибки
    ///
    /// Если при отправке какого‑либо фрагмента происходит ошибка, все уже отправленные
    /// фрагменты удаляются, и возвращается ошибка.
    pub async fn send_message(&self, params: SendMessageParams) -> Result<Vec<String>> {
        // Проверка получателя
        if (params.chat_id.is_none() && params.user_id.is_none()) ||
           (params.chat_id.is_some() && params.user_id.is_some()) {
            return Err(crate::error::Error::InvalidInput(
                "Exactly one of chat_id or user_id must be specified".into(),
            ));
        }

        // Подготовка вложений
        let mut ready_attachments = Vec::new();    // (type, token) – файловые
        let mut content_attachments = Vec::new();  // (type, payload) – sticker, contact, location, share
        let mut keyboard_attachments = Vec::new(); // (type, payload) – inline_keyboard

        for att in &params.attachments {
            match att {
                // Файловые типы
                Attachment::Image { source, .. } |
                Attachment::Video { source, .. } |
                Attachment::Audio { source, .. } |
                Attachment::File { source, .. } => {
                    let token = match source {
                        AttachmentSource::LocalFile(path) => {
                            let file_type = att.get_type();
                            let tok = self.upload_file(path, file_type).await?;
                            tokio::time::sleep(Duration::from_millis(500)).await; // пауза для обработки
                            tok
                        }
                        AttachmentSource::Token(tok) => tok.clone(),
                    };
                    ready_attachments.push((att.get_type(), token));
                }
                // Стикер
                Attachment::Sticker { code } => {
                    let payload = json!({ "code": code });
                    content_attachments.push((att.get_type(), json!({ "payload": payload })));
                }
                // Контакт
                Attachment::Contact(data) => {
                    let mut payload = serde_json::Map::new();
                    if let Some(name) = &data.name {
                        payload.insert("name".into(), name.clone().into());
                    }
                    if let Some(contact_id) = data.contact_id {
                        payload.insert("contact_id".into(), contact_id.into());
                    }
                    if let Some(vcf_info) = &data.vcf_info {
                        payload.insert("vcf_info".into(), vcf_info.clone().into());
                    }
                    if let Some(vcf_phone) = &data.vcf_phone {
                        payload.insert("vcf_phone".into(), vcf_phone.clone().into());
                    }
                    content_attachments.push((att.get_type(), json!({ "payload": payload })));
                }
                // Inline-клавиатура
                Attachment::InlineKeyboard(keyboard) => {
                    let mut buttons_json = Vec::new();
                    for row in &keyboard.buttons {
                        let mut row_json = Vec::new();
                        for btn in row {
                            let mut btn_json = serde_json::Map::new();
                            btn_json.insert("type".into(), btn.r#type.clone().into());
                            btn_json.insert("text".into(), btn.text.clone().into());
                            if let Some(payload) = &btn.payload {
                                btn_json.insert("payload".into(), payload.clone().into());
                            }
                            if let Some(url) = &btn.url {
                                btn_json.insert("url".into(), url.clone().into());
                            }
                            if let Some(quick) = btn.quick {
                                btn_json.insert("quick".into(), quick.into());
                            }
                            if let Some(web_app) = &btn.web_app {
                                btn_json.insert("web_app".into(), web_app.clone().into());
                            }
                            if let Some(contact_id) = btn.contact_id {
                                btn_json.insert("contact_id".into(), contact_id.into());
                            }
                            if let Some(app_payload) = &btn.app_payload {
                                btn_json.insert("payload".into(), app_payload.clone().into());
                            }
                            if let Some(msg_text) = &btn.message_text {
                                btn_json.insert("text".into(), msg_text.clone().into());
                            }
                            row_json.push(serde_json::Value::Object(btn_json));
                        }
                        buttons_json.push(serde_json::Value::Array(row_json));
                    }
                    let payload = json!({ "buttons": buttons_json });
                    keyboard_attachments.push((att.get_type(), json!({ "payload": payload })));
                }
                // Геолокация
                Attachment::Location { latitude, longitude } => {
                    let payload = json!({ "latitude": latitude, "longitude": longitude });
                    content_attachments.push((att.get_type(), payload));
                }
                // Отсылка (share)
                Attachment::Share(data) => {
                    let mut payload = serde_json::Map::new();
                    if let Some(url) = &data.url {
                        payload.insert("url".into(), url.clone().into());
                    }
                    if let Some(token) = &data.token {
                        payload.insert("token".into(), token.clone().into());
                    }
                    content_attachments.push((att.get_type(), json!({ "payload": payload })));
                }
            }
        }

        // Разбивка текста на фрагменты
        let fragments = utils::split_text(&params.text, params.format.as_deref());
        let fragments = if fragments.is_empty() &&
            ready_attachments.is_empty() &&
            content_attachments.is_empty()
        {
            return Err(crate::error::Error::InvalidInput(
                "Empty message text and no attachments".into(),
            ));
        } else if fragments.is_empty() {
            vec!["".to_string()]
        } else {
            fragments
        };

        // GET-параметры (передаются в URL)
        let mut get_params = Vec::new();
        if let Some(disable) = params.disable_link_preview {
            get_params.push(("disable_link_preview", disable.to_string()));
        }

        // Формирование link для reply (если задан)
        let reply_link = params
            .reply_mid
            .as_ref()
            .map(|mid| json!({ "type": "reply", "mid": mid }));

        let mut sent_ids = Vec::new();

        for (i, fragment_text) in fragments.iter().enumerate() {
            let mut body = json!({
                "text": fragment_text,
            });
            if let Some(fmt) = &params.format {
                body["format"] = json!(fmt);
            }
            if let Some(notify) = params.notify {
                body["notify"] = json!(notify);
            }

            // Первый фрагмент: файловые вложения + вложения содержимого + reply
            if i == 0 {
                if !body["attachments"].is_array() {
                    body["attachments"] = serde_json::Value::Array(vec![]);
                }
                let attachments_array = body["attachments"].as_array_mut().unwrap();
                for (att_type, token) in &ready_attachments {
                    attachments_array.push(json!({
                        "type": att_type,
                        "payload": { "token": token }
                    }));
                }
                for (att_type, payload) in &content_attachments {
                    let mut attachment = json!({ "type": att_type });
                    if let Some(p) = payload.as_object() {
                        for (k, v) in p {
                            attachment[k] = v.clone();
                        }
                    } else {
                        attachment["payload"] = payload.clone();
                    }
                    attachments_array.push(attachment);
                }
                if let Some(link) = &reply_link {
                    body["link"] = link.clone();
                }
            }
            // Последний фрагмент: клавиатура (если есть)
            if i == fragments.len() - 1 && !keyboard_attachments.is_empty() {
                if !body["attachments"].is_array() {
                    body["attachments"] = serde_json::Value::Array(vec![]);
                }
                let attachments_array = body["attachments"].as_array_mut().unwrap();
                for (att_type, payload) in &keyboard_attachments {
                    let mut attachment = json!({ "type": att_type });
                    if let Some(p) = payload.as_object() {
                        for (k, v) in p {
                            attachment[k] = v.clone();
                        }
                    } else {
                        attachment["payload"] = payload.clone();
                    }
                    attachments_array.push(attachment);
                }
            }

            match self.send_fragment_with_retry(
                params.chat_id,
                params.user_id,
                body,
                &get_params,
                i == 0, // is_first
                params.max_retries,
            ).await {
                Ok(mid) => sent_ids.push(mid),
                Err(e) => {
                    // Откат: удалить все успешно отправленные фрагменты
                    if !sent_ids.is_empty() {
                        let _ = self.delete_messages(&sent_ids).await;
                    }
                    return Err(e);
                }
            }
        }

        Ok(sent_ids)
    }

    /// Удобный метод для отправки сообщения с использованием построителя.
    ///
    /// Эквивалентен вызову `send_message(builder.build())`.
    pub async fn send_message_builder(
        &self,
        builder: SendMessageParamsBuilder,
    ) -> Result<Vec<String>> {
        self.send_message(builder.build()).await
    }

    /// Пересылает существующее сообщение в другой чат или пользователю.
    ///
    /// # Аргументы
    ///
    /// * `chat_id` / `user_id` — получатель (должен быть указан ровно один).
    /// * `forward_mid` — строковый идентификатор (`mid`) пересылаемого сообщения.
    /// * `notify` — отправлять ли уведомление (по умолчанию `true`).
    /// * `disable_link_preview` — отключить предпросмотр ссылок.
    ///
    /// # Возвращаемое значение
    ///
    /// Идентификатор (`mid`) нового пересланного сообщения.
    pub async fn forward_message(
        &self,
        chat_id: Option<i64>,
        user_id: Option<i64>,
        forward_mid: &str,
        notify: Option<bool>,
        disable_link_preview: Option<bool>,
    ) -> Result<String> {
        // Проверка получателя
        if (chat_id.is_none() && user_id.is_none()) ||
           (chat_id.is_some() && user_id.is_some()) {
            return Err(crate::error::Error::InvalidInput(
                "Exactly one of chat_id or user_id must be specified".into(),
            ));
        }

        let mut query = Vec::new();
        if let Some(cid) = chat_id {
            query.push(("chat_id", cid.to_string()));
        }
        if let Some(uid) = user_id {
            query.push(("user_id", uid.to_string()));
        }
        if let Some(disable) = disable_link_preview {
            query.push(("disable_link_preview", disable.to_string()));
        }
        // Обязательный параметр: message_id (строковый mid)
        query.push(("message_id", forward_mid.to_string()));

        let body = json!({
            "link": {
                "type": "forward",
                "mid": forward_mid
            },
            "notify": notify.unwrap_or(true),
        });

        let resp: serde_json::Value = self.request_with_rate_limit(
            reqwest::Method::POST,
            "/messages",
            &query,
            Some(body),
        ).await?;

        let new_mid = resp["message"]["body"]["mid"]
            .as_str()
            .ok_or_else(|| crate::error::Error::Api {
                code: 500,
                message: "No message ID in forward response".into(),
            })?;
        Ok(new_mid.to_string())
    }

    /// Удаляет одно сообщение по его идентификатору `mid`.
    pub async fn delete_message(&self, message_id: &str) -> Result<()> {
        self.request_with_rate_limit::<serde_json::Value>(
            reqwest::Method::DELETE,
            "/messages",
            &[("message_id", message_id.to_string())],
            None,
        ).await?;
        Ok(())
    }

    /// Удаляет несколько сообщений последовательно.
    ///
    /// Ошибки при удалении отдельных сообщений игнорируются (выводится предупреждение в stderr).
    pub async fn delete_messages(&self, message_ids: &[String]) -> Result<()> {
        for id in message_ids {
            if let Err(e) = self.delete_message(id).await {
                eprintln!("Warning: failed to delete message {}: {}", id, e);
            }
        }
        Ok(())
    }

    /// Возвращает постраничный список сообщений из указанного чата.
    ///
    /// Сообщения возвращаются в обратном хронологическом порядке (от новых к старым).
    ///
    /// # Аргументы
    ///
    /// * `params` - параметры запроса ([`GetMessagesParams`]).
    ///
    /// # Возвращаемое значение
    ///
    /// Кортеж `(сообщения, следующий_маркер)`. Если `следующий_маркер` равен `None`,
    /// достигнут конец истории чата.
    ///
    /// # Пример
    ///
    /// ```no_run
    /// # use maxbot::{MaxClient, GetMessagesParams};
    /// # async fn example(client: &MaxClient) -> Result<(), Box<dyn std::error::Error>> {
    /// let (messages, next_marker) = client.get_messages(GetMessagesParams {
    ///     chat_id: 123456789,
    ///     count: Some(50),
    ///     ..Default::default()
    /// }).await?;
    /// # Ok(())
    /// # }
    /// ```
    pub async fn get_messages(
        &self,
        params: GetMessagesParams,
    ) -> Result<(Vec<crate::types::Message>, Option<String>)> {
        let mut query = vec![("chat_id", params.chat_id.to_string())];
        if let Some(m) = &params.marker {
            query.push(("marker", m.clone()));
        }
        if let Some(c) = params.count {
            query.push(("count", c.to_string()));
        }
        let resp: serde_json::Value = self
            .request_with_rate_limit(reqwest::Method::GET, "/messages", &query, None)
            .await?;
        let messages: Vec<crate::types::Message> =
            serde_json::from_value(resp["messages"].clone())?;
        let next_marker = resp
            .get("marker")
            .and_then(|v| v.as_str())
            .map(String::from);
        Ok((messages, next_marker))
    }

    /// Редактирует ранее отправленное сообщение.
    ///
    /// Можно изменить текст, форматирование, вложения и inline‑клавиатуру.
    /// Поля, оставленные как `None`, не изменяются.
    ///
    /// # Аргументы
    ///
    /// * `message_mid` - строковый идентификатор (`mid`) редактируемого сообщения.
    /// * `params` - параметры редактирования ([`EditMessageParams`]).
    ///
    /// # Пример
    ///
    /// ```no_run
    /// # use maxbot::{MaxClient, EditMessageParams};
    /// # async fn example(client: &MaxClient) -> Result<(), Box<dyn std::error::Error>> {
    /// let params = EditMessageParams {
    ///     text: Some("Обновлённый текст".to_string()),
    ///     ..Default::default()
    /// };
    /// client.edit_message("abc123mid", params).await?;
    /// # Ok(())
    /// # }
    /// ```
    pub async fn edit_message(&self, message_mid: &str, params: EditMessageParams) -> Result<()> {
        let mut body = serde_json::Map::new();
        if let Some(text) = params.text {
            body.insert("text".into(), text.into());
        }
        if let Some(format) = params.format {
            body.insert("format".into(), format.into());
        }
        if let Some(disable) = params.disable_link_preview {
            body.insert("disable_link_preview".into(), disable.into());
        }

        // Собираем все вложения: attachments + inline_keyboard (если есть)
        let mut all_attachments = params.attachments;
        if let Some(keyboard) = params.inline_keyboard {
            all_attachments.push(Attachment::InlineKeyboard(keyboard));
        }

        if !all_attachments.is_empty() {
            let attachments_json = prepare_attachments_json(self, &all_attachments).await?;
            body.insert("attachments".into(), attachments_json);
        }

        self.request_with_rate_limit::<serde_json::Value>(
            reqwest::Method::PUT,
            "/messages",
            &[("message_id", message_mid.to_string())],
            Some(serde_json::Value::Object(body)),
        )
        .await?;
        Ok(())
    }

    /// Редактирует длинное сообщение, представленное несколькими фрагментами.
    ///
    /// Новый текст разбивается на фрагменты (как при отправке). Затем:
    /// - Существующие фрагменты обновляются через `edit_message`.
    /// - Если старых фрагментов больше, лишние удаляются.
    /// - Если новых больше, недостающие отправляются через `send_message`.
    ///
    /// # Аргументы
    /// * `chat_id` – идентификатор чата (необходим для отправки новых фрагментов).
    /// * `old_mids` – список идентификаторов текущих фрагментов (в порядке следования).
    /// * `new_text` – новый текст сообщения (может быть любой длины).
    /// * `format` – формат текста (`"markdown"` или `"html"`).
    /// * `notify` – отправлять ли уведомление.
    /// * `disable_link_preview` – отключить предпросмотр ссылок.
    /// * `reply_mid` – идентификатор сообщения, на которое следует ответить (новые фрагменты).
    /// * `attachments` – вложения (будут прикреплены только к первому фрагменту).
    /// * `inline_keyboard` – клавиатура (будет прикреплена к последнему фрагменту).
    ///
    /// # Возвращает
    /// Вектор идентификаторов фрагментов после редактирования.
    ///
    /// # Пример
    /// ```no_run
    /// # use maxbot::MaxClient;
    /// # async fn example(client: &MaxClient, chat_id: i64, old_mids: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
    /// let new_ids = client.edit_multimessage(
    ///     chat_id,
    ///     &old_mids,
    ///     "Новый очень длинный текст…",
    ///     Some("markdown"),
    ///     None,
    ///     None,
    ///     None,
    ///     vec![],
    ///     None,
    /// ).await?;
    /// # Ok(())
    /// # }
    /// ```
    pub async fn edit_multimessage(
        &self,
        chat_id: i64,
        old_mids: &[String],
        new_text: &str,
        format: Option<&str>,
        notify: Option<bool>,
        disable_link_preview: Option<bool>,
        reply_mid: Option<&str>,
        attachments: Vec<Attachment>,
        inline_keyboard: Option<InlineKeyboard>,
    ) -> Result<Vec<String>> {
        // 1. Разбить новый текст на фрагменты
        let fragments = utils::split_text(new_text, format);
        if fragments.is_empty() {
            return Err(crate::error::Error::InvalidInput(
                "Empty message text after splitting".into(),
            ));
        }

        let old_count = old_mids.len();
        let new_count = fragments.len();

        // 2. Подготовить вложения для первого фрагмента и клавиатуру для последнего
        let first_attachments_json = if !attachments.is_empty() {
            Some(prepare_attachments_json(self, &attachments).await?)
        } else {
            None
        };
        let keyboard_attachment_json = if let Some(keyboard) = inline_keyboard {
            let kb_att = Attachment::InlineKeyboard(keyboard);
            let json_arr = prepare_attachments_json(self, &[kb_att]).await?;
            // Ожидаем массив с одним элементом
            if let serde_json::Value::Array(mut arr) = json_arr {
                arr.pop()
            } else {
                None
            }
        } else {
            None
        };

        let mut new_ids = Vec::with_capacity(new_count);

        // 3. Редактирование существующих фрагментов (до min(old, new))
        for i in 0..std::cmp::min(old_count, new_count) {
            let mut body = serde_json::Map::new();
            body.insert("text".into(), fragments[i].clone().into());
            if let Some(fmt) = format {
                body.insert("format".into(), fmt.into());
            }
            if let Some(disable) = disable_link_preview {
                body.insert("disable_link_preview".into(), disable.into());
            }

            let mut final_attachments = Vec::new();
            if i == 0 {
                if let Some(ref arr) = first_attachments_json {
                    if let serde_json::Value::Array(arr_vec) = arr {
                        final_attachments.extend(arr_vec.clone());
                    }
                }
            }
            if i == new_count - 1 {
                if let Some(kb) = &keyboard_attachment_json {
                    final_attachments.push(kb.clone());
                }
            }
            if !final_attachments.is_empty() {
                body.insert("attachments".into(), serde_json::Value::Array(final_attachments));
            }

            let query = [("message_id", old_mids[i].to_string())];
            self.request_with_rate_limit::<serde_json::Value>(
                reqwest::Method::PUT,
                "/messages",
                &query,
                Some(serde_json::Value::Object(body)),
            )
            .await?;
            new_ids.push(old_mids[i].clone());
        }

        // 4. Удаление лишних старых фрагментов
        if old_count > new_count {
            let extra_mids = &old_mids[new_count..];
            self.delete_messages(extra_mids).await?;
        }

        // 5. Отправка дополнительных фрагментов (если новых больше)
        if new_count > old_count {
            let extra_fragments = &fragments[old_count..];
            for (i, text) in extra_fragments.iter().enumerate() {
                let mut builder = SendMessageParamsBuilder::new()
                    .text(text)
                    .chat_id(chat_id)
                    .max_retries(3);
                if let Some(fmt) = format {
                    if fmt == "markdown" {
                        builder = builder.format_markdown();
                    } else if fmt == "html" {
                        builder = builder.format_html();
                    }
                }
                if let Some(notify_val) = notify {
                    if !notify_val {
                        builder = builder.silent();
                    }
                }
                if let Some(disable) = disable_link_preview {
                    if disable {
                        builder = builder.disable_preview();
                    }
                }
                // Для самого первого из новых фрагментов добавляем reply, если указан
                if i == 0 && reply_mid.is_some() {
                    builder = builder.reply_to(reply_mid.unwrap());
                }
                let sent_ids = self.send_message_builder(builder).await?;
                new_ids.extend(sent_ids);
            }
        }

        Ok(new_ids)
    }

    // -------------------------------------------------------------------------
    // Внутренние вспомогательные методы
    // -------------------------------------------------------------------------

    async fn send_fragment_with_retry(
        &self,
        chat_id: Option<i64>,
        user_id: Option<i64>,
        body: serde_json::Value,
        get_params: &[(&str, String)],
        is_first: bool,
        max_retries: usize,
    ) -> Result<String> {
        let mut attempt = 0;
        let mut delay_sec = 1;            // для ошибки attachment.not.ready
        let mut rate_limit_delay_sec = 5; // для ошибки 429

        loop {
            match self
                .send_single_fragment(chat_id, user_id, body.clone(), get_params)
                .await
            {
                Ok(mid) => return Ok(mid),
                Err(e) => {
                    if !is_first {
                        // Для не-первого фрагмента не повторяем при ошибках вложений
                        return Err(e);
                    }
                    attempt += 1;
                    if attempt >= max_retries {
                        return Err(e);
                    }
                    let err_str = e.to_string();
                    if err_str.contains("429") || err_str.contains("too.many.requests") {
                        tokio::time::sleep(Duration::from_secs(rate_limit_delay_sec)).await;
                        rate_limit_delay_sec *= 2;
                        continue;
                    }
                    if err_str.contains("attachment.not.ready") || err_str.contains("not.processed") {
                        tokio::time::sleep(Duration::from_secs(delay_sec)).await;
                        delay_sec *= 2;
                        continue;
                    }
                    return Err(e);
                }
            }
        }
    }

    async fn send_single_fragment(
        &self,
        chat_id: Option<i64>,
        user_id: Option<i64>,
        body: serde_json::Value,
        get_params: &[(&str, String)],
    ) -> Result<String> {
        let mut query = get_params.to_vec();
        if let Some(cid) = chat_id {
            query.push(("chat_id", cid.to_string()));
        }
        if let Some(uid) = user_id {
            query.push(("user_id", uid.to_string()));
        }

        let resp: serde_json::Value = self.request_with_rate_limit(
            reqwest::Method::POST,
            "/messages",
            &query,
            Some(body),
        ).await?;

        let mid = resp["message"]["body"]["mid"]
            .as_str()
            .ok_or_else(|| crate::error::Error::Api {
                code: 500,
                message: "No message ID in response (expected message.body.mid)".into(),
            })?;
        Ok(mid.to_string())
    }
}
