//! Handler WebSocket pour le chat server
//!
//! Ce module fournit un handler WebSocket utilisant Axum pour gérer
//! les connexions, déconnexions et le routage des messages de chat.

use axum::extract::ws::{Message, WebSocket};
use axum::extract::{Query, State, WebSocketUpgrade};
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use futures_util::StreamExt;
use serde_json;
use std::collections::HashMap;
use std::sync::Arc;
use tracing::{debug, error, info, info_span, warn, Instrument};
use uuid::Uuid;

use crate::reactions::ReactionsManager;
use crate::websocket::{IncomingMessage, OutgoingMessage, WebSocketClient, WebSocketManager};

/// État partagé pour le handler WebSocket
#[derive(Clone)]
pub struct WebSocketState {
    // pub store: Arc<SimpleMessageStore>, // Remove SimpleMessageStore
    pub message_repo: Arc<MessageRepository>, // Add MessageRepository
    pub read_receipt_manager: Arc<ReadReceiptManager>, // Add ReadReceiptManager
    pub delivered_status_manager: Arc<DeliveredStatusManager>, // Add DeliveredStatusManager
    pub typing_indicator_manager: Arc<TypingIndicatorManager>, // Add TypingIndicatorManager
    pub message_edit_service: Arc<MessageEditService>, // Add MessageEditService
    pub reactions_manager: Arc<ReactionsManager>, // Add ReactionsManager
    pub ws_manager: Arc<WebSocketManager>,
    pub jwt_manager: Arc<JwtManager>,
    pub permission_service: Arc<PermissionService>, // Add PermissionService
    pub metrics: Arc<ChatMetrics>,
}

/// Handler principal pour les connexions WebSocket
///
/// Cette fonction gère la mise à niveau de la connexion HTTP vers WebSocket
/// et délègue la gestion de la connexion à `handle_socket`.
/// FIX #13: Extrait le request_id depuis les extensions Axum (ajouté par le middleware)
pub async fn websocket_handler(
    ws: WebSocketUpgrade,
    Query(params): Query<HashMap<String, String>>,
    State(state): State<WebSocketState>,
    // FIX #13: Extraire le request_id depuis les extensions (ajouté par request_id_middleware)
    request_id: Option<axum::extract::Extension<Uuid>>,
) -> Response {
    // FIX #13: Extraire le request_id pour la corrélation
    let request_id = request_id.map(|ext| *ext).unwrap_or_else(Uuid::new_v4);
    
    // Créer un span avec le request_id pour la corrélation
    let span = info_span!(
        "websocket_upgrade",
        request_id = %request_id,
    );
    
    async move {
        info!(request_id = %request_id, "🔌 Nouvelle connexion WebSocket demandée");

        let token = match params.get("token") {
            Some(t) => t,
            None => {
                error!(request_id = %request_id, "❌ Token manquant");
                return (StatusCode::UNAUTHORIZED, "Missing token").into_response();
            }
        };

        // Validate token
        match state.jwt_manager.validate_access_token(token).await {
            Ok(claims) => {
                info!(
                    request_id = %request_id,
                    username = %claims.username,
                    user_id = %claims.user_id,
                    "✅ Connexion autorisée"
                );
                // FIX #13: Passer le request_id au handler de socket
                ws.on_upgrade(move |socket| handle_socket(socket, state, claims, request_id))
            }
            Err(e) => {
                error!(request_id = %request_id, error = %e, "❌ Token invalide");
                (StatusCode::UNAUTHORIZED, "Invalid token").into_response()
            }
        }
    }
    .instrument(span)
    .await
}

