import 'package:hive/hive.dart';
import 'package:uuid/uuid.dart';
import '../../core/constants/app_keys.dart';
import '../models/conversation_model.dart';
import '../models/message_model.dart';
import '../models/participant_model.dart';
import '../services/chat_api_service.dart';
import '../services/notifications/mqtt_notification_service.dart';

/// Repository pour la gestion des fonctionnalités de chat
///
/// Ce repository centralise toutes les opérations liées au chat,
/// y compris la gestion des conversations, des messages et des participants

class ChatRepository {
  final ChatApiService _apiService;
  final MqttNotificationService _mqttService;
  
  ChatRepository(this._apiService, this._mqttService);

  /// Liste des conversations de l'utilisateur
  Future<List<ConversationModel>> getConversations({bool forceRefresh = false}) async {
    try {
      // Récupérer depuis Hive
      var box = await Hive.openBox<ConversationModel>(AppKeys.chatConversationsBoxName);
      var localConversations = box.values.toList();
      
      // Si on force le rafraîchissement ou qu'on n'a pas de données locales
      if (forceRefresh || localConversations.isEmpty) {
        try {
          // Récupérer depuis l'API
          var apiConversations = await _apiService.getConversations();
          
          // Mettre à jour Hive
          await box.clear();
          for (var conversation in apiConversations) {
            await box.put(conversation.id, conversation);
          }
          
          return apiConversations;
        } catch (e) {
          // Si l'API échoue, utiliser les données locales
          if (localConversations.isNotEmpty) {
            return localConversations;
          }
          rethrow;
        }
      }
      
      return localConversations;
    } catch (e) {
      throw Exception('Erreur lors de la récupération des conversations: $e');
    }
  }

  /// Récupère une conversation spécifique
  Future<ConversationModel> getConversation(String id) async {
    try {
      // Vérifier d'abord dans Hive
      var box = await Hive.openBox<ConversationModel>(AppKeys.chatConversationsBoxName);
      var localConversation = box.get(id);
      
      if (localConversation != null) {
        return localConversation;
      }
      
      // Sinon récupérer depuis l'API
      var apiConversation = await _apiService.getConversation(id);
      await box.put(id, apiConversation);
      
      return apiConversation;
    } catch (e) {
      throw Exception('Erreur lors de la récupération de la conversation: $e');
    }
  }

  /// Crée une nouvelle conversation
  Future<ConversationModel> createConversation(Map<String, dynamic> data) async {
    try {
      // Créer via l'API
      var conversation = await _apiService.createConversation(data);
      
      // Sauvegarder dans Hive
      var box = await Hive.openBox<ConversationModel>(AppKeys.chatConversationsBoxName);
      await box.put(conversation.id, conversation);
      
      // S'abonner aux notifications de la conversation
      await _mqttService.subscribeToConversation(conversation.id);
      
      return conversation;
    } catch (e) {
      throw Exception('Erreur lors de la création de la conversation: $e');
    }
  }

  /// Supprime une conversation
  Future<void> deleteConversation(String id) async {
    try {
      // Supprimer via l'API
      await _apiService.deleteConversation(id);
      
      // Supprimer de Hive
      var box = await Hive.openBox<ConversationModel>(AppKeys.chatConversationsBoxName);
      await box.delete(id);
      
      // Se désabonner des notifications
      await _mqttService.unsubscribeFromConversation(id);
    } catch (e) {
      throw Exception('Erreur lors de la suppression de la conversation: $e');
    }
  }

  /// Épingle/désépingle une conversation
  Future<void> pinConversation(String id, bool isPinned) async {
    try {
      await _apiService.pinConversation(id, isPinned);
      
      // Mettre à jour dans Hive
      var box = await Hive.openBox<ConversationModel>(AppKeys.chatConversationsBoxName);
      var conversation = box.get(id);
      if (conversation != null) {
        await box.put(id, conversation.copyWith(isPinned: isPinned));
      }
    } catch (e) {
      throw Exception('Erreur lors de l\'épinglage de la conversation: $e');
    }
  }

