<?php

declare(strict_types=1);

namespace App\Controllers;

require_once __DIR__ . '/../Services/LogService.php';
require_once __DIR__ . '/../Services/ApiService.php';

use PDO;
use Database;
use Request;
use Response;
use Session;
use App\Services\LogService;
use App\Services\ApiService;

class ChatController {
    private PDO $db;
    
    public function __construct() {
        $this->db = Database::getInstance();
    }
    
    /**
     * GET /api/chat/rooms
     * Liste des conversations filtrées par rôle et entité
     */
    public function getRooms(): void {
        // L'authentification est déjà vérifiée par le Router pour les routes privées
        
        try {
            $userId = Session::getUserId();
            $entityId = Session::getEntityId();
            
            // Vérifier si c'est une synchronisation incrémentale
            $updatedAfter = $_GET['updated_after'] ?? null;
            $isIncrementalSync = !empty($updatedAfter);
            
            
            // Récupérer le rôle de l'utilisateur
            $userRole = $this->getUserRole($userId);
            
            // Timestamp de synchronisation actuel en UTC
            // Utiliser le format ISO 8601 avec timezone UTC
            $syncTimestamp = gmdate('Y-m-d\TH:i:s\Z');
            
            // Convertir updated_after de UTC vers le timezone de la BD si nécessaire
            if ($isIncrementalSync) {
                // Le paramètre arrive en UTC, on doit le convertir pour la comparaison avec la BD
                // Si la BD stocke en heure locale (Europe/Paris = UTC+2 en été)
                $updatedAfterUTC = new \DateTime($updatedAfter, new \DateTimeZone('UTC'));
                $updatedAfterLocal = clone $updatedAfterUTC;
                $updatedAfterLocal->setTimezone(new \DateTimeZone('Europe/Paris'));
                $updatedAfter = $updatedAfterLocal->format('Y-m-d H:i:s');
            }
            
            // Construction de la requête selon le rôle
            $sql = '
                SELECT DISTINCT
                    r.id,
                    r.title,
                    r.type,
                    r.created_at,
                    r.created_by,
                    r.updated_at,
                    -- Dernier message
                    (SELECT m.content 
                     FROM chat_messages m 
                     WHERE m.room_id = r.id 
                       AND m.is_deleted = 0 
                     ORDER BY m.sent_at DESC 
                     LIMIT 1) as last_message,
                    (SELECT m.sent_at 
                     FROM chat_messages m 
                     WHERE m.room_id = r.id 
                       AND m.is_deleted = 0 
                     ORDER BY m.sent_at DESC 
                     LIMIT 1) as last_message_at,
                    -- Nombre de messages non lus
                    (SELECT COUNT(*) 
                     FROM chat_messages m 
                     WHERE m.room_id = r.id 
                       AND m.sender_id != :user_id_count
                       AND NOT EXISTS (
                           SELECT 1 FROM chat_read_receipts rr 
                           WHERE rr.message_id = m.id 
                             AND rr.user_id = :user_id_receipts
                       )) as unread_count,
                    -- Participants
                    (SELECT COUNT(*) 
                     FROM chat_participants cp 
                     WHERE cp.room_id = r.id 
                       AND cp.left_at IS NULL) as participant_count,
                    r.is_active,
                    -- Permission ecriture
                    CASE 
                        WHEN r.type = "broadcast" THEN (r.created_by = :user_id_write)
                        ELSE COALESCE(p.can_write, 1)
                    END as can_write
                FROM chat_rooms r
                INNER JOIN chat_participants p ON r.id = p.room_id
                WHERE p.user_id = :user_id
            ';
            
            // Pour la synchronisation incrémentale
            if ($isIncrementalSync) {
                $sql .= ' AND (
                    -- Rooms avec nouveaux messages SEULEMENT
                    (r.is_active = 1 AND p.left_at IS NULL AND 
                        EXISTS (
                            SELECT 1 FROM chat_messages m 
                            WHERE m.room_id = r.id 
                            AND m.sent_at > :updated_after_msg
                        )
                    )
                    OR
                    -- Nouvelles rooms créées après updated_after
                    (r.is_active = 1 AND p.left_at IS NULL AND r.created_at > :updated_after_created)
                    OR
                    -- Rooms récemment supprimées (seulement si supprimées après updated_after)
                    (r.is_active = 0 AND r.updated_at > :updated_after_deleted)
                )';
            } else {
                // Synchronisation initiale : seulement les rooms actives
                $sql .= ' AND r.is_active = 1 AND p.left_at IS NULL';
            }
            
            // Filtrage supplémentaire selon le rôle
            if ($userRole == 1) {
                // Utilisateur simple : seulement ses conversations privées et de groupe
                $sql .= ' AND r.type IN ("private", "group")';
            } elseif ($userRole == 2) {
                // Admin d'entité : toutes les conversations de son entité
                $sql .= ' AND (p.entite_id = :entity_id OR r.type = "broadcast")';
            }
            // Rôle > 2 : accès à toutes les conversations
            
            $sql .= ' ORDER BY COALESCE(last_message_at, r.created_at) DESC';
            
            $stmt = $this->db->prepare($sql);
            $params = [
                'user_id' => $userId,
                'user_id_count' => $userId,
                'user_id_receipts' => $userId,
                'user_id_write' => $userId
            ];
            
            if ($isIncrementalSync) {
                $params['updated_after_created'] = $updatedAfter;
                $params['updated_after_msg'] = $updatedAfter;
                $params['updated_after_deleted'] = $updatedAfter;
            }
            
            if ($userRole == 2) {
                $params['entity_id'] = $entityId;
            }
            
            $stmt->execute($params);
            $rooms = $stmt->fetchAll(\PDO::FETCH_ASSOC);
            
            // Pour chaque room, récupérer les participants et les messages récents
            foreach ($rooms as &$room) {
                // Ajouter le flag deleted si la room est inactive
                $room['deleted'] = ($room['is_active'] == 0);
                unset($room['is_active']); // Retirer is_active de la réponse
                
                // Convertir can_write en booléen
                $room['can_write'] = (bool)$room['can_write'];
                
                // Pour les rooms supprimées, pas besoin des détails
                if (!$room['deleted']) {
                    $room['participants'] = $this->getRoomParticipants($room['id']);
                    
                    if ($isIncrementalSync) {
                        // Sync incrémentale : seulement les nouveaux messages
                        $newMessages = $this->getNewMessages($room['id'], $userId, $updatedAfter);
                        $room['recent_messages'] = $newMessages;
                        
                        // Si pas de nouveaux messages mais la room est quand même retournée,
                        // c'est qu'elle a été créée récemment - on prend les 5 derniers
                        if (empty($newMessages) && $room['created_at'] > $updatedAfter) {
                            $room['recent_messages'] = $this->getRecentMessages($room['id'], $userId, 5);
                        }
                    } else {
                        // Sync initiale : les 5 derniers messages
                        $room['recent_messages'] = $this->getRecentMessages($room['id'], $userId, 5);
                    }
                } else {
                    $room['participants'] = [];
                    $room['recent_messages'] = [];
                }
            }
            
            Response::json([
                'status' => 'success',
                'sync_timestamp' => $syncTimestamp,
                'has_changes' => !empty($rooms),
                'rooms' => $rooms
            ]);
            
        } catch (\PDOException $e) {
            LogService::log('Erreur lors de la récupération des conversations', [
                'level' => 'error',
                'error' => $e->getMessage()
            ]);
            Response::json([
                'status' => 'error',
                'message' => 'Erreur serveur'
            ], 500);
        }
    }
    
    /**
     * POST /api/chat/rooms
     * Créer une nouvelle conversation
     */
    public function createRoom(): void {
        // L'authentification est déjà vérifiée par le Router pour les routes privées
        
        try {
            $data = Request::getJson();
            $userId = Session::getUserId();
            $entityId = Session::getEntityId();
            $userRole = $this->getUserRole($userId);
            
            // Récupérer le temp_id s'il est fourni (pour la synchronisation offline)
            $tempId = $data['temp_id'] ?? null;
            
            // Validation des données
            if (!isset($data['type']) || !in_array($data['type'], ['private', 'group', 'broadcast'])) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Type de conversation invalide'
                ], 400);
                return;
            }
            
            // Vérification des permissions pour broadcast
            // Seuls les super admins (role = 9) peuvent créer des broadcasts
            if ($data['type'] === 'broadcast' && $userRole != 9) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Seuls les super administrateurs peuvent créer des annonces'
                ], 403);
                return;
            }
            
            // Validation des participants
            if (!isset($data['participants']) || !is_array($data['participants']) || empty($data['participants'])) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Au moins un participant requis'
                ], 400);
                return;
            }
            
            // Pour une conversation privée, limiter à 2 participants (incluant le créateur)
            if ($data['type'] === 'private' && count($data['participants']) > 1) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Une conversation privée ne peut avoir que 2 participants'
                ], 400);
                return;
            }
            
            // Vérifier que tous les participants existent et sont accessibles
            $participantIds = array_map('intval', $data['participants']);
            if (!in_array($userId, $participantIds)) {
                $participantIds[] = $userId; // Ajouter le créateur
            }
            
            // Vérifier l'existence d'une conversation privée existante
            if ($data['type'] === 'private' && count($participantIds) === 2) {
                $existingRoom = $this->findExistingPrivateRoom($participantIds[0], $participantIds[1]);
                if ($existingRoom) {
                    // Ajouter le temp_id même pour une room existante
                    if ($tempId !== null) {
                        $existingRoom['temp_id'] = $tempId;
                    }
                    Response::json([
                        'status' => 'success',
                        'room' => $existingRoom,
                        'existing' => true
                    ]);
                    return;
                }
            }
            
            // Générer un UUID pour la room
            $roomId = $this->generateUUID();
            
            // Titre de la conversation
            $title = $data['title'] ?? null;
            if (!$title && $data['type'] === 'private') {
                // Pour une conversation privée, pas de titre par défaut
                $title = null;
            }
            
            $this->db->beginTransaction();
            
            try {
                // Créer la room
                $stmt = $this->db->prepare('
                    INSERT INTO chat_rooms (id, title, type, created_by, created_at)
                    VALUES (:id, :title, :type, :created_by, NOW())
                ');
                $stmt->execute([
                    'id' => $roomId,
                    'title' => $title,
                    'type' => $data['type'],
                    'created_by' => $userId
                ]);
                
                // Ajouter les participants
                $participantStmt = $this->db->prepare('
                    INSERT INTO chat_participants (room_id, user_id, role, entite_id, is_admin, can_write)
                    VALUES (:room_id, :user_id, :role, :entite_id, :is_admin, :can_write)
                ');
                
                foreach ($participantIds as $participantId) {
                    $participantData = $this->getUserData($participantId);
                    if (!$participantData) {
                        throw new \Exception("Participant invalide: $participantId");
                    }
                    
                    // Pour les broadcasts, seul le créateur peut écrire
                    $canWrite = true;
                    if ($data['type'] === 'broadcast') {
                        $canWrite = ($participantId === $userId);
                    }
                    
                    $participantStmt->execute([
                        'room_id' => $roomId,
                        'user_id' => $participantId,
                        'role' => $participantData['fk_role'],
                        'entite_id' => $participantData['fk_entite'],
                        'is_admin' => ($participantId === $userId) ? 1 : 0,
                        'can_write' => $canWrite ? 1 : 0
                    ]);
                }
                
                // Si un message initial est fourni, l'envoyer
                if (isset($data['initial_message']) && !empty($data['initial_message'])) {
                    $messageId = $this->generateUUID();
                    $msgStmt = $this->db->prepare('
                        INSERT INTO chat_messages (id, room_id, content, sender_id, sent_at)
                        VALUES (:id, :room_id, :content, :sender_id, NOW())
                    ');
                    $msgStmt->execute([
                        'id' => $messageId,
                        'room_id' => $roomId,
                        'content' => $data['initial_message'],
                        'sender_id' => $userId
                    ]);
                }
                
                $this->db->commit();
                
                LogService::log('Conversation créée', [
                    'level' => 'info',
                    'room_id' => $roomId,
                    'type' => $data['type'],
                    'created_by' => $userId,
                    'participant_count' => count($participantIds)
                ]);
                
                // Récupérer la room créée avec ses détails
                $room = $this->getRoomDetails($roomId);
                
                // Ajouter le temp_id à la réponse si fourni
                if ($tempId !== null) {
                    $room['temp_id'] = $tempId;
                }
                
                Response::json([
                    'status' => 'success',
                    'room' => $room
                ], 201);
                
            } catch (\Exception $e) {
                $this->db->rollBack();
                throw $e;
            }
            
        } catch (\PDOException $e) {
            LogService::log('Erreur lors de la création de la conversation', [
                'level' => 'error',
                'error' => $e->getMessage()
            ]);
            Response::json([
                'status' => 'error',
                'message' => 'Erreur serveur'
            ], 500);
        } catch (\Exception $e) {
            LogService::log('Erreur lors de la création de la conversation', [
                'level' => 'error',
                'error' => $e->getMessage()
            ]);
            Response::json([
                'status' => 'error',
                'message' => $e->getMessage()
            ], 400);
        }
    }
    
    /**
     * PUT /api/chat/rooms/{id}
     * Mettre à jour une conversation
     */
    public function updateRoom(string $roomId): void {
        // L'authentification est déjà vérifiée par le Router pour les routes privées
        
        try {
            $data = Request::getJson();
            $userId = Session::getUserId();
            
            // Récupérer le temp_id s'il est fourni (pour la synchronisation offline)
            $tempId = $data['temp_id'] ?? null;
            
            // Vérifier que la room existe et que l'utilisateur est admin ou créateur
            $stmt = $this->db->prepare('
                SELECT r.created_by, r.is_active, p.is_admin
                FROM chat_rooms r
                INNER JOIN chat_participants p ON r.id = p.room_id
                WHERE r.id = ? AND p.user_id = ?
            ');
            $stmt->execute([$roomId, $userId]);
            $room = $stmt->fetch(\PDO::FETCH_ASSOC);
            
            if (!$room) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Conversation non trouvée ou accès non autorisé'
                ], 404);
                return;
            }
            
            // Vérifier les permissions
            if ($room['created_by'] != $userId && !$room['is_admin']) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Seul le créateur ou un admin peut modifier la conversation'
                ], 403);
                return;
            }
            
            // Mettre à jour le titre si fourni
            if (isset($data['title'])) {
                $updateStmt = $this->db->prepare('
                    UPDATE chat_rooms 
                    SET title = :title, updated_at = NOW()
                    WHERE id = :room_id
                ');
                $updateStmt->execute([
                    'title' => $data['title'],
                    'room_id' => $roomId
                ]);
            }
            
            // Récupérer la room mise à jour
            $updatedRoom = $this->getRoomDetails($roomId);
            
            // Ajouter le temp_id à la réponse si fourni
            if ($tempId !== null) {
                $updatedRoom['temp_id'] = $tempId;
            }
            
            LogService::log('Conversation mise à jour', [
                'level' => 'info',
                'room_id' => $roomId,
                'updated_by' => $userId
            ]);
            
            Response::json([
                'status' => 'success',
                'room' => $updatedRoom
            ]);
            
        } catch (\PDOException $e) {
            LogService::log('Erreur lors de la mise à jour de la conversation', [
                'level' => 'error',
                'room_id' => $roomId,
                'error' => $e->getMessage()
            ]);
            Response::json([
                'status' => 'error',
                'message' => 'Erreur serveur'
            ], 500);
        }
    }
    
    /**
     * DELETE /api/chat/rooms/{id}
     * Supprimer une conversation (soft delete)
     * Seul le créateur peut supprimer
     */
    public function deleteRoom(string $roomId): void {
        // L'authentification est déjà vérifiée par le Router pour les routes privées
        
        try {
            $userId = Session::getUserId();
            
            // Vérifier que la room existe et récupérer le créateur
            $stmt = $this->db->prepare('
                SELECT created_by, is_active 
                FROM chat_rooms 
                WHERE id = ?
            ');
            $stmt->execute([$roomId]);
            $room = $stmt->fetch(\PDO::FETCH_ASSOC);
            
            // Vérifier que la room existe
            if (!$room) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Conversation non trouvée'
                ], 404);
                return;
            }
            
            // Vérifier que la room n'est pas déjà supprimée
            if ($room['is_active'] == 0) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Cette conversation est déjà supprimée'
                ], 400);
                return;
            }
            
            // Vérifier que l'utilisateur est le créateur
            if ($room['created_by'] != $userId) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Seul le créateur de la conversation peut la supprimer'
                ], 403);
                return;
            }
            
            $this->db->beginTransaction();
            
            try {
                // Soft delete : marquer comme inactive
                $updateStmt = $this->db->prepare('
                    UPDATE chat_rooms 
                    SET is_active = 0,
                        updated_at = NOW()
                    WHERE id = ?
                ');
                $updateStmt->execute([$roomId]);
                
                // Marquer tous les participants comme ayant quitté
                $participantsStmt = $this->db->prepare('
                    UPDATE chat_participants 
                    SET left_at = NOW()
                    WHERE room_id = ?
                      AND left_at IS NULL
                ');
                $participantsStmt->execute([$roomId]);
                
                $this->db->commit();
                
                LogService::log('Conversation supprimée', [
                    'level' => 'info',
                    'room_id' => $roomId,
                    'deleted_by' => $userId
                ]);
                
                Response::json([
                    'status' => 'success',
                    'message' => 'Conversation supprimée avec succès'
                ]);
                
            } catch (\Exception $e) {
                if ($this->db->inTransaction()) {
                    $this->db->rollBack();
                }
                throw $e;
            }
            
        } catch (\PDOException $e) {
            LogService::log('Erreur lors de la suppression de la conversation', [
                'level' => 'error',
                'room_id' => $roomId,
                'error' => $e->getMessage()
            ]);
            Response::json([
                'status' => 'error',
                'message' => 'Erreur serveur'
            ], 500);
        }
    }
    
    /**
     * GET /api/chat/rooms/{id}/messages
     * Récupérer les messages d'une conversation
     */
    public function getRoomMessages(string $roomId): void {
        // L'authentification est déjà vérifiée par le Router pour les routes privées
        
        try {
            $userId = Session::getUserId();
            
            // Vérifier que l'utilisateur est participant
            if (!$this->isUserInRoom($userId, $roomId)) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Accès non autorisé à cette conversation'
                ], 403);
                return;
            }
            
            // Paramètres de pagination
            $limit = isset($_GET['limit']) ? min(100, max(1, (int)$_GET['limit'])) : 50;
            $before = $_GET['before'] ?? null; // Message ID pour pagination
            
            $sql = '
                SELECT 
                    m.id,
                    m.content,
                    m.sender_id,
                    m.sent_at,
                    m.edited_at,
                    m.is_deleted,
                    u.encrypted_name as sender_name,
                    u.first_name as sender_first_name,
                    -- Statut de lecture
                    (SELECT COUNT(*) 
                     FROM chat_read_receipts r 
                     WHERE r.message_id = m.id) as read_count,
                    (SELECT COUNT(*) 
                     FROM chat_read_receipts r 
                     WHERE r.message_id = m.id 
                       AND r.user_id = :user_id) as is_read
                FROM chat_messages m
                INNER JOIN users u ON m.sender_id = u.id
                WHERE m.room_id = :room_id
            ';
            
            $params = [
                'room_id' => $roomId,
                'user_id' => $userId
            ];
            
            if ($before) {
                $sql .= ' AND m.sent_at < (SELECT sent_at FROM chat_messages WHERE id = :before)';
                $params['before'] = $before;
            }
            
            $sql .= ' ORDER BY m.sent_at DESC LIMIT :limit';
            
            $stmt = $this->db->prepare($sql);
            foreach ($params as $key => $value) {
                if ($key === 'limit') {
                    $stmt->bindValue($key, $value, \PDO::PARAM_INT);
                } else {
                    $stmt->bindValue($key, $value);
                }
            }
            $stmt->bindValue('limit', $limit, \PDO::PARAM_INT);
            $stmt->execute();
            
            $messages = $stmt->fetchAll(\PDO::FETCH_ASSOC);
            
            // Déchiffrer les noms
            foreach ($messages as &$message) {
                $message['sender_name'] = ApiService::decryptData($message['sender_name']);
                $message['is_read'] = (bool)$message['is_read'];
                $message['is_mine'] = ($message['sender_id'] == $userId);
            }
            
            // Inverser pour avoir l'ordre chronologique
            $messages = array_reverse($messages);
            
            // Marquer automatiquement tous les messages comme lus
            $markedCount = $this->markAllMessagesAsRead($roomId, $userId);
            
            // Mettre à jour last_read_at pour ce participant
            $this->updateLastRead($roomId, $userId);
            
            // Compter les messages non lus restants (devrait être 0)
            $unreadCount = $this->getUnreadCount($roomId, $userId);
            
            Response::json([
                'status' => 'success',
                'messages' => $messages,
                'has_more' => count($messages) === $limit,
                'marked_as_read' => $markedCount,
                'unread_count' => $unreadCount
            ]);
            
        } catch (\PDOException $e) {
            LogService::log('Erreur lors de la récupération des messages', [
                'level' => 'error',
                'room_id' => $roomId,
                'error' => $e->getMessage()
            ]);
            Response::json([
                'status' => 'error',
                'message' => 'Erreur serveur'
            ], 500);
        }
    }
    
    /**
     * POST /api/chat/rooms/{id}/messages
     * Envoyer un message dans une conversation
     */
    public function sendMessage(string $roomId): void {
        // L'authentification est déjà vérifiée par le Router pour les routes privées
        
        try {
            $data = Request::getJson();
            $userId = Session::getUserId();
            
            // Récupérer le temp_id s'il est fourni (pour la synchronisation offline)
            $tempId = $data['temp_id'] ?? null;
            
            // Vérifier que l'utilisateur est participant
            if (!$this->isUserInRoom($userId, $roomId)) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Accès non autorisé à cette conversation'
                ], 403);
                return;
            }
            
            // Vérifier les permissions d'écriture pour les broadcasts
            $roomInfo = $this->getRoomInfo($roomId);
            if ($roomInfo && $roomInfo['type'] === 'broadcast') {
                // Pour les broadcasts, seul le créateur peut écrire
                if ($roomInfo['created_by'] != $userId) {
                    Response::json([
                        'status' => 'error',
                        'message' => 'Seul l\'administrateur peut poster dans une annonce'
                    ], 403);
                    return;
                }
            } else {
                // Pour les autres types, vérifier can_write
                if (!$this->canUserWrite($userId, $roomId)) {
                    Response::json([
                        'status' => 'error',
                        'message' => 'Vous n\'avez pas la permission d\'écrire dans cette conversation'
                    ], 403);
                    return;
                }
            }
            
            // Validation du contenu
            if (!isset($data['content']) || empty(trim($data['content']))) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Le message ne peut pas être vide'
                ], 400);
                return;
            }
            
            $content = trim($data['content']);
            
            // Limiter la longueur du message
            if (mb_strlen($content, 'UTF-8') > 5000) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Message trop long (max 5000 caractères)'
                ], 400);
                return;
            }
            
            $messageId = $this->generateUUID();
            
            // Insérer le message
            $stmt = $this->db->prepare('
                INSERT INTO chat_messages (id, room_id, content, sender_id, sent_at)
                VALUES (:id, :room_id, :content, :sender_id, NOW())
            ');
            $stmt->execute([
                'id' => $messageId,
                'room_id' => $roomId,
                'content' => $content,
                'sender_id' => $userId
            ]);
            
            // Mettre à jour la date de dernière modification de la room
            $updateStmt = $this->db->prepare('
                UPDATE chat_rooms 
                SET updated_at = NOW() 
                WHERE id = :room_id
            ');
            $updateStmt->execute(['room_id' => $roomId]);
            
            // Récupérer le message créé avec les infos du sender
            $msgStmt = $this->db->prepare('
                SELECT 
                    m.id,
                    m.content,
                    m.sender_id,
                    m.sent_at,
                    u.encrypted_name as sender_name,
                    u.first_name as sender_first_name
                FROM chat_messages m
                INNER JOIN users u ON m.sender_id = u.id
                WHERE m.id = :id
            ');
            $msgStmt->execute(['id' => $messageId]);
            $message = $msgStmt->fetch(\PDO::FETCH_ASSOC);
            
            $message['sender_name'] = ApiService::decryptData($message['sender_name']);
            $message['is_mine'] = true;
            $message['is_read'] = false;
            $message['read_count'] = 0;
            
            // Ajouter le temp_id à la réponse si fourni
            if ($tempId !== null) {
                $message['temp_id'] = $tempId;
            }
            
            LogService::log('Message envoyé', [
                'level' => 'debug',
                'room_id' => $roomId,
                'message_id' => $messageId,
                'sender_id' => $userId
            ]);
            
            Response::json([
                'status' => 'success',
                'message' => $message
            ], 201);
            
        } catch (\PDOException $e) {
            LogService::log('Erreur lors de l\'envoi du message', [
                'level' => 'error',
                'room_id' => $roomId,
                'error' => $e->getMessage()
            ]);
            Response::json([
                'status' => 'error',
                'message' => 'Erreur serveur'
            ], 500);
        }
    }
    
    /**
     * PUT /api/chat/messages/{id}
     * Mettre à jour un message (édition)
     */
    public function updateMessage(string $messageId): void {
        // L'authentification est déjà vérifiée par le Router pour les routes privées
        
        try {
            $data = Request::getJson();
            $userId = Session::getUserId();
            
            // Récupérer le temp_id s'il est fourni (pour la synchronisation offline)
            $tempId = $data['temp_id'] ?? null;
            
            // Vérifier que le message existe et appartient à l'utilisateur
            $stmt = $this->db->prepare('
                SELECT m.id, m.sender_id, m.room_id, m.content, m.is_deleted
                FROM chat_messages m
                WHERE m.id = ?
            ');
            $stmt->execute([$messageId]);
            $message = $stmt->fetch(\PDO::FETCH_ASSOC);
            
            if (!$message) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Message non trouvé'
                ], 404);
                return;
            }
            
            // Vérifier que l'utilisateur est le sender du message
            if ($message['sender_id'] != $userId) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Vous ne pouvez modifier que vos propres messages'
                ], 403);
                return;
            }
            
            // Vérifier que le message n'est pas supprimé
            if ($message['is_deleted']) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Ce message a été supprimé'
                ], 400);
                return;
            }
            
            // Validation du contenu
            if (!isset($data['content']) || empty(trim($data['content']))) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Le message ne peut pas être vide'
                ], 400);
                return;
            }
            
            $content = trim($data['content']);
            
            // Limiter la longueur du message
            if (mb_strlen($content, 'UTF-8') > 5000) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Message trop long (max 5000 caractères)'
                ], 400);
                return;
            }
            
            // Mettre à jour le message
            $updateStmt = $this->db->prepare('
                UPDATE chat_messages 
                SET content = :content, edited_at = NOW()
                WHERE id = :message_id
            ');
            $updateStmt->execute([
                'content' => $content,
                'message_id' => $messageId
            ]);
            
            // Récupérer le message mis à jour
            $msgStmt = $this->db->prepare('
                SELECT 
                    m.id,
                    m.content,
                    m.sender_id,
                    m.sent_at,
                    m.edited_at,
                    u.encrypted_name as sender_name,
                    u.first_name as sender_first_name
                FROM chat_messages m
                INNER JOIN users u ON m.sender_id = u.id
                WHERE m.id = :id
            ');
            $msgStmt->execute(['id' => $messageId]);
            $updatedMessage = $msgStmt->fetch(\PDO::FETCH_ASSOC);
            
            $updatedMessage['sender_name'] = ApiService::decryptData($updatedMessage['sender_name']);
            $updatedMessage['is_mine'] = true;
            
            // Ajouter le temp_id à la réponse si fourni
            if ($tempId !== null) {
                $updatedMessage['temp_id'] = $tempId;
            }
            
            LogService::log('Message modifié', [
                'level' => 'debug',
                'message_id' => $messageId,
                'sender_id' => $userId
            ]);
            
            Response::json([
                'status' => 'success',
                'message' => $updatedMessage
            ]);
            
        } catch (\PDOException $e) {
            LogService::log('Erreur lors de la modification du message', [
                'level' => 'error',
                'message_id' => $messageId,
                'error' => $e->getMessage()
            ]);
            Response::json([
                'status' => 'error',
                'message' => 'Erreur serveur'
            ], 500);
        }
    }
    
    /**
     * POST /api/chat/rooms/{id}/read
     * Marquer les messages comme lus
     */
    public function markAsRead(string $roomId): void {
        // L'authentification est déjà vérifiée par le Router pour les routes privées
        
        try {
            $data = Request::getJson();
            $userId = Session::getUserId();
            
            // Vérifier que l'utilisateur est participant
            if (!$this->isUserInRoom($userId, $roomId)) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Accès non autorisé à cette conversation'
                ], 403);
                return;
            }
            
            // Si des message_ids spécifiques sont fournis
            if (isset($data['message_ids']) && is_array($data['message_ids'])) {
                $messageIds = $data['message_ids'];
                
                // Marquer ces messages spécifiques comme lus
                $stmt = $this->db->prepare('
                    INSERT IGNORE INTO chat_read_receipts (message_id, user_id, read_at)
                    VALUES (:message_id, :user_id, NOW())
                ');
                
                foreach ($messageIds as $messageId) {
                    $stmt->execute([
                        'message_id' => $messageId,
                        'user_id' => $userId
                    ]);
                }
            } else {
                // Marquer tous les messages non lus de la room comme lus
                $stmt = $this->db->prepare('
                    INSERT IGNORE INTO chat_read_receipts (message_id, user_id, read_at)
                    SELECT m.id, :user_id, NOW()
                    FROM chat_messages m
                    WHERE m.room_id = :room_id
                      AND m.id NOT IN (
                          SELECT message_id 
                          FROM chat_read_receipts 
                          WHERE user_id = :user_id_check
                      )
                ');
                $stmt->execute([
                    'user_id' => $userId,
                    'user_id_check' => $userId,
                    'room_id' => $roomId
                ]);
            }
            
            // Mettre à jour last_read_at
            $this->updateLastRead($roomId, $userId);
            
            // Compter les messages non lus restants
            $countStmt = $this->db->prepare('
                SELECT COUNT(*) as unread_count
                FROM chat_messages m
                WHERE m.room_id = :room_id
                  AND m.sender_id != :user_id
                  AND m.id NOT IN (
                      SELECT message_id 
                      FROM chat_read_receipts 
                      WHERE user_id = :user_id_check
                  )
            ');
            $countStmt->execute([
                'room_id' => $roomId,
                'user_id' => $userId,
                'user_id_check' => $userId
            ]);
            $result = $countStmt->fetch(\PDO::FETCH_ASSOC);
            
            Response::json([
                'status' => 'success',
                'unread_count' => (int)$result['unread_count']
            ]);
            
        } catch (\PDOException $e) {
            LogService::log('Erreur lors du marquage comme lu', [
                'level' => 'error',
                'room_id' => $roomId,
                'error' => $e->getMessage()
            ]);
            Response::json([
                'status' => 'error',
                'message' => 'Erreur serveur'
            ], 500);
        }
    }
    
    /**
     * GET /api/chat/recipients
     * Liste des destinataires possibles selon le rôle
     */
    public function getRecipients(): void {
        // L'authentification est déjà vérifiée par le Router pour les routes privées
        
        try {
            $userId = Session::getUserId();
            $entityId = Session::getEntityId();
            $userRole = $this->getUserRole($userId);
            
            $sql = '
                SELECT 
                    u.id,
                    u.encrypted_name as name,
                    u.first_name,
                    u.sect_name,
                    u.fk_role as role,
                    u.fk_entite as entite_id,
                    e.encrypted_name as entite_name
                FROM users u
                LEFT JOIN entites e ON u.fk_entite = e.id
                WHERE u.chk_active = 1
                  AND u.id != :user_id
            ';
            
            $params = ['user_id' => $userId];
            
            // Filtrage selon le rôle
            if ($userRole == 1) {
                // Utilisateur simple : seulement les utilisateurs de son entité
                $sql .= ' AND u.fk_entite = :entity_id';
                $params['entity_id'] = $entityId;
            } elseif ($userRole == 2) {
                // Admin d'entité : 
                // - Tous les membres actifs de son amicale (même entité)
                // - Les super-admins (fk_role=9) de l'entité 1
                $sql .= ' AND (
                    u.fk_entite = :entity_id 
                    OR (u.fk_role = 9 AND u.fk_entite = 1)
                )';
                $params['entity_id'] = $entityId;
            } elseif ($userRole == 9) {
                // Super-administrateur : 
                // - SEULEMENT les administrateurs actifs des amicales (fk_role=2)
                // - PAS les autres super-admins pour éviter les broadcasts entre super-admins
                $sql .= ' AND u.fk_role = 2';
            }
            // Autres rôles (3-8) : pas de filtrage spécifique pour le moment
            
            $sql .= ' ORDER BY u.fk_entite, u.encrypted_name';
            
            $stmt = $this->db->prepare($sql);
            $stmt->execute($params);
            $recipients = $stmt->fetchAll(\PDO::FETCH_ASSOC);
            
            // Déchiffrer les données et organiser par entité
            $recipientsByEntity = [];
            $recipientsDecrypted = [];
            
            foreach ($recipients as &$recipient) {
                // Déchiffrer le nom
                $recipient['name'] = ApiService::decryptData($recipient['name']);
                
                // Déchiffrer le nom de l'entité
                $entiteName = $recipient['entite_name'] ? 
                    ApiService::decryptData($recipient['entite_name']) : 
                    'Sans entité';
                
                // Créer une copie pour recipients_by_entity
                $recipientCopy = $recipient;
                unset($recipientCopy['entite_name']);
                
                // Organiser par entité
                if (!isset($recipientsByEntity[$entiteName])) {
                    $recipientsByEntity[$entiteName] = [];
                }
                $recipientsByEntity[$entiteName][] = $recipientCopy;
                
                // Remplacer entite_name chiffré par la version déchiffrée
                $recipient['entite_name'] = $entiteName;
                
                // Ajouter à la liste déchiffrée
                $recipientsDecrypted[] = $recipient;
            }
            
            Response::json([
                'status' => 'success',
                'recipients' => $recipientsDecrypted,
                'recipients_by_entity' => $recipientsByEntity
            ]);
            
        } catch (\PDOException $e) {
            LogService::log('Erreur lors de la récupération des destinataires', [
                'level' => 'error',
                'error' => $e->getMessage()
            ]);
            Response::json([
                'status' => 'error',
                'message' => 'Erreur serveur'
            ], 500);
        }
    }
    
    // ===== Méthodes utilitaires privées =====
    
    /**
     * Récupérer le rôle d'un utilisateur
     */
    private function getUserRole(int $userId): int {
        $stmt = $this->db->prepare('SELECT fk_role FROM users WHERE id = ?');
        $stmt->execute([$userId]);
        $result = $stmt->fetch(\PDO::FETCH_ASSOC);
        return $result ? (int)$result['fk_role'] : 1;
    }
    
    /**
     * Récupérer les données d'un utilisateur
     */
    private function getUserData(int $userId): ?array {
        $stmt = $this->db->prepare('
            SELECT id, fk_role, fk_entite, encrypted_name, first_name 
            FROM users 
            WHERE id = ? AND chk_active = 1
        ');
        $stmt->execute([$userId]);
        $result = $stmt->fetch(\PDO::FETCH_ASSOC);
        return $result ?: null;
    }
    
    /**
     * Vérifier si un utilisateur est dans une room
     */
    private function isUserInRoom(int $userId, string $roomId): bool {
        $stmt = $this->db->prepare('
            SELECT COUNT(*) as count 
            FROM chat_participants 
            WHERE room_id = ? 
              AND user_id = ? 
              AND left_at IS NULL
        ');
        $stmt->execute([$roomId, $userId]);
        $result = $stmt->fetch(\PDO::FETCH_ASSOC);
        return $result && $result['count'] > 0;
    }
    
    /**
     * Récupérer les participants d'une room
     */
    private function getRoomParticipants(string $roomId): array {
        $stmt = $this->db->prepare('
            SELECT 
                p.user_id,
                p.is_admin,
                u.encrypted_name as name,
                u.first_name
            FROM chat_participants p
            INNER JOIN users u ON p.user_id = u.id
            WHERE p.room_id = ?
              AND p.left_at IS NULL
        ');
        $stmt->execute([$roomId]);
        $participants = $stmt->fetchAll(\PDO::FETCH_ASSOC);
        
        foreach ($participants as &$participant) {
            $participant['name'] = ApiService::decryptData($participant['name']);
        }
        
        return $participants;
    }
    
    /**
     * Récupérer les détails d'une room
     */
    private function getRoomDetails(string $roomId): array {
        $stmt = $this->db->prepare('
            SELECT 
                r.id,
                r.title,
                r.type,
                r.created_at,
                r.created_by,
                r.updated_at
            FROM chat_rooms r
            WHERE r.id = ?
        ');
        $stmt->execute([$roomId]);
        $room = $stmt->fetch(\PDO::FETCH_ASSOC);
        
        if ($room) {
            $room['participants'] = $this->getRoomParticipants($roomId);
        }
        
        return $room;
    }
    
    /**
     * Trouver une conversation privée existante entre deux utilisateurs
     */
    private function findExistingPrivateRoom(int $user1, int $user2): ?array {
        $stmt = $this->db->prepare('
            SELECT r.* 
            FROM chat_rooms r
            WHERE r.type = "private"
              AND r.is_active = 1
              AND EXISTS (
                  SELECT 1 FROM chat_participants p1 
                  WHERE p1.room_id = r.id 
                    AND p1.user_id = ? 
                    AND p1.left_at IS NULL
              )
              AND EXISTS (
                  SELECT 1 FROM chat_participants p2 
                  WHERE p2.room_id = r.id 
                    AND p2.user_id = ? 
                    AND p2.left_at IS NULL
              )
              AND (
                  SELECT COUNT(*) 
                  FROM chat_participants p 
                  WHERE p.room_id = r.id 
                    AND p.left_at IS NULL
              ) = 2
        ');
        $stmt->execute([$user1, $user2]);
        $room = $stmt->fetch(\PDO::FETCH_ASSOC);
        
        if ($room) {
            $room['participants'] = $this->getRoomParticipants($room['id']);
            return $room;
        }
        
        return null;
    }
    
    /**
     * Mettre à jour la date de dernière lecture
     */
    private function updateLastRead(string $roomId, int $userId): void {
        $stmt = $this->db->prepare('
            UPDATE chat_participants 
            SET last_read_at = NOW() 
            WHERE room_id = ? 
              AND user_id = ?
        ');
        $stmt->execute([$roomId, $userId]);
    }
    
    /**
     * Récupérer les nouveaux messages d'une room depuis une date donnée
     */
    private function getNewMessages(string $roomId, int $userId, string $since): array {
        $stmt = $this->db->prepare('
            SELECT 
                m.id,
                m.content,
                m.sender_id,
                m.sent_at,
                m.edited_at,
                m.is_deleted,
                u.encrypted_name as sender_name,
                u.first_name as sender_first_name,
                CASE 
                    WHEN m.sender_id = :sender_id THEN 1
                    ELSE (SELECT COUNT(*) 
                          FROM chat_read_receipts r 
                          WHERE r.message_id = m.id 
                            AND r.user_id = :user_id) > 0
                END as is_read,
                (m.sender_id = :sender_check) as is_mine
            FROM chat_messages m
            INNER JOIN users u ON m.sender_id = u.id
            WHERE m.room_id = :room_id
              AND m.is_deleted = 0
              AND m.sent_at > :since
            ORDER BY m.sent_at ASC
        ');
        
        $stmt->bindValue('room_id', $roomId);
        $stmt->bindValue('user_id', $userId, \PDO::PARAM_INT);
        $stmt->bindValue('sender_id', $userId, \PDO::PARAM_INT);
        $stmt->bindValue('sender_check', $userId, \PDO::PARAM_INT);
        $stmt->bindValue('since', $since);
        $stmt->execute();
        
        $messages = $stmt->fetchAll(\PDO::FETCH_ASSOC);
        
        // Déchiffrer les noms et convertir les booléens
        foreach ($messages as &$message) {
            $message['sender_name'] = ApiService::decryptData($message['sender_name']);
            $message['is_read'] = (bool)$message['is_read'];
            $message['is_mine'] = (bool)$message['is_mine'];
        }
        
        return $messages;
    }
    
    /**
     * Récupérer les messages récents d'une room
     */
    private function getRecentMessages(string $roomId, int $userId, int $limit = 5): array {
        $stmt = $this->db->prepare('
            SELECT 
                m.id,
                m.content,
                m.sender_id,
                m.sent_at,
                m.edited_at,
                m.is_deleted,
                u.encrypted_name as sender_name,
                u.first_name as sender_first_name,
                CASE 
                    WHEN m.sender_id = :sender_id THEN 1
                    ELSE (SELECT COUNT(*) 
                          FROM chat_read_receipts r 
                          WHERE r.message_id = m.id 
                            AND r.user_id = :user_id) > 0
                END as is_read,
                (m.sender_id = :sender_check) as is_mine
            FROM chat_messages m
            INNER JOIN users u ON m.sender_id = u.id
            WHERE m.room_id = :room_id
              AND m.is_deleted = 0
            ORDER BY m.sent_at DESC
            LIMIT :limit
        ');
        
        $stmt->bindValue('room_id', $roomId);
        $stmt->bindValue('user_id', $userId, \PDO::PARAM_INT);
        $stmt->bindValue('sender_id', $userId, \PDO::PARAM_INT);
        $stmt->bindValue('sender_check', $userId, \PDO::PARAM_INT);
        $stmt->bindValue('limit', $limit, \PDO::PARAM_INT);
        $stmt->execute();
        
        $messages = $stmt->fetchAll(\PDO::FETCH_ASSOC);
        
        // Déchiffrer les noms et convertir les booléens
        foreach ($messages as &$message) {
            $message['sender_name'] = ApiService::decryptData($message['sender_name']);
            $message['is_read'] = (bool)$message['is_read'];
            $message['is_mine'] = (bool)$message['is_mine'];
        }
        
        // Inverser pour avoir l'ordre chronologique
        return array_reverse($messages);
    }
    
    /**
     * Marquer tous les messages d'une room comme lus
     */
    private function markAllMessagesAsRead(string $roomId, int $userId): int {
        // Compter d'abord les messages non lus
        $countStmt = $this->db->prepare('
            SELECT COUNT(*) as count
            FROM chat_messages m
            WHERE m.room_id = :room_id
              AND m.sender_id != :user_id
              AND NOT EXISTS (
                  SELECT 1 FROM chat_read_receipts r 
                  WHERE r.message_id = m.id 
                    AND r.user_id = :user_id_check
              )
        ');
        $countStmt->execute([
            'room_id' => $roomId,
            'user_id' => $userId,
            'user_id_check' => $userId
        ]);
        $result = $countStmt->fetch(\PDO::FETCH_ASSOC);
        $unreadCount = (int)$result['count'];
        
        // Marquer tous les messages non lus comme lus
        if ($unreadCount > 0) {
            $stmt = $this->db->prepare('
                INSERT IGNORE INTO chat_read_receipts (message_id, user_id, read_at)
                SELECT m.id, :user_id, NOW()
                FROM chat_messages m
                WHERE m.room_id = :room_id
                  AND NOT EXISTS (
                      SELECT 1 FROM chat_read_receipts r 
                      WHERE r.message_id = m.id 
                        AND r.user_id = :user_id_check
                  )
            ');
            $stmt->execute([
                'user_id' => $userId,
                'room_id' => $roomId,
                'user_id_check' => $userId
            ]);
        }
        
        return $unreadCount;
    }
    
    /**
     * Compter les messages non lus d'une room
     */
    private function getUnreadCount(string $roomId, int $userId): int {
        $stmt = $this->db->prepare('
            SELECT COUNT(*) as count
            FROM chat_messages m
            WHERE m.room_id = :room_id
              AND m.sender_id != :user_id
              AND NOT EXISTS (
                  SELECT 1 FROM chat_read_receipts r 
                  WHERE r.message_id = m.id 
                    AND r.user_id = :user_id_check
              )
        ');
        $stmt->execute([
            'room_id' => $roomId,
            'user_id' => $userId,
            'user_id_check' => $userId
        ]);
        $result = $stmt->fetch(\PDO::FETCH_ASSOC);
        return (int)$result['count'];
    }
    
    /**
     * Générer un UUID v4
     */
    private function generateUUID(): string {
        return sprintf(
            '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
            mt_rand(0, 0xffff), mt_rand(0, 0xffff),
            mt_rand(0, 0xffff),
            mt_rand(0, 0x0fff) | 0x4000,
            mt_rand(0, 0x3fff) | 0x8000,
            mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
        );
    }
    
    /**
     * Récupérer les infos d'une room
     */
    private function getRoomInfo(string $roomId): ?array {
        $stmt = $this->db->prepare('
            SELECT id, type, created_by, title 
            FROM chat_rooms 
            WHERE id = ?
        ');
        $stmt->execute([$roomId]);
        $result = $stmt->fetch(\PDO::FETCH_ASSOC);
        return $result ?: null;
    }
    
    /**
     * Vérifier si un utilisateur peut écrire dans une room
     */
    private function canUserWrite(int $userId, string $roomId): bool {
        $stmt = $this->db->prepare('
            SELECT p.can_write, r.type, r.created_by
            FROM chat_participants p
            INNER JOIN chat_rooms r ON p.room_id = r.id
            WHERE p.room_id = ? 
              AND p.user_id = ?
              AND p.left_at IS NULL
        ');
        $stmt->execute([$roomId, $userId]);
        $result = $stmt->fetch(\PDO::FETCH_ASSOC);
        
        if (!$result) {
            return false;
        }
        
        // Pour les broadcasts, seul le créateur peut écrire
        if ($result['type'] === 'broadcast') {
            return $result['created_by'] == $userId;
        }
        
        // Pour les autres types, vérifier can_write
        return (bool)$result['can_write'];
    }
}