//! Webhook model and implementations.

use super::{
    id::{
        ChannelId,
        GuildId,
        WebhookId
    },
    user::User
};

#[cfg(feature = "model")]
use builder::ExecuteWebhook;
#[cfg(feature = "model")]
use internal::prelude::*;
#[cfg(feature = "model")]
use std::mem;
#[cfg(feature = "model")]
use super::channel::Message;
#[cfg(feature = "model")]
use {http, utils};

/// A representation of a webhook, which is a low-effort way to post messages to
/// channels. They do not necessarily require a bot user or authentication to
/// use.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Webhook {
    /// The unique Id.
    ///
    /// Can be used to calculate the creation date of the webhook.
    pub id: WebhookId,
    /// The default avatar.
    ///
    /// This can be modified via [`ExecuteWebhook::avatar`].
    ///
    /// [`ExecuteWebhook::avatar`]: ../../builder/struct.ExecuteWebhook.html#method.avatar
    pub avatar: Option<String>,
    /// The Id of the channel that owns the webhook.
    pub channel_id: ChannelId,
    /// The Id of the guild that owns the webhook.
    pub guild_id: Option<GuildId>,
    /// The default name of the webhook.
    ///
    /// This can be modified via [`ExecuteWebhook::username`].
    ///
    /// [`ExecuteWebhook::username`]: ../../builder/struct.ExecuteWebhook.html#method.username
    pub name: Option<String>,
    /// The webhook's secure token.
    pub token: String,
    /// The user that created the webhook.
    ///
    /// **Note**: This is not received when getting a webhook by its token.
    pub user: Option<User>,
}

#[cfg(feature = "model")]
impl Webhook {
    /// Deletes the webhook.
    ///
    /// As this calls the [`http::delete_webhook_with_token`] function,
    /// authentication is not required.
    ///
    /// [`http::delete_webhook_with_token`]: ../../http/fn.delete_webhook_with_token.html
    #[inline]
    pub fn delete(&self) -> Result<()> { http::delete_webhook_with_token(self.id.0, &self.token) }

    ///
    /// Edits the webhook in-place. All fields are optional.
    ///
    /// To nullify the avatar, pass `Some("")`. Otherwise, passing `None` will
    /// not modify the avatar.
    ///
    /// Refer to [`http::edit_webhook`] for httprictions on editing webhooks.
    ///
    /// As this calls the [`http::edit_webhook_with_token`] function,
    /// authentication is not required.
    ///
    /// # Examples
    ///
    /// Editing a webhook's name:
    ///
    /// ```rust,no_run
    /// use serenity::http;
    ///
    /// let id = 245037420704169985;
    /// let token = "ig5AO-wdVWpCBtUUMxmgsWryqgsW3DChbKYOINftJ4DCrUbnkedoYZD0VOH1QLr-S3sV";
    ///
    /// let mut webhook = http::get_webhook_with_token(id, token)
    ///     .expect("valid webhook");
    ///
    /// let _ = webhook.edit(Some("new name"), None).expect("Error editing");
    /// ```
    ///
    /// Setting a webhook's avatar:
    ///
    /// ```rust,no_run
    /// use serenity::http;
    ///
    /// let id = 245037420704169985;
    /// let token = "ig5AO-wdVWpCBtUUMxmgsWryqgsW3DChbKYOINftJ4DCrUbnkedoYZD0VOH1QLr-S3sV";
    ///
    /// let mut webhook = http::get_webhook_with_token(id, token)
    ///     .expect("valid webhook");
    ///
    /// let image = serenity::utils::read_image("./webhook_img.png")
    ///     .expect("Error reading image");
    ///
    /// let _ = webhook.edit(None, Some(&image)).expect("Error editing");
    /// ```
    ///
    /// [`http::edit_webhook`]: ../../http/fn.edit_webhook.html
    /// [`http::edit_webhook_with_token`]: ../../http/fn.edit_webhook_with_token.html
    pub fn 
(&mut self, name: Option<&str>, avatar: Option<&str>) -> Result<()> {
        if name.is_none() && avatar.is_none() {
            return Ok(());
        }

        let mut map = Map::new();

        if let Some(avatar) = avatar {
            map.insert(
                "avatar".to_string(),
                if avatar.is_empty() {
                    Value::Null
                } else {
                    Value::String(avatar.to_string())
                },
            );
        }

        if let Some(name) = name {
            map.insert("name".to_string(), Value::String(name.to_string()));
        }

        match http::edit_webhook_with_token(self.id.0, &self.token, &map) {
            Ok(replacement) => {
                mem::replace(self, replacement);

                Ok(())
            },
            Err(why) => Err(why),
        }
    }