  /// Met à jour les permissions de réponse
  Future<void> updateReplyPermission(String id, String replyPermission) async {
    try {
      await _apiService.updateReplyPermission(id, replyPermission);
      
      // Mettre à jour dans Hive
      var box = await Hive.openBox<ConversationModel>(AppKeys.chatConversationsBoxName);
      var conversation = box.get(id);
      if (conversation != null) {
        await box.put(id, conversation.copyWith(replyPermission: replyPermission));
      }
    } catch (e) {
      throw Exception('Erreur lors de la mise à jour des permissions: $e');
    }
  }

  /// Récupère les messages d'une conversation
  Future<List<MessageModel>> getMessages(String conversationId, {int page = 1, int limit = 50}) async {
    try {
      // Récupérer depuis Hive
      var box = await Hive.openBox<MessageModel>(AppKeys.chatMessagesBoxName);
      var localMessages = box.values
          .where((m) => m.conversationId == conversationId)
          .toList()
        ..sort((a, b) => b.createdAt.compareTo(a.createdAt));
      
      // Si on a assez de messages localement
      if (localMessages.length >= page * limit) {
        return localMessages.skip((page - 1) * limit).take(limit).toList();
      }
      
      try {
        // Récupérer depuis l'API
        var apiMessages = await _apiService.getMessages(conversationId, page: page, limit: limit);
        
        // Mettre à jour Hive
        for (var message in apiMessages) {
          await box.put(message.id, message);
        }
        
        return apiMessages;
      } catch (e) {
        // Si l'API échoue, utiliser les données locales
        if (localMessages.isNotEmpty) {
          return localMessages.skip((page - 1) * limit).take(limit).toList();
        }
        rethrow;
      }
    } catch (e) {
      throw Exception('Erreur lors de la récupération des messages: $e');
    }
  }

  /// Envoie un message via MQTT
  Future<void> sendMessage(String conversationId, Map<String, dynamic> messageData) async {
    try {
      // Générer un ID unique pour le message
      var messageId = const Uuid().v4();
      var userId = messageData['senderId'] as String?;
      
      // Créer le message
      var message = MessageModel(
        id: messageId,
        conversationId: conversationId,
        senderId: userId,
        senderType: 'user',
        content: messageData['content'] as String,
        contentType: messageData['contentType'] as String? ?? 'text',
        createdAt: DateTime.now(),
        status: 'sent',
        isAnnouncement: messageData['isAnnouncement'] as bool? ?? false,
      );
      
      // Sauvegarder temporairement dans Hive
      var box = await Hive.openBox<MessageModel>(AppKeys.chatMessagesBoxName);
      await box.put(messageId, message);
      
      // Publier via MQTT
      await _mqttService.publishMessage('chat/message/send', {
        'messageId': messageId,
        'conversationId': conversationId,
        'senderId': userId,
        'content': message.content,
        'contentType': message.contentType,
        'timestamp': message.createdAt.toIso8601String(),
        'isAnnouncement': message.isAnnouncement,
      });
    } catch (e) {
      throw Exception('Erreur lors de l\'envoi du message: $e');
    }
  }

  /// Marque un message comme lu
  Future<void> markMessageAsRead(String messageId) async {
    try {
      // Mettre à jour via l'API
      await _apiService.markMessageAsRead(messageId);
      
      // Mettre à jour dans Hive
      var box = await Hive.openBox<MessageModel>(AppKeys.chatMessagesBoxName);
      var message = box.get(messageId);
      if (message != null) {
        await box.put(messageId, message.copyWith(
          status: 'read',
          readAt: DateTime.now(),
        ));
      }
    } catch (e) {
      throw Exception('Erreur lors du marquage comme lu: $e');
    }
  }

  /// Ajoute un participant à une conversation
  Future<void> addParticipant(String conversationId, Map<String, dynamic> participantData) async {
    try {
      await _apiService.addParticipant(conversationId, participantData);
      
      // Mettre à jour la conversation dans Hive
      var conversationBox = await Hive.openBox<ConversationModel>(AppKeys.chatConversationsBoxName);
      var conversation = conversationBox.get(conversationId);
      if (conversation != null) {
        var updatedParticipants = List<ParticipantModel>.from(conversation.participants);
        updatedParticipants.add(ParticipantModel.fromJson(participantData));
        await conversationBox.put(conversationId, conversation.copyWith(participants: updatedParticipants));
      }
    } catch (e) {
      throw Exception('Erreur lors de l\'ajout du participant: $e');
    }
  }

