//! Service de gestion des rooms pour le chat server
//!
//! Ce module fournit un service de haut niveau pour gérer les rooms:
//! - Création et suppression de rooms
//! - Ajout et retrait d'utilisateurs
//! - Liste des utilisateurs d'une room

use crate::error::ChatError;
use crate::repository::{Room, RoomMember, RoomRepository};
use sqlx::PgPool;
use tracing::{debug, info, warn};
use uuid::Uuid;

/// Service de gestion des rooms
pub struct RoomService {
    repo: RoomRepository,
}

impl RoomService {
    /// Crée un nouveau service de gestion des rooms
    ///
    /// # Arguments
    ///
    /// * `pool` - Pool de connexions PostgreSQL
    pub fn new(pool: PgPool) -> Self {
        Self {
            repo: RoomRepository::new(pool),
        }
    }

    /// Crée une nouvelle room
    ///
    /// # Arguments
    ///
    /// * `name` - Nom de la room (optionnel)
    /// * `description` - Description de la room (optionnel)
    /// * `room_type` - Type de room (ex: "public", "private", "direct")
    /// * `is_private` - Si la room est privée
    /// * `creator_id` - ID du créateur de la room
    ///
    /// # Returns
    ///
    /// L'ID de la room créée
    ///
    /// # Errors
    ///
    /// Retourne une erreur si la création échoue
    pub async fn create_room(
        &self,
        name: Option<&str>,
        description: Option<&str>,
        room_type: &str,
        is_private: bool,
        creator_id: Uuid,
    ) -> Result<Uuid, ChatError> {
        info!(
            "Création d'une room: name={:?}, type={}, private={}, creator={}",
            name, room_type, is_private, creator_id
        );

        let room = self
            .repo
            .create(name, description, room_type, is_private, creator_id)
            .await
            .map_err(|e| {
                ChatError::internal_error(format!("Erreur lors de la création de la room: {}", e))
            })?;

        info!("✅ Room créée avec succès: {}", room.id);

        // Ajouter le créateur comme membre de la room avec le rôle "owner"
        let _ = self
            .add_user(room.id, creator_id, "owner")
            .await
            .map_err(|e| {
                warn!("⚠️ Impossible d'ajouter le créateur comme membre: {}", e);
                // On continue même si l'ajout échoue
            });

        Ok(room.id)
    }

    /// Supprime une room
    ///
    /// # Arguments
    ///
    /// * `room_id` - ID de la room à supprimer
    ///
    /// # Returns
    ///
    /// Ok(()) si la suppression réussit
    ///
    /// # Errors
    ///
    /// Retourne une erreur si la room n'existe pas ou si la suppression échoue
    pub async fn delete_room(&self, room_id: Uuid) -> Result<(), ChatError> {
        info!("Suppression de la room: {}", room_id);

        // Vérifier que la room existe
        let room = self.repo.get_by_id(room_id).await.map_err(|e| {
            ChatError::internal_error(format!("Erreur lors de la vérification de la room: {}", e))
        })?;

        if room.is_none() {
            return Err(ChatError::not_found("Room", &room_id.to_string()));
        }

        // Supprimer tous les membres de la room
        let members = self.repo.get_members(room_id).await.map_err(|e| {
            ChatError::internal_error(format!("Erreur lors de la récupération des membres: {}", e))
        })?;

        for member in members {
            let _ = self
                .repo
                .remove_member(room_id, member.user_id)
                .await
                .map_err(|e| {
                    warn!(
                        "⚠️ Erreur lors de la suppression du membre {}: {}",
                        member.user_id, e
                    );
                    // Continue même en cas d'erreur
                });
        }

        // Supprimer la room (nécessite une méthode delete dans le repository)
        // Pour l'instant, on utilise une requête SQL directe
        sqlx::query("DELETE FROM conversations WHERE id = $1")
            .bind(room_id)
            .execute(self.repo.pool())
            .await
            .map_err(|e| {
                ChatError::internal_error(format!(
                    "Erreur lors de la suppression de la room: {}",
                    e
                ))
            })?;

        info!("✅ Room {} supprimée avec succès", room_id);
        Ok(())
    }

    /// Ajoute un utilisateur à une room
    ///
    /// # Arguments
    ///
    /// * `room_id` - ID de la room
    /// * `user_id` - ID de l'utilisateur à ajouter
    /// * `role` - Rôle de l'utilisateur dans la room (ex: "member", "admin", "owner")
    ///
    /// # Returns
    ///
    /// Le membre créé
    ///
    /// # Errors
    ///
    /// Retourne une erreur si la room n'existe pas ou si l'ajout échoue
    pub async fn add_user(
        &self,
        room_id: Uuid,
        user_id: Uuid,
        role: &str,
    ) -> Result<RoomMember, ChatError> {
        debug!(
            "Ajout de l'utilisateur {} à la room {} avec le rôle {}",
            user_id, room_id, role
        );

        // Vérifier que la room existe
        let room = self.repo.get_by_id(room_id).await.map_err(|e| {
            ChatError::internal_error(format!("Erreur lors de la vérification de la room: {}", e))
        })?;

        if room.is_none() {
            return Err(ChatError::not_found("Room", &room_id.to_string()));
        }

        // Ajouter l'utilisateur à la room
        let member = self
            .repo
            .add_member(room_id, user_id, role)
            .await
            .map_err(|e| {
                ChatError::internal_error(format!(
                    "Erreur lors de l'ajout de l'utilisateur à la room: {}",
                    e
                ))
            })?;

        info!(
            "✅ Utilisateur {} ajouté à la room {} avec le rôle {}",
            user_id, room_id, role
        );
        Ok(member)
    }

