// Auto-generated by crates/tele-codegen. Do not edit manually.
use serde::Serialize;
use serde_json::Value;

use crate::{Error, Result};

use crate::types::validation::{
    positive_i64 as validate_positive_i64, required_string as validate_required_string,
    string_id as validate_string_id,
};

use super::AdvancedRequest;

trait GeneratedValidate {
    fn validate_generated(&self) -> Result<()>;
}

impl GeneratedValidate for crate::types::payment::LabeledPrice {
    fn validate_generated(&self) -> Result<()> {
        self.validate()
    }
}

impl GeneratedValidate for crate::types::payment::ShippingOption {
    fn validate_generated(&self) -> Result<()> {
        self.validate()
    }
}

impl GeneratedValidate for crate::types::telegram::PassportElementError {
    fn validate_generated(&self) -> Result<()> {
        self.validate()
    }
}

fn validate_items<T: GeneratedValidate>(values: &[T]) -> Result<()> {
    for value in values {
        value.validate_generated()?;
    }

    Ok(())
}

fn validate_required_items<T: GeneratedValidate>(field: &str, values: &[T]) -> Result<()> {
    if values.is_empty() {
        return Err(Error::InvalidRequest {
            reason: format!("{field} cannot be empty"),
        });
    }

    validate_items(values)
}

/// Auto-generated request for `sendInvoice`.
#[derive(Clone, Debug, Serialize)]
pub struct AdvancedSendInvoiceRequest {
    pub chat_id: crate::types::common::ChatId,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub message_thread_id: Option<i64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub direct_messages_topic_id: Option<i64>,
    pub title: String,
    pub description: String,
    pub payload: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub provider_token: Option<String>,
    pub currency: String,
    pub prices: Vec<crate::types::payment::LabeledPrice>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub max_tip_amount: Option<i64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub suggested_tip_amounts: Option<Vec<i64>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub start_parameter: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub provider_data: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub photo_url: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub photo_size: Option<i64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub photo_width: Option<i64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub photo_height: Option<i64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub need_name: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub need_phone_number: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub need_email: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub need_shipping_address: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub send_phone_number_to_provider: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub send_email_to_provider: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub is_flexible: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub disable_notification: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub protect_content: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub allow_paid_broadcast: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub message_effect_id: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub suggested_post_parameters: Option<crate::types::telegram::SuggestedPostParameters>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub reply_parameters: Option<crate::types::telegram::ReplyParameters>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub reply_markup: Option<crate::types::telegram::InlineKeyboardMarkup>,
}

impl AdvancedSendInvoiceRequest {
    pub fn new(
        chat_id: impl Into<crate::types::common::ChatId>,
        title: impl Into<String>,
        description: impl Into<String>,
        payload: impl Into<String>,
        currency: impl Into<String>,
        prices: Vec<crate::types::payment::LabeledPrice>,
    ) -> Self {
        Self {
            chat_id: chat_id.into(),
            message_thread_id: None,
            direct_messages_topic_id: None,
            title: title.into(),
            description: description.into(),
            payload: payload.into(),
            provider_token: None,
            currency: currency.into(),
            prices,
            max_tip_amount: None,
            suggested_tip_amounts: None,
            start_parameter: None,
            provider_data: None,
            photo_url: None,
            photo_size: None,
            photo_width: None,
            photo_height: None,
            need_name: None,
            need_phone_number: None,
            need_email: None,
            need_shipping_address: None,
            send_phone_number_to_provider: None,
            send_email_to_provider: None,
            is_flexible: None,
            disable_notification: None,
            protect_content: None,
            allow_paid_broadcast: None,
            message_effect_id: None,
            suggested_post_parameters: None,
            reply_parameters: None,
            reply_markup: None,
        }
    }
}

impl AdvancedRequest for AdvancedSendInvoiceRequest {
    type Response = crate::types::message::Message;
    const METHOD: &'static str = "sendInvoice";

