use serde::{Deserialize, Serialize};
use sqlx::types::chrono::{DateTime, Utc};
use sqlx::PgPool;
use std::collections::HashMap;
use tracing::{debug, info, instrument};
use uuid::Uuid;

/// Émoji de réaction supporté
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ReactionEmoji {
    Like,
    Love,
    Haha,
    Wow,
    Sad,
    Angry,
}

impl ReactionEmoji {
    pub fn as_str(&self) -> &'static str {
        match self {
            ReactionEmoji::Like => "👍",
            ReactionEmoji::Love => "❤️",
            ReactionEmoji::Haha => "😂",
            ReactionEmoji::Wow => "😮",
            ReactionEmoji::Sad => "😢",
            ReactionEmoji::Angry => "😠",
        }
    }

    pub fn from_str(emoji: &str) -> Option<Self> {
        match emoji {
            "👍" => Some(ReactionEmoji::Like),
            "❤️" => Some(ReactionEmoji::Love),
            "😂" => Some(ReactionEmoji::Haha),
            "😮" => Some(ReactionEmoji::Wow),
            "😢" => Some(ReactionEmoji::Sad),
            "😠" => Some(ReactionEmoji::Angry),
            _ => None,
        }
    }
}

/// Représente une réaction sur un message
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MessageReaction {
    pub message_id: Uuid,
    pub user_id: Uuid,
    pub emoji: ReactionEmoji,
    pub created_at: DateTime<Utc>,
}

/// Manager pour gérer les réactions sur les messages
pub struct ReactionsManager {
    pool: PgPool,
}

impl ReactionsManager {
    pub fn new(pool: PgPool) -> Self {
        Self { pool }
    }

    /// Ajouter une réaction à un message
    #[instrument(skip(self))]
    pub async fn add_reaction(
        &self,
        message_id: Uuid,
        user_id: Uuid,
        emoji: ReactionEmoji,
    ) -> Result<(), sqlx::Error> {
        // Vérifier si l'utilisateur a déjà réagi à ce message
        let existing: Option<(i32,)> = sqlx::query_as(
            "SELECT id FROM message_reactions 
             WHERE message_id = $1 AND user_id = $2"
        )
        .bind(message_id)
        .bind(user_id)
        .fetch_optional(&self.pool)
        .await?;

        if let Some(_) = existing {
            // L'utilisateur a déjà réagi, supprimer la réaction existante
            sqlx::query(
                "DELETE FROM message_reactions WHERE message_id = $1 AND user_id = $2"
            )
            .bind(message_id)
            .bind(user_id)
            .execute(&self.pool)
            .await?;

            debug!(
                message_id = %message_id,
                user_id = %user_id,
                "Existing reaction removed"
            );
        }

        // Ajouter la nouvelle réaction
        sqlx::query(
            "INSERT INTO message_reactions (message_id, user_id, reaction_type, created_at) 
             VALUES ($1, $2, $3, NOW())"
        )
        .bind(message_id)
        .bind(user_id)
        .bind(emoji.as_str())
        .execute(&self.pool)
        .await?;

        info!(
            message_id = %message_id,
            user_id = %user_id,
            emoji = %emoji.as_str(),
            "Reaction added to message"
        );

        Ok(())
    }

    /// Retirer une réaction d'un message
    #[instrument(skip(self))]
    pub async fn remove_reaction(
        &self,
        message_id: Uuid,
        user_id: Uuid,
    ) -> Result<(), sqlx::Error> {
        sqlx::query(
            "DELETE FROM message_reactions WHERE message_id = $1 AND user_id = $2"
        )
        .bind(message_id)
        .bind(user_id)
        .execute(&self.pool)
        .await?;

        info!(
            message_id = %message_id,
            user_id = %user_id,
            "Reaction removed from message"
        );

        Ok(())
    }

    /// Obtenir toutes les réactions d'un message
    #[instrument(skip(self))]
    pub async fn get_message_reactions(
        &self,
        message_id: Uuid,
    ) -> Result<HashMap<ReactionEmoji, Vec<Uuid>>, sqlx::Error> {
        let reactions: Vec<(String, Uuid)> = sqlx::query_as(
            "SELECT reaction_type, user_id FROM message_reactions WHERE message_id = $1"
        )
        .bind(message_id)
        .fetch_all(&self.pool)
        .await?;

        let mut result = HashMap::new();
        
        for (emoji_str, user_id) in reactions {
            if let Some(emoji) = ReactionEmoji::from_str(&emoji_str) {
                result.entry(emoji).or_insert_with(Vec::new).push(user_id);
            }
        }

        Ok(result)
    }

    /// Obtenir le nombre de réactions par émoji pour un message
    #[instrument(skip(self))]
    pub async fn get_reaction_counts(
        &self,
        message_id: Uuid,
    ) -> Result<HashMap<ReactionEmoji, usize>, sqlx::Error> {
        let reactions = self.get_message_reactions(message_id).await?;
        
        let counts: HashMap<ReactionEmoji, usize> = reactions
            .iter()
            .map(|(emoji, users)| (emoji.clone(), users.len()))
            .collect();

        Ok(counts)
    }

    /// Obtenir les réactions d'un user pour tous les messages d'une conversation
    #[instrument(skip(self))]
    pub async fn get_user_reactions_in_conversation(
        &self,
        conversation_id: Uuid,
        user_id: Uuid,
    ) -> Result<HashMap<Uuid, ReactionEmoji>, sqlx::Error> {
        let reactions: Vec<(Uuid, String)> = sqlx::query_as(
            "SELECT mr.message_id, mr.reaction_type 
             FROM message_reactions mr
             JOIN messages m ON m.id = mr.message_id
             WHERE m.conversation_id = $1 AND mr.user_id = $2"
        )
        .bind(conversation_id)
        .bind(user_id)
        .fetch_all(&self.pool)
        .await?;

        let mut result = HashMap::new();
        
        for (message_id, emoji_str) in reactions {
            if let Some(emoji) = ReactionEmoji::from_str(&emoji_str) {
                result.insert(message_id, emoji);
            }
        }

        Ok(result)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_reaction_emoji_conversion() {
        assert_eq!(ReactionEmoji::Like.as_str(), "👍");
        assert_eq!(ReactionEmoji::from_str("👍"), Some(ReactionEmoji::Like));
        assert_eq!(ReactionEmoji::from_str("invalid"), None);
    }
}