    /// Retire un utilisateur d'une room
    ///
    /// # Arguments
    ///
    /// * `room_id` - ID de la room
    /// * `user_id` - ID de l'utilisateur à retirer
    ///
    /// # Returns
    ///
    /// Ok(()) si la suppression réussit
    ///
    /// # Errors
    ///
    /// Retourne une erreur si la room ou l'utilisateur n'existe pas, ou si la suppression échoue
    pub async fn remove_user(&self, room_id: Uuid, user_id: Uuid) -> Result<(), ChatError> {
        info!(
            "Retrait de l'utilisateur {} de la room {}",
            user_id, room_id
        );

        // Vérifier que la room existe
        let room = self.repo.get_by_id(room_id).await.map_err(|e| {
            ChatError::internal_error(format!("Erreur lors de la vérification de la room: {}", e))
        })?;

        if room.is_none() {
            return Err(ChatError::not_found("Room", &room_id.to_string()));
        }

        // Retirer l'utilisateur de la room
        self.repo
            .remove_member(room_id, user_id)
            .await
            .map_err(|e| {
                ChatError::internal_error(format!(
                    "Erreur lors du retrait de l'utilisateur de la room: {}",
                    e
                ))
            })?;

        info!("✅ Utilisateur {} retiré de la room {}", user_id, room_id);
        Ok(())
    }

    /// Liste tous les utilisateurs d'une room
    ///
    /// # Arguments
    ///
    /// * `room_id` - ID de la room
    ///
    /// # Returns
    ///
    /// Un vecteur de membres de la room
    ///
    /// # Errors
    ///
    /// Retourne une erreur si la room n'existe pas ou si la récupération échoue
    pub async fn list_users(&self, room_id: Uuid) -> Result<Vec<RoomMember>, ChatError> {
        debug!(
            "Récupération de la liste des utilisateurs de la room {}",
            room_id
        );

        // Vérifier que la room existe
        let room = self.repo.get_by_id(room_id).await.map_err(|e| {
            ChatError::internal_error(format!("Erreur lors de la vérification de la room: {}", e))
        })?;

        if room.is_none() {
            return Err(ChatError::not_found("Room", &room_id.to_string()));
        }

        // Récupérer les membres de la room
        let members = self.repo.get_members(room_id).await.map_err(|e| {
            ChatError::internal_error(format!("Erreur lors de la récupération des membres: {}", e))
        })?;

        debug!(
            "✅ {} membres trouvés dans la room {}",
            members.len(),
            room_id
        );
        Ok(members)
    }

    /// Obtient une room par son ID
    ///
    /// # Arguments
    ///
    /// * `room_id` - ID de la room
    ///
    /// # Returns
    ///
    /// La room si elle existe
    ///
    /// # Errors
    ///
    /// Retourne une erreur si la room n'existe pas
    pub async fn get_room(&self, room_id: Uuid) -> Result<Room, ChatError> {
        self.repo
            .get_by_id(room_id)
            .await
            .map_err(|e| {
                ChatError::internal_error(format!(
                    "Erreur lors de la récupération de la room: {}",
                    e
                ))
            })?
            .ok_or_else(|| ChatError::not_found("Room", &room_id.to_string()))
    }
}

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

    // Note: Les tests nécessitent une base de données de test
    // Ils sont marqués avec #[ignore] car ils nécessitent une configuration spécifique

    #[tokio::test]
    #[ignore] // Nécessite une base de données de test
    async fn test_create_room() {
        // Ce test nécessite un pool de test
        // let pool = create_test_pool().await;
        // let service = RoomService::new(pool);
        // let creator_id = Uuid::new_v4();
        //
        // let room_id = service
        //     .create_room(Some("Test Room"), None, "public", false, creator_id)
        //     .await
        //     .unwrap();
        //
        // assert!(!room_id.is_nil());
    }

    #[tokio::test]
    #[ignore] // Nécessite une base de données de test
    async fn test_add_and_remove_user() {
        // Ce test nécessite un pool de test et une room existante
        // let pool = create_test_pool().await;
        // let service = RoomService::new(pool);
        // let room_id = Uuid::new_v4();
        // let user_id = Uuid::new_v4();
        //
        // // Ajouter l'utilisateur
        // let member = service.add_user(room_id, user_id, "member").await.unwrap();
        // assert_eq!(member.user_id, user_id);
        //
        // // Retirer l'utilisateur
        // service.remove_user(room_id, user_id).await.unwrap();
        //
        // // Vérifier que l'utilisateur n'est plus dans la room
        // let members = service.list_users(room_id).await.unwrap();
        // assert!(!members.iter().any(|m| m.user_id == user_id));
    }

    #[tokio::test]
    #[ignore] // Nécessite une base de données de test
    async fn test_list_users() {
        // Ce test nécessite un pool de test et une room avec des membres
        // let pool = create_test_pool().await;
        // let service = RoomService::new(pool);
        // let room_id = Uuid::new_v4();
        //
        // let members = service.list_users(room_id).await.unwrap();
        // assert!(members.is_empty() || !members.is_empty()); // Au moins vérifier que ça ne panique pas
    }
}
