use super::{InteractionData, InteractionDataType, InteractionType};
use crate::api::BotApi;
use crate::models::serde_helpers::{deserialize_string_or_number, is_default};
use serde::{Deserialize, Serialize};
use serde_json::Value;

/// Interaction structure representing user interactions
#[derive(Debug, Clone, Serialize)]
pub struct Interaction {
    /// API client reference
    #[serde(skip)]
    api: BotApi,
    /// Interaction ID
    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<String>,
    /// Application ID
    #[serde(skip_serializing_if = "Option::is_none")]
    pub application_id: Option<String>,
    /// Interaction type
    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
    pub interaction_type: Option<InteractionType>,
    /// Scene identifier
    #[serde(skip_serializing_if = "Option::is_none")]
    pub scene: Option<String>,
    /// Chat type
    #[serde(skip_serializing_if = "Option::is_none")]
    pub chat_type: Option<u64>,
    /// Event ID
    #[serde(skip_serializing_if = "Option::is_none")]
    pub event_id: Option<String>,
    /// Interaction data
    #[serde(skip_serializing_if = "is_default")]
    pub data: InteractionData,
    /// Guild ID
    #[serde(skip_serializing_if = "Option::is_none")]
    pub guild_id: Option<String>,
    /// Channel ID
    #[serde(skip_serializing_if = "Option::is_none")]
    pub channel_id: Option<String>,
    /// User OpenID
    #[serde(skip_serializing_if = "Option::is_none")]
    pub user_openid: Option<String>,
    /// Group OpenID
    #[serde(skip_serializing_if = "Option::is_none")]
    pub group_openid: Option<String>,
    /// Group member OpenID
    #[serde(skip_serializing_if = "Option::is_none")]
    pub group_member_openid: Option<String>,
    /// Timestamp
    #[serde(skip_serializing_if = "Option::is_none")]
    pub timestamp: Option<String>,
    /// Version
    #[serde(skip_serializing_if = "Option::is_none")]
    pub version: Option<u64>,
}

impl Interaction {
    /// Builds an interaction event from the gateway payload.
    pub fn new(api: BotApi, event_id: Option<String>, data: &Value) -> Self {
        let wire: InteractionWire = serde_json::from_value(data.clone()).unwrap_or_default();
        Self {
            api,
            event_id,
            id: wire.id,
            application_id: wire.application_id,
            interaction_type: wire.interaction_type,
            scene: wire.scene,
            chat_type: wire.chat_type,
            data: wire.data,
            guild_id: wire.guild_id,
            channel_id: wire.channel_id,
            user_openid: wire.user_openid,
            group_openid: wire.group_openid,
            group_member_openid: wire.group_member_openid,
            timestamp: wire.timestamp,
            version: wire.version,
        }
    }

    /// Get the API client reference
    pub fn api(&self) -> &BotApi {
        &self.api
    }

    /// Check if this is a button interaction
    pub fn is_button_interaction(&self) -> bool {
        matches!(
            self.data.data_type,
            Some(InteractionDataType::InlineKeyboardButtonClick)
        )
    }

    /// Check if this is a command interaction
    pub fn is_command_interaction(&self) -> bool {
        matches!(
            self.interaction_type,
            Some(InteractionType::ApplicationCommand)
        )
    }

    /// Get the button ID if this is a button interaction
    pub fn button_id(&self) -> Option<&str> {
        (!self.data.resolved.button_id.is_empty()).then_some(self.data.resolved.button_id.as_str())
    }

    /// Get the button data if this is a button interaction
    pub fn button_data(&self) -> Option<&str> {
        (!self.data.resolved.button_data.is_empty())
            .then_some(self.data.resolved.button_data.as_str())
    }
}

impl std::fmt::Display for Interaction {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Interaction {{ id: {:?}, type: {:?}, scene: {:?}, chat_type: {:?}, event_id: {:?} }}",
            self.id, self.interaction_type, self.scene, self.chat_type, self.event_id
        )
    }
}

#[derive(Debug, Default, Deserialize)]
struct InteractionWire {
    #[serde(default)]
    id: Option<String>,
    #[serde(default, deserialize_with = "deserialize_string_or_number")]
    application_id: Option<String>,
    #[serde(rename = "type", default)]
    interaction_type: Option<InteractionType>,
    #[serde(default)]
    scene: Option<String>,
    #[serde(default)]
    chat_type: Option<u64>,
    #[serde(default)]
    data: InteractionData,
    #[serde(default)]
    guild_id: Option<String>,
    #[serde(default)]
    channel_id: Option<String>,
    #[serde(default)]
    user_openid: Option<String>,
    #[serde(default)]
    group_openid: Option<String>,
    #[serde(default)]
    group_member_openid: Option<String>,
    #[serde(default, deserialize_with = "deserialize_string_or_number")]
    timestamp: Option<String>,
    #[serde(default)]
    version: Option<u64>,
}