    /// Executes a webhook with the fields set via the given builder.
    ///
    /// The builder provides a method of setting only the fields you need,
    /// without needing to pass a long set of arguments.
    ///
    /// # Examples
    ///
    /// Execute a webhook with message content of `test`:
    ///
    /// ```rust,no_run
    /// use serenity::http;
    ///
    /// let id = 245037420704169985;
    /// let token = "ig5AO-wdVWpCBtUUMxmgsWryqgsW3DChbKYOINftJ4DCrUbnkedoYZD0VOH1QLr-S3sV";
    ///
    /// let mut webhook = http::get_webhook_with_token(id, token)
    ///     .expect("valid webhook");
    ///
    /// let _ = webhook.execute(false, |w| w.content("test")).expect("Error executing");
    /// ```
    ///
    /// Execute a webhook with message content of `test`, overriding the
    /// username to `serenity`, and sending an embed:
    ///
    /// ```rust,no_run
    /// use serenity::http;
    /// use serenity::model::channel::Embed;
    ///
    /// let id = 245037420704169985;
    /// let token = "ig5AO-wdVWpCBtUUMxmgsWryqgsW3DChbKYOINftJ4DCrUbnkedoYZD0VOH1QLr-S3sV";
    ///
    /// let mut webhook = http::get_webhook_with_token(id, token)
    ///     .expect("valid webhook");
    ///
    /// let embed = Embed::fake(|e| e
    ///     .title("Rust's website")
    ///     .description("Rust is a systems programming language that runs
    ///                   blazingly fast, prevents segfaults, and guarantees
    ///                   thread safety.")
    ///     .url("https://rust-lang.org"));
    ///
    /// let _ = webhook.execute(false, |w| w
    ///     .content("test")
    ///     .username("serenity")
    ///     .embeds(vec![embed]))
    ///     .expect("Error executing");
    /// ```
    #[inline]
    pub fn execute<F: FnOnce(ExecuteWebhook) -> ExecuteWebhook>(&self,
                                                                wait: bool,
                                                                f: F)
                                                                -> Result<Option<Message>> {
        let map = utils::vecmap_to_json_map(f(ExecuteWebhook::default()).0);

        http::execute_webhook(self.id.0, &self.token, wait, &map)
    }

    /// Retrieves the latest information about the webhook, editing the
    /// webhook in-place.
    ///
    /// As this calls the [`http::get_webhook_with_token`] function,
    /// authentication is not required.
    ///
    /// [`http::get_webhook_with_token`]: ../../http/fn.get_webhook_with_token.html
    pub fn refresh(&mut self) -> Result<()> {
        match http::get_webhook_with_token(self.id.0, &self.token) {
            Ok(replacement) => {
                let _ = mem::replace(self, replacement);

                Ok(())
            },
            Err(why) => Err(why),
        }
    }
}

#[cfg(feature = "model")]
impl WebhookId {
    /// Retrieves the webhook by the Id.
    ///
    /// **Note**: Requires the [Manage Webhooks] permission.
    ///
    /// [Manage Webhooks]: ../../model/permissions/struct.Permissions.html#associatedconstant.MANAGE_WEBHOOKS
    #[inline]
    #[deprecated(since = "0.5.8", note = "Use the `to_webhook`-method instead.")]
    pub fn get(self) -> Result<Webhook> { self.to_webhook() }

    /// Requests [`Webhook`] over REST API.
    ///
    /// **Note**: Requires the [Manage Webhooks] permission.
    ///
    /// [`Webhook`]: struct.Webhook.html
    /// [Manage Webhooks]: ../../model/permissions/struct.Permissions.html#associatedconstant.MANAGE_WEBHOOKS
    #[inline]
    pub fn to_webhook(self) -> Result<Webhook> { http::get_webhook(self.0) }
}

edit