    fn validate(&self) -> Result<()> {
        self.chat_id.validate()?;
        if let Some(value) = self.message_thread_id {
            validate_positive_i64("message_thread_id", value)?;
        }
        if let Some(value) = self.direct_messages_topic_id {
            validate_positive_i64("direct_messages_topic_id", value)?;
        }
        validate_required_string("title", &self.title)?;
        validate_required_string("description", &self.description)?;
        validate_required_string("payload", &self.payload)?;
        validate_required_string("currency", &self.currency)?;
        validate_required_items::<crate::types::payment::LabeledPrice>("prices", &self.prices)?;
        if let Some(value) = self.max_tip_amount {
            validate_positive_i64("max_tip_amount", value)?;
        }
        if let Some(value) = self.photo_size {
            validate_positive_i64("photo_size", value)?;
        }
        if let Some(value) = self.photo_width {
            validate_positive_i64("photo_width", value)?;
        }
        if let Some(value) = self.photo_height {
            validate_positive_i64("photo_height", value)?;
        }
        if let Some(value) = self.message_effect_id.as_deref() {
            validate_string_id("message_effect_id", value)?;
        }
        if let Some(value) = self.suggested_post_parameters.as_ref() {
            value.validate()?;
        }
        if let Some(value) = self.reply_parameters.as_ref() {
            value.validate()?;
        }
        if let Some(value) = self.reply_markup.as_ref() {
            value.validate()?;
        }
        Ok(())
    }
}

/// Auto-generated request for `createInvoiceLink`.
#[derive(Clone, Debug, Serialize)]
pub struct AdvancedCreateInvoiceLinkRequest {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub business_connection_id: Option<String>,
    pub title: String,
    pub description: String,
    pub payload: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub provider_token: Option<String>,
    pub currency: String,
    pub prices: Vec<crate::types::payment::LabeledPrice>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub subscription_period: Option<i64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub max_tip_amount: Option<i64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub suggested_tip_amounts: Option<Vec<i64>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub provider_data: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub photo_url: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub photo_size: Option<i64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub photo_width: Option<i64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub photo_height: Option<i64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub need_name: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub need_phone_number: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub need_email: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub need_shipping_address: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub send_phone_number_to_provider: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub send_email_to_provider: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub is_flexible: Option<bool>,
}

impl AdvancedCreateInvoiceLinkRequest {
    pub fn new(
        title: impl Into<String>,
        description: impl Into<String>,
        payload: impl Into<String>,
        currency: impl Into<String>,
        prices: Vec<crate::types::payment::LabeledPrice>,
    ) -> Self {
        Self {
            business_connection_id: None,
            title: title.into(),
            description: description.into(),
            payload: payload.into(),
            provider_token: None,
            currency: currency.into(),
            prices,
            subscription_period: None,
            max_tip_amount: None,
            suggested_tip_amounts: None,
            provider_data: None,
            photo_url: None,
            photo_size: None,
            photo_width: None,
            photo_height: None,
            need_name: None,
            need_phone_number: None,
            need_email: None,
            need_shipping_address: None,
            send_phone_number_to_provider: None,
            send_email_to_provider: None,
            is_flexible: None,
        }
    }
}

impl AdvancedRequest for AdvancedCreateInvoiceLinkRequest {
    type Response = String;
    const METHOD: &'static str = "createInvoiceLink";

    fn validate(&self) -> Result<()> {
        if let Some(value) = self.business_connection_id.as_deref() {
            validate_string_id("business_connection_id", value)?;
        }
        validate_required_string("title", &self.title)?;
        validate_required_string("description", &self.description)?;
        validate_required_string("payload", &self.payload)?;
        validate_required_string("currency", &self.currency)?;
        validate_required_items::<crate::types::payment::LabeledPrice>("prices", &self.prices)?;
        if let Some(value) = self.subscription_period {
            validate_positive_i64("subscription_period", value)?;
        }
        if let Some(value) = self.max_tip_amount {
            validate_positive_i64("max_tip_amount", value)?;
        }
        if let Some(value) = self.photo_size {
            validate_positive_i64("photo_size", value)?;
        }
        if let Some(value) = self.photo_width {
            validate_positive_i64("photo_width", value)?;
        }
        if let Some(value) = self.photo_height {
            validate_positive_i64("photo_height", value)?;
        }
        Ok(())
    }
}