  /// Retire un participant d'une conversation
  Future<void> removeParticipant(String conversationId, String participantId) async {
    try {
      await _apiService.removeParticipant(conversationId, participantId);
      
      // Mettre à jour la conversation dans Hive
      var conversationBox = await Hive.openBox<ConversationModel>(AppKeys.chatConversationsBoxName);
      var conversation = conversationBox.get(conversationId);
      if (conversation != null) {
        var updatedParticipants = List<ParticipantModel>.from(conversation.participants);
        updatedParticipants.removeWhere((p) => p.id == participantId);
        await conversationBox.put(conversationId, conversation.copyWith(participants: updatedParticipants));
      }
    } catch (e) {
      throw Exception('Erreur lors du retrait du participant: $e');
    }
  }

  /// Crée un utilisateur anonyme (pour Resalice)
  Future<String> createAnonymousUser({String? name, String? email}) async {
    try {
      return await _apiService.createAnonymousUser(name: name, email: email);
    } catch (e) {
      throw Exception('Erreur lors de la création de l\'utilisateur anonyme: $e');
    }
  }

  /// Convertit un utilisateur anonyme en utilisateur authentifié
  Future<void> convertAnonymousToUser(String anonymousId, String userId) async {
    try {
      // Mettre à jour tous les messages de l'utilisateur anonyme
      var messageBox = await Hive.openBox<MessageModel>(AppKeys.chatMessagesBoxName);
      var messages = messageBox.values.where((m) => m.senderId == anonymousId).toList();
      
      for (var message in messages) {
        await messageBox.put(message.id, message.copyWith(
          senderId: userId,
          senderType: 'user',
        ));
      }
    } catch (e) {
      throw Exception('Erreur lors de la conversion de l\'utilisateur: $e');
    }
  }

  /// Récupère les annonces
  Future<List<ConversationModel>> getAnnouncements({bool forceRefresh = false}) async {
    try {
      // Filtrer les conversations pour n'avoir que les annonces
      var conversations = await getConversations(forceRefresh: forceRefresh);
      return conversations.where((c) => c.type == 'announcement').toList();
    } catch (e) {
      throw Exception('Erreur lors de la récupération des annonces: $e');
    }
  }

  /// Crée une nouvelle annonce
  Future<ConversationModel> createAnnouncement(Map<String, dynamic> data) async {
    try {
      // Créer la conversation comme une annonce
      data['type'] = 'announcement';
      return await createConversation(data);
    } catch (e) {
      throw Exception('Erreur lors de la création de l\'annonce: $e');
    }
  }

  /// Récupère les statistiques d'une annonce
  Future<Map<String, dynamic>> getAnnouncementStats(String conversationId) async {
    try {
      return await _apiService.getAnnouncementStats(conversationId);
    } catch (e) {
      throw Exception('Erreur lors de la récupération des statistiques: $e');
    }
  }

  /// Récupère les cibles d'audience disponibles
  Future<List<Map<String, dynamic>>> getAvailableAudienceTargets() async {
    try {
      return await _apiService.getAvailableAudienceTargets();
    } catch (e) {
      throw Exception('Erreur lors de la récupération des cibles: $e');
    }
  }

  /// Ajoute une cible d'audience
  Future<void> addAudienceTarget(String conversationId, Map<String, dynamic> targetData) async {
    try {
      // L'ajout des cibles d'audience est géré lors de la création de l'annonce
      // Mais on pourrait avoir besoin de modifier les cibles plus tard
      throw UnimplementedError('Ajout de cible non encore implémenté');
    } catch (e) {
      throw Exception('Erreur lors de l\'ajout de cible: $e');
    }
  }

  /// Retire une cible d'audience
  Future<void> removeAudienceTarget(String conversationId, String targetId) async {
    try {
      // Le retrait des cibles d'audience est géré lors de la création de l'annonce
      throw UnimplementedError('Retrait de cible non encore implémenté');
    } catch (e) {
      throw Exception('Erreur lors du retrait de cible: $e');
    }
  }
}
