use super::{
    channel::ChannelMention, interaction::InteractionType, poll::Poll, Channel, Embed, Interaction,
    Reaction, User,
};
use serde::{Deserialize, Serialize};
use serde_json::json;
use serde_repr::{Deserialize_repr, Serialize_repr};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum MessageType {
    Default = 0,
    RecipientAdd = 1,
    RecipientRemove = 2,
    Call = 3,
    ChannelNameChange = 4,
    ChannelIconChange = 5,
    ChannelPinnedMessage = 6,
    GuildMemberJoin = 7,
    UserPremiumGuildSubscription = 8,
    UserPremiumGuildSubscriptionTier1 = 9,
    UserPremiumGuildSubscriptionTier2 = 10,
    UserPremiumGuildSubscriptionTier3 = 11,
    ChannelFollowAdd = 12,
    GuildDiscoveryDisqualified = 14,
    GuildDiscoveryRequalified = 15,
    GuildDiscoveryGracePeriodInitialWarning = 16,
    GuildDiscoveryGracePeriodFinalWarning = 17,
    ThreadCreated = 18,
    Reply = 19,
    ChatInputCommand = 20,
    ThreadStarterMessage = 21,
    GuildInviteReminder = 22,
    ContextMenuCommand = 23,
    AutoModerationAction = 24,
    RoleSubscriptionPurchase = 25,
    InteractionPremiumUpsell = 26,
    StageStart = 27,
    StageEnd = 28,
    StageSpeaker = 29,
    StageTopic = 31,
    GuildApplicationPremiumSubscription = 32,
    GuildIncidentAlertModeEnabled = 36,
    GuildIncidentAlertModeDisabled = 37,
    GuildIncidentReportRaid = 38,
    GuildIncidentReportFalseAlarm = 39,
    PurchaseNotification = 44,
    PollResult = 46,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Message {
    /// Unique ID of the message
    pub id: String,

    /// ID of the channel the message was sent in
    pub channel_id: String,

    /// Author of the message
    pub author: User,

    /// Content of the message
    pub content: String,

    /// Timestamp of when the message was sent
    pub timestamp: String,

    /// Edit timestamp (if the message was edited)
    pub edited_timestamp: Option<String>,

    /// Whether the message is TTS
    #[serde(default)]
    pub tts: bool,

    /// Whether the message mentions everyone
    #[serde(default)]
    pub mention_everyone: bool,

    /// Users mentioned in the message
    #[serde(default)]
    pub mentions: Vec<User>,

    /// Roles mentioned in the message
    #[serde(default)]
    pub mention_roles: Vec<String>,

    /// Channels mentioned in the message
    #[serde(default)]
    pub mention_channels: Vec<ChannelMention>,

    /// Attachments in the message
    #[serde(default)]
    pub attachments: Vec<Attachment>,

    /// Embeds in the message
    #[serde(default)]
    pub embeds: Vec<Embed>,

    /// Reactions to the message
    #[serde(default)]
    pub reactions: Vec<Reaction>,

    /// Nonce of the message (if it's a bot message)
    pub nonce: Option<String>,

    /// Whether the message is pinned
    #[serde(default)]
    pub pinned: bool,

    /// Webhook ID (if the message was generated by a webhook)
    pub webhook_id: Option<String>,

    /// Type of the message (default is 0)
    #[serde(rename = "type")]
    pub kind: MessageType,

    /// Sent with Rich Presence-related chat embeds
    #[serde(default)]
    pub activity: Option<MessageActivity>,

    /// Sent with Rich Presence-related chat embeds
    pub application: Option<String>,

    /// Application ID of the Rich Presence-related chat embeds
    pub application_id: Option<String>,

    /// Messages Flags (bitfield)
    pub message_flags: Option<u64>,

    /// Message reference data (if the attachment is a reply)
    pub message_reference: Option<MessageReference>,

    /// Interaction metadata (if the attachment is from an interaction)
    pub interaction_metadata: Option<MessageInteractionMetadata>,

    /// Deprecated in favor of interaction_metadata; sent if the message is a response to an interaction
    pub interaction: Option<Interaction>,

    /// The thread that was started from this mesage, includes thread member object
    pub thread: Option<Channel>,

    /// Components (e.g., buttons, select menus) included in the message
    #[serde(default)]
    pub components: Vec<serde_json::Value>,

    /// Sticker items included in the message
    #[serde(default)]
    pub sticker_items: Vec<Sticker>,

    /// Position of the message in a thread, will always be 0 in a channel not part of a thread
    pub position: Option<u64>,

    /// A poll !
    pub poll: Option<Poll>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Attachment {
    /// Unique ID of the attachment
    pub id: String,

    /// Filename of the attachment
    pub filename: String,

    /// Title of the file
    pub title: Option<String>,

    /// Description of the file
    pub description: Option<String>,

    /// The attachment's media type (e.g., "image/png")
    pub content_type: Option<String>,

    /// Size of the attachment in bytes
    pub size: u64,

    /// URL of the attachment
    pub url: String,

    /// Proxy URL of the attachment
    pub proxy_url: String,

    /// Height of the attachment (if it's an image)
    pub height: Option<u64>,

    /// Width of the attachment (if it's an image)
    pub width: Option<u64>,

    /// Whether the attachment is ephemeral (only sent in interactions)
    #[serde(default)]
    pub ephemeral: bool,

    /// Duration seconds (Currently for voice messages)
    pub duration_secs: Option<f64>,

    /// Waveform (Currently for voice messages)
    pub waveform: Option<String>,

    /// Attachment's flags (bitfield)
    pub flags: Option<u64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MessageActivity {
    #[serde(rename = "type")]
    pub kind: u8,

    pub party_id: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MessageReference {
    /// Type of the message reference (0 = default, 1 = forward)
    #[serde(rename = "type")]
    pub kind: Option<u8>,

    /// ID of the original message
    pub message_id: Option<String>,

    /// ID of the original channel
    pub channel_id: Option<String>,

    /// ID of the original guild
    pub guild_id: Option<String>,

    /// Whether to error if the referenced message doesn't exist (default true)
    #[serde(default)]
    pub fail_if_not_exists: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MessageInteractionMetadata {
    /// ID of the interaction
    pub id: String,

    /// Type of the interaction (1 = ping, 2 = application command, 3 = message component, 4 = application command autocomplete)
    pub kind: InteractionType,

    /// User that triggered the interaction
    pub user: User,

    /// ID of the original response message, present only on follow-up messages
    pub original_response_message_id: Option<String>,

    /// The user the command was run on, present only on user command interactions
    pub target_user: Option<User>,

    /// The ID of the message the command was run on, present only on message command interactions
    pub target_message_id: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Sticker {
    /// Unique ID of the sticker
    pub id: String,

    /// For standard stickers, the pack ID of the sticker
    pub pack_id: Option<String>,

    /// Name of the sticker
    pub name: String,

    /// Description of the sticker
    pub description: String,

    /// The type of sticker format (1 = PNG, 2 = APNG, 3 = Lottie)
    pub format_type: u8,

    /// Whether the sticker is available
    #[serde(default)]
    pub available: bool,

    /// The ID of the guild that owns this sticker, if it's a guild sticker
    pub guild_id: Option<String>,

    /// User that created this sticker
    pub user: Option<User>,

    /// The sort order of the sticker in the pack
    pub sort_value: Option<u64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SupplementalMessageRequest {
    /// The ID of the message request
    pub channel_id: String,
    /// The trigger message
    pub message_preview: Message,
}

impl Message {
    /// Checks if the message starts with a prefix
    pub fn starts_with(&self, prefix: &str) -> bool {
        self.content.starts_with(prefix)
    }

    /// Helper method to check if the message is a reply
    pub fn is_reply(&self) -> bool {
        self.message_reference.is_some()
    }

    /// Parses message as command + arguments
    pub fn parse_command(&self, prefix: &str) -> Option<(&str, Vec<&str>)> {
        let content = self.content.strip_prefix(prefix)?.trim();
        let mut parts = content.split_whitespace();
        let command = parts.next()?;
        let args: Vec<&str> = parts.collect();
        Some((command, args))
    }

    /// Gets the channel this message was sent in
    ///
    /// # Example
    /// ```ignore
    /// async fn example(ctx, msg) {
    ///     let channel = msg.channel(&ctx.http).await?;
    ///     println!("Message was sent in channel: {}", channel.name);
    ///     Ok(())
    /// }
    /// ```
    pub async fn channel(&self, http: &crate::http::HttpClient) -> Option<Channel> {
        let url = crate::http::api_url(&format!("/channels/{}", self.channel_id));
        if let Ok(response) = http.get(&url).await {
            if let Ok(channel) = serde_json::from_value(response) {
                return Some(channel);
            }
        }
        None
    }

    /// Replies to the message
    ///
    /// # Example
    /// ```ignore
    /// use diself::prelude::*;
    ///
    /// async fn example(ctx: &Context, msg: &Message) {
    ///     msg.reply(&ctx.http, "This is a reply!").await?;
    ///     Ok(())
    /// }
    /// ```
    pub async fn reply(
        &self,
        http: &crate::http::HttpClient,
        content: impl Into<String>,
    ) -> crate::Result<Message> {
        let url = crate::http::api_url(&format!("/channels/{}/messages", self.channel_id));
        let body = json!({
            "content": content.into(),
            "message_reference": {
                "message_id": self.id,
                "channel_id": self.channel_id,
                "fail_if_not_exists": false
            }
        });
        let response = http.post(&url, body).await?;
        let message: Message = serde_json::from_value(response)?;
        Ok(message)
    }

    /// Edits the message
    pub async fn edit(
        &self,
        http: &crate::http::HttpClient,
        new_content: impl Into<String>,
    ) -> crate::Result<Message> {
        let url = crate::http::api_url(&format!(
            "/channels/{}/messages/{}",
            self.channel_id, self.id
        ));
        let body = json!({
            "content": new_content.into()
        });
        let response = http.patch(&url, body).await?;
        let message: Message = serde_json::from_value(response)?;
        Ok(message)
    }

    /// Deletes the message
    pub async fn delete(&self, http: &crate::http::HttpClient) -> crate::Result<()> {
        let url = crate::http::api_url(&format!(
            "/channels/{}/messages/{}",
            self.channel_id, self.id
        ));
        http.delete(&url).await?;
        Ok(())
    }

    /// Adds a reaction to the message
    ///
    /// # Example
    /// ```ignore
    /// use diself::prelude::*;
    ///
    /// async fn example(ctx: &Context, msg: &Message) {
    ///     msg.react(&ctx.http, "👍").await?;
    ///     Ok(())
    /// }
    /// ```
    pub async fn react(
        &self,
        http: &crate::http::HttpClient,
        emoji: impl AsRef<str>,
    ) -> crate::Result<()> {
        let url = crate::http::api_url(&format!(
            "/channels/{}/messages/{}/reactions/{}/@me",
            self.channel_id,
            self.id,
            urlencoding::encode(emoji.as_ref())
        ));
        http.put(&url, json!({})).await?;
        Ok(())
    }

    /// Removes a reaction from the message
    pub async fn remove_reaction(
        &self,
        http: &crate::http::HttpClient,
        emoji: impl AsRef<str>,
    ) -> crate::Result<()> {
        let url = crate::http::api_url(&format!(
            "/channels/{}/messages/{}/reactions/{}/@me",
            self.channel_id,
            self.id,
            urlencoding::encode(emoji.as_ref())
        ));
        http.delete(&url).await?;
        Ok(())
    }

    /// Gets the reactions for the message
    pub async fn reactions(
        &self,
        http: &crate::http::HttpClient,
        emoji: impl AsRef<str>,
    ) -> crate::Result<Vec<Reaction>> {
        let url = crate::http::api_url(&format!(
            "/channels/{}/messages/{}/reactions/{}",
            self.channel_id,
            self.id,
            urlencoding::encode(emoji.as_ref())
        ));
        let response = http.get(&url).await?;
        let reactions: Vec<Reaction> = serde_json::from_value(response)?;
        Ok(reactions)
    }

    /// Deletes all reactions on a message.
    pub async fn clear_reactions(&self, http: &crate::http::HttpClient) -> crate::Result<()> {
        let url = crate::http::api_url(&format!(
            "/channels/{}/messages/{}/reactions",
            self.channel_id, self.id
        ));
        http.delete(&url).await?;
        Ok(())
    }

    /// Replies with an advanced message (embeds, tts, etc.)
    pub async fn reply_with(
        &self,
        http: &crate::http::HttpClient,
        message: crate::model::CreateMessage,
    ) -> crate::Result<Message> {
        let msg = crate::model::CreateMessage {
            message_reference: Some(crate::model::message_builder::MessageReferencePayload {
                message_id: self.id.clone(),
                channel_id: Some(self.channel_id.clone()),
                guild_id: None,
                fail_if_not_exists: false,
            }),
            ..message
        };
        let url = crate::http::api_url(&format!("/channels/{}/messages", self.channel_id));
        let response = http.post(&url, msg).await?;
        let message: Message = serde_json::from_value(response)?;
        Ok(message)
    }
}
