import 'package:flutter/material.dart'; import '/data/models/contact_model.dart'; import '/data/repositories/contact_repository.dart'; import '/domain/services/crypto_service.dart'; import 'dart:isolate'; import 'package:cryptography/cryptography.dart'; import 'package:flutter/foundation.dart'; import 'package:chepuhagram/data/models/message_model.dart'; class ContactProvider extends ChangeNotifier { final ContactRepository _repository = ContactRepository(); final CryptoService _cryptoService; ContactProvider(this._cryptoService); final Map _sharedKeysCache = {}; List _contacts = []; List _allContacts = []; bool _isLoading = false; bool _isFirstLoad = true; String? _error; int? _currentUserId; List get contacts => _contacts; List get allContacts => _allContacts; bool get isLoading => _isLoading; String? get error => _error; Map get sharedKeysCache => _sharedKeysCache; void setSharedKey(int contactId, SecretKey key) { _sharedKeysCache[contactId] = key; } void setCurrentUserId(int? id) { _currentUserId = id; notifyListeners(); } int? getCurrentUserId() { return _currentUserId; } Future loadContacts({bool enrichContacts = true}) async { if (_isFirstLoad) { _isFirstLoad = false; _isLoading = true; } _error = null; notifyListeners(); try { final allContacts = await _repository.fetchChatContacts(); final userIdCopy = _currentUserId; _contacts = await Isolate.run(() { return allContacts .where((contact) => contact.id != userIdCopy) .toList(); }); // Check if user changed during isolate execution if (userIdCopy != _currentUserId) { return; // Discard stale data } _allContacts = _contacts; _isLoading = false; notifyListeners(); if (enrichContacts) { await _enrichContactsWithLastMessages(); } } catch (e) { _error = e.toString(); print("❌ ОШИБКА ПРИ ЗАГРУЗКЕ КОНТАКТОВ: $_error"); } finally { if (_error != null) { _isLoading = false; } notifyListeners(); } } Future loadAllContactsForNewChat() async { _isLoading = true; _error = null; notifyListeners(); try { final allContacts = await _repository.fetchAllUsers(); _allContacts = allContacts .where((contact) => contact.id != _currentUserId) .toList(); } catch (e) { _error = e.toString(); } finally { _isLoading = false; notifyListeners(); } } String _getMediaPreview(MessageType type) { switch (type) { case MessageType.videoNote: return '[Кружок]'; case MessageType.voiceNote: return '[Голосовое]'; case MessageType.image: return '[Фото]'; case MessageType.video: return '[Видео]'; case MessageType.file: return '[Файл]'; case MessageType.text: default: return ''; } } Future _enrichContactsWithLastMessages() async { final myPrivKeyBase64 = await _cryptoService.getPrivateKey(); if (myPrivKeyBase64 == null) return; // Создаем локальные копии для передачи final contactsToProcess = List.from(_contacts); final cacheCopy = Map.from(_sharedKeysCache); print('Avialable cache for contacts: ${cacheCopy.length}'); try { final updatedContacts = await compute(CryptoService.bulkDecryptContacts, { 'contacts': contactsToProcess, 'privKey': myPrivKeyBase64, 'cache': cacheCopy, }); for (var contact in updatedContacts) { print( 'Decrypted contact: ${contact.name} ${contact.surname}, lastMessage: ${contact.lastMessage}, isDecrypted: ${contact.isLastMsgDecrypted}', ); } _contacts = updatedContacts; notifyListeners(); } catch (e) { print("Ошибка дешифровки: $e"); } } Future updateContact( int userId, { String? lastMessage, DateTime? lastMessageTime, bool? isLastMsgDecrypted, int? unreadCount, }) async { try { final updatedContact = await _repository.fetchContactById(userId); final index = _contacts.indexWhere((c) => c.id == userId); if (index != -1) { final existing = _contacts[index]; _contacts[index] = existing.copyWith( username: updatedContact.username, name: updatedContact.name, surname: updatedContact.surname, avatarUrl: updatedContact.avatarUrl, avatarFileId: updatedContact.avatarFileId, isOnline: updatedContact.isOnline, publicKey: updatedContact.publicKey, lastMessage: lastMessage ?? existing.lastMessage, lastMessageTime: lastMessageTime ?? existing.lastMessageTime, isLastMsgDecrypted: isLastMsgDecrypted ?? existing.isLastMsgDecrypted, unreadCount: unreadCount ?? existing.unreadCount, lastMessageId: updatedContact.lastMessageId, ); print( "Контакт ${updatedContact.name} ${updatedContact.surname} ${updatedContact.id} ${updatedContact.avatarFileId} ${updatedContact.avatarUrl} обновлен", ); notifyListeners(); } } catch (e) { print("Error updating contact: $e"); } } Future updateContactOnlineStatus(int userId, bool isOnline) async { try { final index = _contacts.indexWhere((c) => c.id == userId); if (index != -1) { final existing = _contacts[index]; _contacts[index] = existing.copyWith( isOnline: isOnline, username: existing.username, name: existing.name, surname: existing.surname, avatarUrl: existing.avatarUrl, avatarFileId: existing.avatarFileId, publicKey: existing.publicKey, ); print("Контакт ${existing.name} ${existing.surname} онлайн обновлен"); notifyListeners(); } } catch (e) { print("Error updating contact: $e"); } } Future updateContactLastMessage(int contactId, {String? lastMessage, DateTime? lastMessageTime, bool? isLastMsgDecrypted, int? lastMessageId, bool isEdited = false}) async { try { final index = _contacts.indexWhere((c) => c.id == contactId); if (index != -1) { final existing = _contacts[index]; String displayMessage; if (isEdited) { final baseMessage = lastMessage ?? existing.lastMessage; final rawMessage = baseMessage != null && baseMessage.isNotEmpty ? baseMessage : 'Сообщение изменено'; displayMessage = rawMessage.endsWith('(изменено)') ? rawMessage : '$rawMessage (изменено)'; } else { displayMessage = lastMessage ?? existing.lastMessage ?? ''; } _contacts[index] = existing.copyWith( lastMessage: displayMessage.isNotEmpty ? displayMessage : null, lastMessageTime: lastMessageTime, isLastMsgDecrypted: isLastMsgDecrypted ?? existing.isLastMsgDecrypted, lastMessageId: lastMessageId, ); print("Последнее сообщение контакта ${existing.name} обновлено: $displayMessage"); notifyListeners(); } } catch (e) { print("Error updating contact last message: $e"); } } Future refreshContactLastMessage(int contactId) async { try { // Получить предпоследнее сообщение из базы данных final lastMessages = await _repository.getLastMessagesForContact(contactId, limit: 2); if (lastMessages.isNotEmpty) { final lastMsg = lastMessages.first; final contact = _contacts.firstWhere((c) => c.id == contactId); final messageId = int.tryParse(lastMsg['id'].toString()); final timestamp = DateTime.tryParse(lastMsg['timestamp']?.toString() ?? ''); final myPrivKeyBase64 = await _cryptoService.getPrivateKey(); if (myPrivKeyBase64 != null && contact.publicKey != null) { try { final sharedSecret = await _cryptoService.deriveSharedSecret( myPrivKeyBase64, contact.publicKey!, ); final decryptedText = await _cryptoService.decryptMessage( lastMsg['content'], sharedSecret, ); await updateContactLastMessage( contactId, lastMessage: decryptedText, lastMessageTime: timestamp, isLastMsgDecrypted: true, lastMessageId: messageId, ); } catch (e) { print("Error decrypting last message: $e"); await updateContactLastMessage( contactId, lastMessage: lastMsg['content50'] ?? 'Зашифрованное сообщение', lastMessageTime: timestamp, isLastMsgDecrypted: false, lastMessageId: messageId, ); } } else { await updateContactLastMessage( contactId, lastMessage: lastMsg['content50'] ?? 'Зашифрованное сообщение', lastMessageTime: timestamp, isLastMsgDecrypted: false, lastMessageId: messageId, ); } } else { // Нет сообщений await updateContactLastMessage( contactId, lastMessage: null, lastMessageTime: null, lastMessageId: null, ); } } catch (e) { print("Error refreshing contact last message: $e"); } } }