/// Auto-generated request for `answerShippingQuery`.
#[derive(Clone, Debug, Serialize)]
pub struct AdvancedAnswerShippingQueryRequest {
    pub shipping_query_id: String,
    pub ok: bool,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub shipping_options: Option<Vec<crate::types::payment::ShippingOption>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub error_message: Option<String>,
}

impl AdvancedAnswerShippingQueryRequest {
    pub fn new(shipping_query_id: impl Into<String>, ok: bool) -> Self {
        Self {
            shipping_query_id: shipping_query_id.into(),
            ok,
            shipping_options: None,
            error_message: None,
        }
    }
}

impl AdvancedRequest for AdvancedAnswerShippingQueryRequest {
    type Response = Value;
    const METHOD: &'static str = "answerShippingQuery";

    fn validate(&self) -> Result<()> {
        validate_string_id("shipping_query_id", &self.shipping_query_id)?;
        if self.ok {
            if self.error_message.is_some() {
                return Err(Error::InvalidRequest {
                    reason: "answerShippingQuery must omit `error_message` when `ok` is true"
                        .to_owned(),
                });
            }
            let Some(shipping_options) = self.shipping_options.as_deref() else {
                return Err(Error::InvalidRequest {
                    reason: "answerShippingQuery requires `shipping_options` when `ok` is true"
                        .to_owned(),
                });
            };
            validate_required_items::<crate::types::payment::ShippingOption>(
                "shipping_options",
                shipping_options,
            )?;
        } else {
            if self.shipping_options.is_some() {
                return Err(Error::InvalidRequest {
                    reason: "answerShippingQuery must omit `shipping_options` when `ok` is false"
                        .to_owned(),
                });
            }
            if self
                .error_message
                .as_deref()
                .is_none_or(|value| value.trim().is_empty())
            {
                return Err(Error::InvalidRequest {
                    reason:
                        "answerShippingQuery requires non-empty `error_message` when `ok` is false"
                            .to_owned(),
                });
            }
        }
        Ok(())
    }
}

/// Auto-generated request for `answerPreCheckoutQuery`.
#[derive(Clone, Debug, Serialize)]
pub struct AdvancedAnswerPreCheckoutQueryRequest {
    pub pre_checkout_query_id: String,
    pub ok: bool,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub error_message: Option<String>,
}

impl AdvancedAnswerPreCheckoutQueryRequest {
    pub fn new(pre_checkout_query_id: impl Into<String>, ok: bool) -> Self {
        Self {
            pre_checkout_query_id: pre_checkout_query_id.into(),
            ok,
            error_message: None,
        }
    }
}

impl AdvancedRequest for AdvancedAnswerPreCheckoutQueryRequest {
    type Response = Value;
    const METHOD: &'static str = "answerPreCheckoutQuery";

    fn validate(&self) -> Result<()> {
        validate_string_id("pre_checkout_query_id", &self.pre_checkout_query_id)?;
        if self.ok {
            if self.error_message.is_some() {
                return Err(Error::InvalidRequest {
                    reason: "answerPreCheckoutQuery must omit `error_message` when `ok` is true"
                        .to_owned(),
                });
            }
        } else if self
            .error_message
            .as_deref()
            .is_none_or(|value| value.trim().is_empty())
        {
            return Err(Error::InvalidRequest {
                reason:
                    "answerPreCheckoutQuery requires non-empty `error_message` when `ok` is false"
                        .to_owned(),
            });
        }
        Ok(())
    }
}

/// Auto-generated request for `setPassportDataErrors`.
#[derive(Clone, Debug, Serialize)]
pub struct AdvancedSetPassportDataErrorsRequest {
    pub user_id: crate::types::common::UserId,
    pub errors: Vec<crate::types::telegram::PassportElementError>,
}

impl AdvancedSetPassportDataErrorsRequest {
    pub fn new(
        user_id: crate::types::common::UserId,
        errors: Vec<crate::types::telegram::PassportElementError>,
    ) -> Self {
        Self { user_id, errors }
    }
}

impl AdvancedRequest for AdvancedSetPassportDataErrorsRequest {
    type Response = bool;
    const METHOD: &'static str = "setPassportDataErrors";

    fn validate(&self) -> Result<()> {
        self.user_id.validate()?;
        validate_required_items::<crate::types::telegram::PassportElementError>(
            "errors",
            &self.errors,
        )?;
        Ok(())
    }
}