/// Gère une connexion WebSocket individuelle
///
/// Note: Toutes les erreurs sont gérées explicitement pour éviter les panics.
/// Tokio capture automatiquement les panics dans les handlers, mais nous
/// nous assurons que toutes les erreurs sont gérées explicitement avec `?` ou `match`.
/// FIX #13: Accepte le request_id pour la corrélation
async fn handle_socket(
    socket: WebSocket,
    state: WebSocketState,
    claims: AccessTokenClaims,
    request_id: Uuid,
) {
    // FIX #13: Créer un span avec le request_id pour toute la durée de la connexion WebSocket
    let span = info_span!(
        "websocket_connection",
        request_id = %request_id,
        user_id = %claims.user_id,
        username = %claims.username,
    );
    
    async move {
    let (sender, mut receiver) = socket.split();

    // ID du client (généré localement pour cette connexion)
    let client_id = Uuid::new_v4();
    // MIGRATION UUID: user_id est déjà String (UUID)
    let client = Arc::new(WebSocketClient::new(
        client_id,
        claims.user_id.clone(),
        sender,
    ));

    state.ws_manager.add_client(client.clone()).await;

    info!(
        request_id = %request_id,
        client_id = %client_id,
        username = %claims.username,
        "✅ Connexion WebSocket établie"
    );

    // Metrics: connection
    state
        .metrics
        .websocket_connected(claims.user_id.clone())
        .await;

    // Envoyer un message de bienvenue
    let welcome_msg = OutgoingMessage::ActionConfirmed {
        action: "connected".to_string(),
        success: true,
    };

    if client.send_message(welcome_msg).await.is_err() {
        error!("❌ Impossible d'envoyer le message de bienvenue");
        state.ws_manager.remove_client(client_id).await;
        return;
    }

    // Timeout d'inactivité (Heartbeat)
    let keepalive_timeout = std::time::Duration::from_secs(60);

    // Boucle principale de gestion des messages avec timeout
    loop {
        match tokio::time::timeout(keepalive_timeout, receiver.next()).await {
            Ok(Some(msg)) => {
                match msg {
                    Ok(Message::Text(text)) => {
                        debug!("📨 Message WebSocket reçu: {}", text);

                        match handle_incoming_message(&text, &state, client.clone(), &claims).await
                        {
                            Ok(should_continue) => {
                                if !should_continue {
                                    break;
                                }
                            }
                            Err(e) => {
                                error!("❌ Erreur lors du traitement du message: {}", e);

                                // Envoyer un message d'erreur au client
                                let error_msg = OutgoingMessage::Error {
                                    message: format!("Erreur: {}", e),
                                };
                                if client.send_message(error_msg).await.is_err() {
                                    error!("❌ Impossible d'envoyer le message d'erreur au client");
                                    break; // Fermer la connexion si on ne peut même pas envoyer d'erreur
                                }
                            }
                        }
                    }
                    Ok(Message::Close(_)) => {
                        info!("👋 Connexion WebSocket fermée par le client");
                        break;
                    }
                    Ok(Message::Ping(_)) => {
                        debug!("🏓 Ping WebSocket reçu");
                        if client.send_message(OutgoingMessage::Pong).await.is_err() {
                            error!("❌ Erreur lors de l'envoi du Pong");
                            break;
                        }
                    }
                    Ok(Message::Pong(_)) => {
                        debug!("🏓 Pong WebSocket reçu");
                    }
                    Ok(_) => {
                        debug!("⚠️ Type de message WebSocket non géré");
                    }
                    Err(e) => {
                        error!("❌ Erreur WebSocket: {}", e);
                        break;
                    }
                }
            }
            Ok(None) => {
                // Fin du stream
                break;
            }
            Err(_) => {
                info!(
                    "💤 Timeout inactivité ({}s) pour client {}, fermeture",
                    keepalive_timeout.as_secs(),
                    client_id
                );
                break;
            }
        }
    }

    info!(
        request_id = %request_id,
        client_id = %client_id,
        username = %claims.username,
        "🔌 Connexion WebSocket terminée"
    );
    state.ws_manager.remove_client(client_id).await;

    // Metrics: disconnection
    state.metrics.websocket_disconnected(claims.user_id).await;
    }
    .instrument(span)
    .await;
}

