/*
 * Zernio API
 *
 * API reference for Zernio. Authenticate with a Bearer API key. Base URL: https://zernio.com/api
 *
 * The version of the OpenAPI document: 1.0.4
 * Contact: support@zernio.com
 * Generated by: https://openapi-generator.tech
 */

use super::{configuration, ContentType, Error};
use crate::{apis::ResponseContent, models};
use reqwest;
use serde::{de::Error as _, Deserialize, Serialize};

/// struct for typed errors of method [`add_discord_member_role`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum AddDiscordMemberRoleError {
    Status400(),
    Status401(models::InlineObject),
    Status404(),
    Status502(),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`create_discord_scheduled_event`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum CreateDiscordScheduledEventError {
    Status400(),
    Status401(models::InlineObject),
    Status404(),
    Status502(),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`delete_discord_scheduled_event`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum DeleteDiscordScheduledEventError {
    Status401(models::InlineObject),
    Status404(),
    Status502(),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`get_discord_channels`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetDiscordChannelsError {
    Status400(),
    Status401(models::InlineObject),
    Status404(),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`get_discord_scheduled_event`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetDiscordScheduledEventError {
    Status401(models::InlineObject),
    Status404(),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`get_discord_settings`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetDiscordSettingsError {
    Status400(),
    Status401(models::InlineObject),
    Status404(),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`list_discord_guild_members`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ListDiscordGuildMembersError {
    Status400(),
    Status401(models::InlineObject),
    Status404(),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`list_discord_guild_roles`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ListDiscordGuildRolesError {
    Status400(),
    Status401(models::InlineObject),
    Status404(),
    Status502(),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`list_discord_pinned_messages`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ListDiscordPinnedMessagesError {
    Status400(),
    Status401(models::InlineObject),
    Status404(),
    Status502(),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`list_discord_scheduled_events`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ListDiscordScheduledEventsError {
    Status400(),
    Status401(models::InlineObject),
    Status404(),
    Status502(),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`pin_discord_message`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PinDiscordMessageError {
    Status400(),
    Status401(models::InlineObject),
    Status404(),
    Status502(),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`remove_discord_member_role`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum RemoveDiscordMemberRoleError {
    Status400(),
    Status401(models::InlineObject),
    Status404(),
    Status502(),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`send_discord_direct_message`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum SendDiscordDirectMessageError {
    Status400(),
    Status401(models::InlineObject),
    Status404(),
    Status502(),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`unpin_discord_message`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum UnpinDiscordMessageError {
    Status400(),
    Status401(models::InlineObject),
    Status404(),
    Status502(),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`update_discord_scheduled_event`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum UpdateDiscordScheduledEventError {
    Status400(),
    Status401(models::InlineObject),
    Status404(),
    Status502(),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`update_discord_settings`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum UpdateDiscordSettingsError {
    Status400(),
    Status401(models::InlineObject),
    Status404(),
    UnknownValue(serde_json::Value),
}

/// Assign one role to one member. Idempotent on Discord's side — re-running on a member who already has the role is a 204 no-op.  Path shape mirrors Discord's own API (`PUT /guilds/{guild}/members/{user}/roles/{role}`) for zero-translation mental mapping.  Bot needs MANAGE_ROLES permission in the guild AND its highest role must be above the target role (Discord hierarchy rule). The `@everyone` role (where roleId == guildId) cannot be assigned.
pub async fn add_discord_member_role(
    configuration: &configuration::Configuration,
    guild_id: &str,
    user_id: &str,
    role_id: &str,
    account_id: &str,
) -> Result<models::AddDiscordMemberRole200Response, Error<AddDiscordMemberRoleError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_path_guild_id = guild_id;
    let p_path_user_id = user_id;
    let p_path_role_id = role_id;
    let p_query_account_id = account_id;

    let uri_str = format!(
        "{}/v1/discord/guilds/{guildId}/members/{userId}/roles/{roleId}",
        configuration.base_path,
        guildId = crate::apis::urlencode(p_path_guild_id),
        userId = crate::apis::urlencode(p_path_user_id),
        roleId = crate::apis::urlencode(p_path_role_id)
    );
    let mut req_builder = configuration.client.request(reqwest::Method::PUT, &uri_str);

    req_builder = req_builder.query(&[("accountId", &p_query_account_id.to_string())]);
    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::AddDiscordMemberRole200Response`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::AddDiscordMemberRole200Response`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<AddDiscordMemberRoleError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Create a guild scheduled event. Three event types, selected via the discriminator on `entity.type`:    - `external` — off-platform (Zoom, in-person, livestream). Requires     both `location` and `endsAt`. Most common type for scheduler     integrations.   - `voice` — hosted in a Discord voice channel. Requires `channelId`.   - `stage` — hosted in a Discord stage channel. Requires `channelId`.  Bot needs MANAGE_EVENTS in the guild. Existing installs (pre-events PR) need a re-invite OR a server admin manually granting the permission — see route header for details.
pub async fn create_discord_scheduled_event(
    configuration: &configuration::Configuration,
    guild_id: &str,
    create_discord_scheduled_event_request: models::CreateDiscordScheduledEventRequest,
) -> Result<models::CreateDiscordScheduledEvent200Response, Error<CreateDiscordScheduledEventError>>
{
    // add a prefix to parameters to efficiently prevent name collisions
    let p_path_guild_id = guild_id;
    let p_body_create_discord_scheduled_event_request = create_discord_scheduled_event_request;

    let uri_str = format!(
        "{}/v1/discord/guilds/{guildId}/events",
        configuration.base_path,
        guildId = crate::apis::urlencode(p_path_guild_id)
    );
    let mut req_builder = configuration
        .client
        .request(reqwest::Method::POST, &uri_str);

    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };
    req_builder = req_builder.json(&p_body_create_discord_scheduled_event_request);

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::CreateDiscordScheduledEvent200Response`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::CreateDiscordScheduledEvent200Response`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<CreateDiscordScheduledEventError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Hard-delete an event. Use PATCH with `status: 'cancelled'` instead if you want the event preserved in the guild's history.
pub async fn delete_discord_scheduled_event(
    configuration: &configuration::Configuration,
    guild_id: &str,
    event_id: &str,
    account_id: &str,
) -> Result<models::DeleteDiscordScheduledEvent200Response, Error<DeleteDiscordScheduledEventError>>
{
    // add a prefix to parameters to efficiently prevent name collisions
    let p_path_guild_id = guild_id;
    let p_path_event_id = event_id;
    let p_query_account_id = account_id;

    let uri_str = format!(
        "{}/v1/discord/guilds/{guildId}/events/{eventId}",
        configuration.base_path,
        guildId = crate::apis::urlencode(p_path_guild_id),
        eventId = crate::apis::urlencode(p_path_event_id)
    );
    let mut req_builder = configuration
        .client
        .request(reqwest::Method::DELETE, &uri_str);

    req_builder = req_builder.query(&[("accountId", &p_query_account_id.to_string())]);
    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::DeleteDiscordScheduledEvent200Response`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::DeleteDiscordScheduledEvent200Response`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<DeleteDiscordScheduledEventError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Returns the text, announcement, and forum channels in the connected Discord guild. Use this to discover available channels when switching the connected channel via PATCH /v1/accounts/{accountId}/discord-settings.
pub async fn get_discord_channels(
    configuration: &configuration::Configuration,
    account_id: &str,
) -> Result<models::GetDiscordChannels200Response, Error<GetDiscordChannelsError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_path_account_id = account_id;

    let uri_str = format!(
        "{}/v1/accounts/{accountId}/discord-channels",
        configuration.base_path,
        accountId = crate::apis::urlencode(p_path_account_id)
    );
    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);

    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::GetDiscordChannels200Response`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::GetDiscordChannels200Response`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<GetDiscordChannelsError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

pub async fn get_discord_scheduled_event(
    configuration: &configuration::Configuration,
    guild_id: &str,
    event_id: &str,
    account_id: &str,
) -> Result<models::CreateDiscordScheduledEvent200Response, Error<GetDiscordScheduledEventError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_path_guild_id = guild_id;
    let p_path_event_id = event_id;
    let p_query_account_id = account_id;

    let uri_str = format!(
        "{}/v1/discord/guilds/{guildId}/events/{eventId}",
        configuration.base_path,
        guildId = crate::apis::urlencode(p_path_guild_id),
        eventId = crate::apis::urlencode(p_path_event_id)
    );
    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);

    req_builder = req_builder.query(&[("accountId", &p_query_account_id.to_string())]);
    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::CreateDiscordScheduledEvent200Response`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::CreateDiscordScheduledEvent200Response`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<GetDiscordScheduledEventError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Returns the current Discord account settings including webhook identity (display name and avatar), connected channel, and guild information.
pub async fn get_discord_settings(
    configuration: &configuration::Configuration,
    account_id: &str,
) -> Result<models::GetDiscordSettings200Response, Error<GetDiscordSettingsError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_path_account_id = account_id;

    let uri_str = format!(
        "{}/v1/accounts/{accountId}/discord-settings",
        configuration.base_path,
        accountId = crate::apis::urlencode(p_path_account_id)
    );
    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);

    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::GetDiscordSettings200Response`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::GetDiscordSettings200Response`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<GetDiscordSettingsError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Cursor-paginated list of guild members. Returns Discord's raw member objects so callers can build community-ops automation (e.g. \"add role to all members joined in the last 7 days\") on the actual platform shape.  **Important:** this endpoint requires the privileged \"Server Members Intent\" enabled on the Discord app (Developer Portal → Bot tab → toggle \"Server Members Intent\" ON, then Save). Without it, Discord returns an empty array with no error. Verify the intent is enabled before relying on this endpoint.  Pagination: pass `after` = the last `user.id` from the previous page. Omit on the first call. Response includes a `nextCursor` and `hasMore` flag so callers don't need to know Discord's pagination shape.
pub async fn list_discord_guild_members(
    configuration: &configuration::Configuration,
    guild_id: &str,
    account_id: &str,
    limit: Option<i32>,
    after: Option<&str>,
) -> Result<models::ListDiscordGuildMembers200Response, Error<ListDiscordGuildMembersError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_path_guild_id = guild_id;
    let p_query_account_id = account_id;
    let p_query_limit = limit;
    let p_query_after = after;

    let uri_str = format!(
        "{}/v1/discord/guilds/{guildId}/members",
        configuration.base_path,
        guildId = crate::apis::urlencode(p_path_guild_id)
    );
    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);

    req_builder = req_builder.query(&[("accountId", &p_query_account_id.to_string())]);
    if let Some(ref param_value) = p_query_limit {
        req_builder = req_builder.query(&[("limit", &param_value.to_string())]);
    }
    if let Some(ref param_value) = p_query_after {
        req_builder = req_builder.query(&[("after", &param_value.to_string())]);
    }
    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::ListDiscordGuildMembers200Response`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::ListDiscordGuildMembers200Response`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<ListDiscordGuildMembersError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Returns all roles in a Discord guild. Useful for building role-mention pickers, role-permission UIs, or finding the role ID before calling the role-assign endpoint.  Roles are returned unordered — sort client-side by `position` if you need Discord's UI ordering.  Caller must pass `accountId` of a Discord SocialAccount bound to this guild (route verifies team access + guild match).
pub async fn list_discord_guild_roles(
    configuration: &configuration::Configuration,
    guild_id: &str,
    account_id: &str,
) -> Result<models::ListDiscordGuildRoles200Response, Error<ListDiscordGuildRolesError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_path_guild_id = guild_id;
    let p_query_account_id = account_id;

    let uri_str = format!(
        "{}/v1/discord/guilds/{guildId}/roles",
        configuration.base_path,
        guildId = crate::apis::urlencode(p_path_guild_id)
    );
    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);

    req_builder = req_builder.query(&[("accountId", &p_query_account_id.to_string())]);
    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::ListDiscordGuildRoles200Response`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::ListDiscordGuildRoles200Response`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<ListDiscordGuildRolesError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Returns the channel's pinned messages, sorted most-recently-pinned first. Discord caps a channel at 50 pinned messages and returns the full list unpaginated.  Bot needs READ_MESSAGE_HISTORY in the channel (granted by default BOT_PERMISSIONS).
pub async fn list_discord_pinned_messages(
    configuration: &configuration::Configuration,
    channel_id: &str,
    account_id: &str,
) -> Result<models::ListDiscordPinnedMessages200Response, Error<ListDiscordPinnedMessagesError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_path_channel_id = channel_id;
    let p_query_account_id = account_id;

    let uri_str = format!(
        "{}/v1/discord/channels/{channelId}/pins",
        configuration.base_path,
        channelId = crate::apis::urlencode(p_path_channel_id)
    );
    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);

    req_builder = req_builder.query(&[("accountId", &p_query_account_id.to_string())]);
    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::ListDiscordPinnedMessages200Response`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::ListDiscordPinnedMessages200Response`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<ListDiscordPinnedMessagesError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Return all scheduled events in the guild. Events are distinct from messages — they appear in the server's Events panel and Discord auto-notifies interested members ahead of start time.  Pass `withUserCount=true` to include `user_count` (number of members who RSVP'd) on each event. Useful for surfacing engagement.
pub async fn list_discord_scheduled_events(
    configuration: &configuration::Configuration,
    guild_id: &str,
    account_id: &str,
    with_user_count: Option<bool>,
) -> Result<models::ListDiscordScheduledEvents200Response, Error<ListDiscordScheduledEventsError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_path_guild_id = guild_id;
    let p_query_account_id = account_id;
    let p_query_with_user_count = with_user_count;

    let uri_str = format!(
        "{}/v1/discord/guilds/{guildId}/events",
        configuration.base_path,
        guildId = crate::apis::urlencode(p_path_guild_id)
    );
    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);

    req_builder = req_builder.query(&[("accountId", &p_query_account_id.to_string())]);
    if let Some(ref param_value) = p_query_with_user_count {
        req_builder = req_builder.query(&[("withUserCount", &param_value.to_string())]);
    }
    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::ListDiscordScheduledEvents200Response`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::ListDiscordScheduledEvents200Response`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<ListDiscordScheduledEventsError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Pin a specific message in a channel. Path shape mirrors Discord's own API (`PUT /channels/{cid}/pins/{mid}`).  Idempotent — re-pinning an already-pinned message is a 204 no-op.  Constraints:   - Bot needs MANAGE_MESSAGES in the channel.   - 50-pin cap per channel — hitting it returns 400 (Discord-side).     Caller should unpin one first.
pub async fn pin_discord_message(
    configuration: &configuration::Configuration,
    channel_id: &str,
    message_id: &str,
    account_id: &str,
) -> Result<models::PinDiscordMessage200Response, Error<PinDiscordMessageError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_path_channel_id = channel_id;
    let p_path_message_id = message_id;
    let p_query_account_id = account_id;

    let uri_str = format!(
        "{}/v1/discord/channels/{channelId}/pins/{messageId}",
        configuration.base_path,
        channelId = crate::apis::urlencode(p_path_channel_id),
        messageId = crate::apis::urlencode(p_path_message_id)
    );
    let mut req_builder = configuration.client.request(reqwest::Method::PUT, &uri_str);

    req_builder = req_builder.query(&[("accountId", &p_query_account_id.to_string())]);
    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::PinDiscordMessage200Response`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::PinDiscordMessage200Response`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<PinDiscordMessageError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Remove one role from one member. Idempotent — removing a role the member doesn't have returns 204 no-op.  Same permission + hierarchy constraints as the PUT counterpart.
pub async fn remove_discord_member_role(
    configuration: &configuration::Configuration,
    guild_id: &str,
    user_id: &str,
    role_id: &str,
    account_id: &str,
) -> Result<models::RemoveDiscordMemberRole200Response, Error<RemoveDiscordMemberRoleError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_path_guild_id = guild_id;
    let p_path_user_id = user_id;
    let p_path_role_id = role_id;
    let p_query_account_id = account_id;

    let uri_str = format!(
        "{}/v1/discord/guilds/{guildId}/members/{userId}/roles/{roleId}",
        configuration.base_path,
        guildId = crate::apis::urlencode(p_path_guild_id),
        userId = crate::apis::urlencode(p_path_user_id),
        roleId = crate::apis::urlencode(p_path_role_id)
    );
    let mut req_builder = configuration
        .client
        .request(reqwest::Method::DELETE, &uri_str);

    req_builder = req_builder.query(&[("accountId", &p_query_account_id.to_string())]);
    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::RemoveDiscordMemberRole200Response`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::RemoveDiscordMemberRole200Response`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<RemoveDiscordMemberRoleError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Send a 1:1 Direct Message from the bot to a Discord user (by snowflake ID). Supports the same payload shape as channel posts — content, embeds, media attachments, and TTS.  Constraints (Discord platform limits):   - The bot can only DM users it shares at least one guild with.   - If the recipient has DMs disabled for non-friends, Discord returns 403     (surfaces as a 502 platform error).   - `content` capped at 2,000 chars.   - At least one of `content`, `embeds`, or `attachments` is required.   - The recipient must be identified by Discord snowflake ID (not username).  This is a dedicated endpoint rather than a `POST /v1/posts` variant because DMs are 1:1 operational messages (onboarding, billing reminders, support pings) with a different lifecycle than scheduled channel posts. DMs are not persisted to `Post` / `ExternalPost` and are always sent immediately.
pub async fn send_discord_direct_message(
    configuration: &configuration::Configuration,
    send_discord_direct_message_request: models::SendDiscordDirectMessageRequest,
) -> Result<models::SendDiscordDirectMessage200Response, Error<SendDiscordDirectMessageError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_body_send_discord_direct_message_request = send_discord_direct_message_request;

    let uri_str = format!("{}/v1/discord/dms", configuration.base_path);
    let mut req_builder = configuration
        .client
        .request(reqwest::Method::POST, &uri_str);

    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };
    req_builder = req_builder.json(&p_body_send_discord_direct_message_request);

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::SendDiscordDirectMessage200Response`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::SendDiscordDirectMessage200Response`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<SendDiscordDirectMessageError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Unpin a message. Same MANAGE_MESSAGES permission requirement as pin. Idempotent — unpinning a non-pinned message is a 204 no-op.
pub async fn unpin_discord_message(
    configuration: &configuration::Configuration,
    channel_id: &str,
    message_id: &str,
    account_id: &str,
) -> Result<models::UnpinDiscordMessage200Response, Error<UnpinDiscordMessageError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_path_channel_id = channel_id;
    let p_path_message_id = message_id;
    let p_query_account_id = account_id;

    let uri_str = format!(
        "{}/v1/discord/channels/{channelId}/pins/{messageId}",
        configuration.base_path,
        channelId = crate::apis::urlencode(p_path_channel_id),
        messageId = crate::apis::urlencode(p_path_message_id)
    );
    let mut req_builder = configuration
        .client
        .request(reqwest::Method::DELETE, &uri_str);

    req_builder = req_builder.query(&[("accountId", &p_query_account_id.to_string())]);
    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::UnpinDiscordMessage200Response`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::UnpinDiscordMessage200Response`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<UnpinDiscordMessageError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Patch any subset of fields. Passing `status: 'cancelled'` is how you cancel an event — Discord doesn't have a dedicated cancel endpoint, it's a status transition.  Most status transitions Discord enforces (you can't go SCHEDULED → COMPLETED directly). The common consumer case is SCHEDULED → CANCELED.
pub async fn update_discord_scheduled_event(
    configuration: &configuration::Configuration,
    guild_id: &str,
    event_id: &str,
    update_discord_scheduled_event_request: models::UpdateDiscordScheduledEventRequest,
) -> Result<models::CreateDiscordScheduledEvent200Response, Error<UpdateDiscordScheduledEventError>>
{
    // add a prefix to parameters to efficiently prevent name collisions
    let p_path_guild_id = guild_id;
    let p_path_event_id = event_id;
    let p_body_update_discord_scheduled_event_request = update_discord_scheduled_event_request;

    let uri_str = format!(
        "{}/v1/discord/guilds/{guildId}/events/{eventId}",
        configuration.base_path,
        guildId = crate::apis::urlencode(p_path_guild_id),
        eventId = crate::apis::urlencode(p_path_event_id)
    );
    let mut req_builder = configuration
        .client
        .request(reqwest::Method::PATCH, &uri_str);

    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };
    req_builder = req_builder.json(&p_body_update_discord_scheduled_event_request);

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::CreateDiscordScheduledEvent200Response`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::CreateDiscordScheduledEvent200Response`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<UpdateDiscordScheduledEventError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Update Discord account settings. Supports two operations (can be combined):  1. **Webhook identity** - Set the default display name and avatar that appear as the message author on every post. These are account-level defaults; individual posts can override them via platformSpecificData.webhookUsername / webhookAvatarUrl.  2. **Switch channel** - Move the connection to a different channel in the same guild. A new webhook is automatically created in the target channel.
pub async fn update_discord_settings(
    configuration: &configuration::Configuration,
    account_id: &str,
    update_discord_settings_request: models::UpdateDiscordSettingsRequest,
) -> Result<models::UpdateDiscordSettings200Response, Error<UpdateDiscordSettingsError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_path_account_id = account_id;
    let p_body_update_discord_settings_request = update_discord_settings_request;

    let uri_str = format!(
        "{}/v1/accounts/{accountId}/discord-settings",
        configuration.base_path,
        accountId = crate::apis::urlencode(p_path_account_id)
    );
    let mut req_builder = configuration
        .client
        .request(reqwest::Method::PATCH, &uri_str);

    if let Some(ref user_agent) = configuration.user_agent {
        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
    }
    if let Some(ref token) = configuration.bearer_access_token {
        req_builder = req_builder.bearer_auth(token.to_owned());
    };
    req_builder = req_builder.json(&p_body_update_discord_settings_request);

    let req = req_builder.build()?;
    let resp = configuration.client.execute(req).await?;

    let status = resp.status();
    let content_type = resp
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("application/octet-stream");
    let content_type = super::ContentType::from(content_type);

    if !status.is_client_error() && !status.is_server_error() {
        let content = resp.text().await?;
        match content_type {
            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::UpdateDiscordSettings200Response`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::UpdateDiscordSettings200Response`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<UpdateDiscordSettingsError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}
