package com.rosetta.messenger.database import android.content.Context import com.rosetta.messenger.crypto.CryptoManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.withContext import java.time.Instant /** * Service for secure database operations * Matches the architecture from React Native app */ class DatabaseService(context: Context) { private val database = RosettaDatabase.getDatabase(context) private val accountDao = database.accountDao() // 🚀 ОПТИМИЗАЦИЯ: LRU кэш для зашифрованных аккаунтов (избегаем повторных запросов к БД) private val accountCache = mutableMapOf() private val cacheMaxSize = 10 companion object { private const val TAG = "DatabaseService" @Volatile private var INSTANCE: DatabaseService? = null fun getInstance(context: Context): DatabaseService { return INSTANCE ?: synchronized(this) { val instance = DatabaseService(context.applicationContext) INSTANCE = instance instance } } } /** * Saves encrypted account to database */ suspend fun saveEncryptedAccount( publicKey: String, privateKeyEncrypted: String, seedPhraseEncrypted: String ): Boolean = withContext(Dispatchers.IO) { try { val account = EncryptedAccountEntity( publicKey = publicKey, privateKeyEncrypted = privateKeyEncrypted, seedPhraseEncrypted = seedPhraseEncrypted, createdAt = Instant.now().toString(), lastUsed = Instant.now().toString(), isActive = true ) accountDao.insertAccount(account) // 🚀 ОПТИМИЗАЦИЯ: Обновляем кэш после сохранения accountCache[publicKey] = account true } catch (e: Exception) { false } } /** * Loads encrypted account from database * 🚀 ОПТИМИЗАЦИЯ: Использует кэш для избежания повторных запросов */ suspend fun getEncryptedAccount(publicKey: String): EncryptedAccountEntity? = withContext(Dispatchers.IO) { try { // Проверяем кэш сначала accountCache[publicKey]?.let { return@withContext it } // Загружаем из БД и кэшируем val account = accountDao.getAccount(publicKey) account?.let { accountCache[publicKey] = it // Ограничиваем размер кэша if (accountCache.size > cacheMaxSize) { accountCache.remove(accountCache.keys.first()) } } account } catch (e: Exception) { null } } /** * Gets all encrypted accounts */ suspend fun getAllEncryptedAccounts(): List = withContext(Dispatchers.IO) { try { accountDao.getAllAccounts() } catch (e: Exception) { emptyList() } } /** * Gets all accounts as Flow for reactive updates */ fun getAllAccountsFlow(): Flow> { return accountDao.getAllAccountsFlow() } /** * Updates last used timestamp for account */ suspend fun updateLastUsed(publicKey: String) = withContext(Dispatchers.IO) { try { accountDao.updateLastUsed(publicKey, Instant.now().toString()) } catch (e: Exception) { } } /** * Deletes account from database */ suspend fun deleteAccount(publicKey: String): Boolean = withContext(Dispatchers.IO) { try { accountDao.deleteAccount(publicKey) true } catch (e: Exception) { false } } /** * Checks if any accounts exist */ suspend fun hasAccounts(): Boolean = withContext(Dispatchers.IO) { try { accountDao.getAccountCount() > 0 } catch (e: Exception) { false } } /** * Decrypts account with password * Returns decrypted private key and seed phrase */ suspend fun decryptAccount( publicKey: String, password: String ): DecryptedAccountData? = withContext(Dispatchers.IO) { try { val encryptedAccount = getEncryptedAccount(publicKey) ?: run { return@withContext null } // Decrypt private key val privateKey = try { CryptoManager.decryptWithPassword( encryptedAccount.privateKeyEncrypted, password ) ?: run { return@withContext null } } catch (e: Exception) { return@withContext null } // Decrypt seed phrase val seedPhraseString = try { CryptoManager.decryptWithPassword( encryptedAccount.seedPhraseEncrypted, password ) ?: run { return@withContext null } } catch (e: Exception) { return@withContext null } val seedPhrase = seedPhraseString.split(" ") // Generate private key hash for protocol val privateKeyHash = CryptoManager.generatePrivateKeyHash(privateKey) DecryptedAccountData( publicKey = publicKey, privateKey = privateKey, privateKeyHash = privateKeyHash, seedPhrase = seedPhrase ) } catch (e: Exception) { null } } } /** * Decrypted account data */ data class DecryptedAccountData( val publicKey: String, val privateKey: String, val privateKeyHash: String, val seedPhrase: List )