/// Traite un message entrant et route selon le type
///
/// Retourne `Ok(true)` si la connexion doit continuer, `Ok(false)` pour fermer
async fn handle_incoming_message(
    text: &str,
    state: &WebSocketState,
    client: Arc<WebSocketClient>,
    claims: &AccessTokenClaims,
) -> Result<bool, ChatError> {
    // Parser le message JSON
    let incoming: IncomingMessage = serde_json::from_str(text)
        .map_err(|e| ChatError::serialization_error("IncomingMessage", text, e))?;

    match incoming {
        IncomingMessage::SendMessage {
            conversation_id,
            content,
            parent_message_id,
            attachments,
        } => {
            info!(
                "💬 Envoi de message via WebSocket par {} (conversation: {})",
                claims.username, conversation_id
            );

            // MIGRATION UUID: user_id est déjà String (UUID), on le parse directement
            let sender_uuid = Uuid::parse_str(&claims.user_id)
                .map_err(|e| ChatError::validation_error(&format!("Invalid user UUID: {}", e)))?;

            // Vérifier les permissions avant d'envoyer le message
            state
                .permission_service
                .can_send_message(sender_uuid, conversation_id)
                .await
                .map_err(|e| {
                    warn!(
                        user_id = %sender_uuid,
                        conversation_id = %conversation_id,
                        error = %e,
                        "Permission refusée pour l'envoi de message"
                    );
                    e
                })?;

            // Préparer les métadonnées pour les pièces jointes
            let metadata = attachments.as_ref().map(|a| serde_json::to_value(a).unwrap_or(serde_json::Value::Null));

            // Enregistrer le message dans le store
            // Note: On pourrait étendre MessageRepository::create pour accepter metadata et parent_message_id
            let message = state
                .message_repo
                .create(conversation_id, sender_uuid, &content)
                .await
                .map_err(|e| {
                    ChatError::internal_error(format!(
                        "Erreur lors de l'enregistrement du message: {}",
                        e
                    ))
                })?;

            // Diffuser le message à tous les clients de la conversation
            let outgoing_message = OutgoingMessage::NewMessage {
                conversation_id,
                message_id: message.id,
                sender_id: message.sender_id,
                content: message.content.clone(),
                created_at: message.created_at,
                attachments,
            };
            state
                .ws_manager
                .broadcast_to_conversation(conversation_id, outgoing_message)
                .await?;

            // Envoyer confirmation au sender
            let confirmation = OutgoingMessage::ActionConfirmed {
                action: "message_sent".to_string(),
                success: true,
            };
            client.send_message(confirmation).await?;

            info!(
                "✅ Message WebSocket envoyé et diffusé - ID: {}",
                message.id
            );
        }
        IncomingMessage::AddReaction {
            message_id,
            conversation_id,
            emoji,
        } => {
            info!(
                "❤️ Ajout de réaction {} au message {} par {}",
                emoji, message_id, claims.username
            );

            let user_uuid = Uuid::parse_str(&claims.user_id)
                .map_err(|e| ChatError::validation_error(&format!("Invalid user UUID: {}", e)))?;

            // Vérifier les permissions
            state
                .permission_service
                .can_read_conversation(user_uuid, conversation_id)
                .await
                .map_err(|e| {
                    warn!(
                        user_id = %user_uuid,
                        conversation_id = %conversation_id,
                        error = %e,
                        "Permission refusée pour ajouter une réaction"
                    );
                    e
                })?;

            // Convertir l'emoji string en enum (optionnel, on peut aussi stocker le string directement)
            if let Some(reaction_emoji) = crate::reactions::ReactionEmoji::from_str(&emoji) {
                state
                    .reactions_manager
                    .add_reaction(message_id, user_uuid, reaction_emoji)
                    .await
                    .map_err(|e| ChatError::internal_error(format!("Erreur DB réaction: {}", e)))?;

                // Diffuser la réaction
                let reaction_msg = OutgoingMessage::ReactionAdded {
                    message_id,
                    conversation_id,
                    user_id: user_uuid,
                    emoji,
                };
                state
                    .ws_manager
                    .broadcast_to_conversation(conversation_id, reaction_msg)
                    .await?;

                client.send_message(OutgoingMessage::ActionConfirmed {
                    action: "reaction_added".to_string(),
                    success: true,
                }).await?;
            } else {
                return Err(ChatError::validation_error("Emoji non supporté"));
            }
        }
        IncomingMessage::RemoveReaction {
            message_id,
            conversation_id,
        } => {
            info!(
                "💔 Retrait de réaction du message {} par {}",
                message_id, claims.username
            );

            let user_uuid = Uuid::parse_str(&claims.user_id)
                .map_err(|e| ChatError::validation_error(&format!("Invalid user UUID: {}", e)))?;

            state
                .reactions_manager
                .remove_reaction(message_id, user_uuid)
                .await
                .map_err(|e| ChatError::internal_error(format!("Erreur DB réaction: {}", e)))?;

            // Diffuser le retrait
            let reaction_msg = OutgoingMessage::ReactionRemoved {
                message_id,
                conversation_id,
                user_id: user_uuid,
            };
            state
                .ws_manager
                .broadcast_to_conversation(conversation_id, reaction_msg)
                .await?;

            client.send_message(OutgoingMessage::ActionConfirmed {
                action: "reaction_removed".to_string(),
                success: true,
            }).await?;
        }
        IncomingMessage::JoinConversation { conversation_id } => {
            info!(
                "🔗 Client {} ({}) rejoint la conversation {}",
                client.id, claims.username, conversation_id
            );

            // MIGRATION UUID: user_id est déjà String (UUID), on le parse directement
            let user_uuid = Uuid::parse_str(&claims.user_id)
                .map_err(|e| ChatError::validation_error(&format!("Invalid user UUID: {}", e)))?;

            // Vérifier les permissions avant de rejoindre
            state
                .permission_service
                .can_join_conversation(user_uuid, conversation_id)
                .await
                .map_err(|e| {
                    warn!(
                        user_id = %user_uuid,
                        conversation_id = %conversation_id,
                        error = %e,
                        "Permission refusée pour rejoindre la conversation"
                    );
                    e
                })?;

            client.add_conversation(conversation_id).await;

            let outgoing = OutgoingMessage::ActionConfirmed {
                action: "joined_conversation".to_string(),
                success: true,
            };
            client.send_message(outgoing).await?;
        }
        IncomingMessage::LeaveConversation { conversation_id } => {
            info!(
                "🔚 Client {} ({}) quitte la conversation {}",
                client.id, claims.username, conversation_id
            );

            client.remove_conversation(conversation_id).await;

            let outgoing = OutgoingMessage::ActionConfirmed {
                action: "left_conversation".to_string(),
                success: true,
            };
            client.send_message(outgoing).await?;
        }
        IncomingMessage::MarkAsRead {
            conversation_id,
            message_id,
        } => {
            info!(
                "👁️ Client {} marque le message {} comme lu dans {}",
                client.id, message_id, conversation_id
            );

            // Parser l'user_id depuis les claims JWT
            let user_uuid = Uuid::parse_str(&claims.user_id)
                .map_err(|e| ChatError::validation_error(&format!("Invalid user UUID: {}", e)))?;

            // Vérifier que le message existe
            let message = state
                .message_repo
                .get_by_id(message_id)
                .await
                .map_err(|e| {
                    ChatError::internal_error(format!(
                        "Erreur lors de la récupération du message: {}",
                        e
                    ))
                })?;

            let message =
                message.ok_or_else(|| ChatError::not_found("Message", &message_id.to_string()))?;

            // Vérifier que le message appartient à la conversation indiquée
            if message.conversation_id != conversation_id {
                return Err(ChatError::validation_error(
                    "Le message n'appartient pas à la conversation indiquée",
                ));
            }

            // Vérifier les permissions pour marquer comme lu
            state
                .permission_service
                .can_mark_read(user_uuid, conversation_id)
                .await
                .map_err(|e| {
                    warn!(
                        user_id = %user_uuid,
                        conversation_id = %conversation_id,
                        error = %e,
                        "Permission refusée pour marquer comme lu"
                    );
                    e
                })?;

            // Marquer le message comme lu
            let receipt = state
                .read_receipt_manager
                .mark_as_read(user_uuid, message_id, conversation_id)
                .await
                .map_err(|e| {
                    ChatError::internal_error(format!("Erreur lors du marquage comme lu: {}", e))
                })?;

            // Créer le message outbound pour notifier les autres participants
            let message_read = OutgoingMessage::MessageRead {
                message_id,
                user_id: user_uuid,
                conversation_id,
                read_at: receipt.read_at,
            };

            // Broadcast aux autres participants de la conversation
            state
                .ws_manager
                .broadcast_to_conversation(conversation_id, message_read.clone())
                .await?;

            // Envoyer confirmation au client qui a initié l'action
            let confirmation = OutgoingMessage::ActionConfirmed {
                action: "marked_as_read".to_string(),
                success: true,
            };
            client.send_message(confirmation).await?;

            info!(
                "✅ Message {} marqué comme lu par {} dans la conversation {}",
                message_id, user_uuid, conversation_id
            );
        }
        IncomingMessage::Typing {
            conversation_id,
            is_typing,
        } => {
            info!(
                "⌨️ Client {} ({}) typing indicator: {} dans conversation {}",
                client.id, claims.username, is_typing, conversation_id
            );

            // Parser l'user_id depuis les claims JWT
            let user_uuid = Uuid::parse_str(&claims.user_id)
                .map_err(|e| ChatError::validation_error(&format!("Invalid user UUID: {}", e)))?;

            // Vérifier les permissions avant d'envoyer le signal typing
            state
                .permission_service
                .can_send_message(user_uuid, conversation_id)
                .await
                .map_err(|e| {
                    warn!(
                        user_id = %user_uuid,
                        conversation_id = %conversation_id,
                        error = %e,
                        "Permission refusée pour typing indicator"
                    );
                    e
                })?;

            if is_typing {
                // User a commencé à taper
                state
                    .typing_indicator_manager
                    .user_started_typing(user_uuid, conversation_id)
                    .await;
            } else {
                // User a arrêté de taper
                state
                    .typing_indicator_manager
                    .user_stopped_typing(user_uuid, conversation_id)
                    .await;
            }

            // Broadcast aux autres participants de la conversation
            let typing_message = OutgoingMessage::UserTyping {
                conversation_id,
                user_id: user_uuid,
                is_typing,
            };
            state
                .ws_manager
                .broadcast_to_conversation(conversation_id, typing_message.clone())
                .await?;

            // Envoyer confirmation au client qui a initié l'action
            let confirmation = OutgoingMessage::ActionConfirmed {
                action: "typing_indicator".to_string(),
                success: true,
            };
            client.send_message(confirmation).await?;

            info!(
                "✅ Typing indicator {} diffusé pour {} dans la conversation {}",
                if is_typing { "activé" } else { "désactivé" },
                user_uuid,
                conversation_id
            );
        }
        IncomingMessage::Delivered {
            conversation_id,
            message_id,
        } => {
            info!(
                "📬 Client {} ({}) marque le message {} comme délivré dans {}",
                client.id, message_id, conversation_id, claims.username
            );

            // Parser l'user_id depuis les claims JWT
            let user_uuid = Uuid::parse_str(&claims.user_id)
                .map_err(|e| ChatError::validation_error(&format!("Invalid user UUID: {}", e)))?;

            // Vérifier les permissions pour marquer comme délivré
            state
                .permission_service
                .can_read_conversation(user_uuid, conversation_id)
                .await
                .map_err(|e| {
                    warn!(
                        user_id = %user_uuid,
                        conversation_id = %conversation_id,
                        error = %e,
                        "Permission refusée pour marquer comme délivré"
                    );
                    e
                })?;

            // Vérifier que le message existe et appartient à la conversation
            let message = state
                .message_repo
                .get_by_id(message_id)
                .await
                .map_err(|e| {
                    ChatError::internal_error(format!(
                        "Erreur lors de la récupération du message: {}",
                        e
                    ))
                })?;

            let message =
                message.ok_or_else(|| ChatError::not_found("Message", &message_id.to_string()))?;

            // Vérifier que le message appartient à la conversation indiquée
            if message.conversation_id != conversation_id {
                return Err(ChatError::validation_error(
                    "Le message n'appartient pas à la conversation indiquée",
                ));
            }

            // Vérifier que le message appartient bien à la conversation (double vérification)
            let belongs = state
                .delivered_status_manager
                .verify_message_belongs_to_conversation(message_id, conversation_id)
                .await
                .map_err(|e| {
                    ChatError::internal_error(format!(
                        "Erreur lors de la vérification du message: {}",
                        e
                    ))
                })?;

            if !belongs {
                return Err(ChatError::validation_error(
                    "Le message n'appartient pas à la conversation indiquée",
                ));
            }

            // Marquer le message comme délivré
            let status = state
                .delivered_status_manager
                .mark_delivered(user_uuid, message_id, conversation_id)
                .await
                .map_err(|e| {
                    ChatError::internal_error(format!(
                        "Erreur lors du marquage comme délivré: {}",
                        e
                    ))
                })?;

            // Créer le message outbound pour notifier les autres participants
            let message_delivered = OutgoingMessage::MessageDelivered {
                message_id,
                user_id: user_uuid,
                conversation_id,
                delivered_at: status.delivered_at,
            };

            // Broadcast aux autres participants de la conversation
            state
                .ws_manager
                .broadcast_to_conversation(conversation_id, message_delivered.clone())
                .await?;

            // Envoyer confirmation au client qui a initié l'action
            let confirmation = OutgoingMessage::ActionConfirmed {
                action: "marked_as_delivered".to_string(),
                success: true,
            };
            client.send_message(confirmation).await?;

            info!(
                "✅ Message {} marqué comme délivré par {} dans la conversation {}",
                message_id, user_uuid, conversation_id
            );
        }
        IncomingMessage::EditMessage {
            message_id,
            conversation_id,
            new_content,
        } => {
            info!(
                "✏️ Client {} ({}) édite le message {} dans {}",
                client.id, claims.username, message_id, conversation_id
            );

            // Parser l'user_id depuis les claims JWT
            let user_uuid = Uuid::parse_str(&claims.user_id)
                .map_err(|e| ChatError::validation_error(&format!("Invalid user UUID: {}", e)))?;

            // Éditer le message via le service
            let updated_message = state
                .message_edit_service
                .edit_message(user_uuid, message_id, &new_content)
                .await
                .map_err(|e| {
                    warn!(
                        user_id = %user_uuid,
                        message_id = %message_id,
                        error = %e,
                        "Erreur lors de l'édition du message"
                    );
                    e
                })?;

            // Vérifier que le message appartient à la conversation indiquée
            if updated_message.conversation_id != conversation_id {
                return Err(ChatError::validation_error(
                    "Le message n'appartient pas à la conversation indiquée",
                ));
            }

            // Créer le message outbound pour notifier les autres participants
            let message_edited = OutgoingMessage::MessageEdited {
                message_id,
                conversation_id,
                editor_id: user_uuid,
                edited_at: updated_message
                    .edited_at
                    .unwrap_or(updated_message.updated_at),
                new_content: updated_message.content.clone(),
            };

            // Broadcast aux autres participants de la conversation
            state
                .ws_manager
                .broadcast_to_conversation(conversation_id, message_edited.clone())
                .await?;

            // Envoyer confirmation au client qui a initié l'action
            let confirmation = OutgoingMessage::ActionConfirmed {
                action: "message_edited".to_string(),
                success: true,
            };
            client.send_message(confirmation).await?;

            info!(
                "✅ Message {} édité par {} dans la conversation {}",
                message_id, user_uuid, conversation_id
            );
        }
        IncomingMessage::DeleteMessage {
            message_id,
            conversation_id,
        } => {
            info!(
                "🗑️ Client {} ({}) supprime le message {} dans {}",
                client.id, claims.username, message_id, conversation_id
            );

            // Parser l'user_id depuis les claims JWT
            let user_uuid = Uuid::parse_str(&claims.user_id)
                .map_err(|e| ChatError::validation_error(&format!("Invalid user UUID: {}", e)))?;

            // Supprimer le message via le service
            let deleted_message = state
                .message_edit_service
                .delete_message(user_uuid, message_id)
                .await
                .map_err(|e| {
                    warn!(
                        user_id = %user_uuid,
                        message_id = %message_id,
                        error = %e,
                        "Erreur lors de la suppression du message"
                    );
                    e
                })?;

            // Vérifier que le message appartient à la conversation indiquée
            if deleted_message.conversation_id != conversation_id {
                return Err(ChatError::validation_error(
                    "Le message n'appartient pas à la conversation indiquée",
                ));
            }

            // Créer le message outbound pour notifier les autres participants
            let message_deleted = OutgoingMessage::MessageDeleted {
                message_id,
                conversation_id,
                deleter_id: user_uuid,
                deleted_at: deleted_message
                    .deleted_at
                    .unwrap_or(deleted_message.updated_at),
            };

            // Broadcast aux autres participants de la conversation
            state
                .ws_manager
                .broadcast_to_conversation(conversation_id, message_deleted.clone())
                .await?;

            // Envoyer confirmation au client qui a initié l'action
            let confirmation = OutgoingMessage::ActionConfirmed {
                action: "message_deleted".to_string(),
                success: true,
            };
            client.send_message(confirmation).await?;

            info!(
                "✅ Message {} supprimé par {} dans la conversation {}",
                message_id, user_uuid, conversation_id
            );
        }
        IncomingMessage::FetchHistory {
            conversation_id,
            before,
            after,
            limit,
        } => {
            info!(
                "📜 Client {} ({}) demande l'historique de la conversation {}",
                client.id, claims.username, conversation_id
            );

            // Parser l'user_id depuis les claims JWT
            let user_uuid = Uuid::parse_str(&claims.user_id)
                .map_err(|e| ChatError::validation_error(&format!("Invalid user UUID: {}", e)))?;

            // Vérifier les permissions pour lire l'historique
            state
                .permission_service
                .can_read_conversation(user_uuid, conversation_id)
                .await
                .map_err(|e| {
                    warn!(
                        user_id = %user_uuid,
                        conversation_id = %conversation_id,
                        error = %e,
                        "Permission refusée pour lire l'historique"
                    );
                    e
                })?;

            // Récupérer l'historique
            let limit = limit.unwrap_or(50).min(100);
            let (messages, has_more_before, has_more_after) = state
                .message_repo
                .fetch_history(conversation_id, before, after, limit, false)
                .await
                .map_err(|e| {
                    ChatError::internal_error(format!(
                        "Erreur lors de la récupération de l'historique: {}",
                        e
                    ))
                })?;

            // Envoyer le chunk d'historique
            let message_count = messages.len();
            let history_chunk = OutgoingMessage::HistoryChunk {
                conversation_id,
                messages,
                has_more_before,
                has_more_after,
            };
            client.send_message(history_chunk).await?;

            info!(
                "✅ Historique envoyé pour la conversation {} ({} messages)",
                conversation_id, message_count
            );
        }
        IncomingMessage::SearchMessages {
            conversation_id,
            query,
            limit,
            offset,
        } => {
            info!(
                "🔍 Client {} ({}) recherche dans la conversation {}: '{}'",
                client.id, claims.username, conversation_id, query
            );

            // Parser l'user_id depuis les claims JWT
            let user_uuid = Uuid::parse_str(&claims.user_id)
                .map_err(|e| ChatError::validation_error(&format!("Invalid user UUID: {}", e)))?;

            // Vérifier les permissions pour rechercher
            state
                .permission_service
                .can_read_conversation(user_uuid, conversation_id)
                .await
                .map_err(|e| {
                    warn!(
                        user_id = %user_uuid,
                        conversation_id = %conversation_id,
                        error = %e,
                        "Permission refusée pour rechercher"
                    );
                    e
                })?;

            // Valider la query (ne pas être vide)
            if query.trim().is_empty() {
                return Err(ChatError::validation_error(
                    "La requête de recherche ne peut pas être vide",
                ));
            }

            // Rechercher les messages
            let limit = limit.unwrap_or(50).min(100);
            let offset = offset.unwrap_or(0);
            let (messages, total) = state
                .message_repo
                .search_messages(conversation_id, &query, limit, offset, false)
                .await
                .map_err(|e| {
                    ChatError::internal_error(format!("Erreur lors de la recherche: {}", e))
                })?;

            // Envoyer les résultats
            let search_results = OutgoingMessage::SearchResults {
                conversation_id,
                messages,
                query: query.clone(),
                total,
            };
            client.send_message(search_results).await?;

            info!(
                "✅ Recherche terminée pour '{}' dans {} ({} résultats)",
                query, conversation_id, total
            );
        }
        IncomingMessage::SyncMessages {
            conversation_id,
            since,
        } => {
            info!(
                "🔄 Client {} ({}) synchronise la conversation {} depuis {}",
                client.id, claims.username, conversation_id, since
            );

            // Parser l'user_id depuis les claims JWT
            let user_uuid = Uuid::parse_str(&claims.user_id)
                .map_err(|e| ChatError::validation_error(&format!("Invalid user UUID: {}", e)))?;

            // Vérifier les permissions pour synchroniser
            state
                .permission_service
                .can_read_conversation(user_uuid, conversation_id)
                .await
                .map_err(|e| {
                    warn!(
                        user_id = %user_uuid,
                        conversation_id = %conversation_id,
                        error = %e,
                        "Permission refusée pour synchroniser"
                    );
                    e
                })?;

            // Récupérer les messages depuis since
            let messages = state
                .message_repo
                .fetch_since(conversation_id, since)
                .await
                .map_err(|e| {
                    ChatError::internal_error(format!("Erreur lors de la synchronisation: {}", e))
                })?;

            // Calculer le dernier timestamp de sync (maintenant)
            let last_sync = chrono::Utc::now();

            // Envoyer le chunk de synchronisation
            let message_count = messages.len();
            let sync_chunk = OutgoingMessage::SyncChunk {
                conversation_id,
                messages,
                last_sync,
            };
            client.send_message(sync_chunk).await?;

            info!(
                "✅ Synchronisation terminée pour {} ({} messages)",
                conversation_id, message_count
            );
        }
        IncomingMessage::Ping => {
            debug!("🏓 Ping WebSocket reçu");
            client.send_message(OutgoingMessage::Pong).await?;
        }
    }

    Ok(true) // Continuer la connexion
}
