package com.rssuper.search import com.rssuper.database.daos.FeedItemDao import com.rssuper.database.daos.SearchHistoryDao import com.rssuper.database.entities.SearchHistoryEntity import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow /** * SearchService - Provides search functionality with FTS */ class SearchService( private val feedItemDao: FeedItemDao, private val searchHistoryDao: SearchHistoryDao, private val resultProvider: SearchResultProvider ) { private data class CacheEntry(val results: List, val timestamp: Long) private val cache = object : LinkedHashMap(maxCacheSize, 0.75f, true) { override fun removeEldestEntry(eldest: MutableEntry?): Boolean { return size > maxCacheSize || eldest?.value?.let { isCacheEntryExpired(it) } ?: false } } private val maxCacheSize = 100 private val cacheExpirationMs = 5 * 60 * 1000L // 5 minutes private fun isCacheEntryExpired(entry: CacheEntry): Boolean { return System.currentTimeMillis() - entry.timestamp > cacheExpirationMs } private fun cleanExpiredCacheEntries() { val expiredKeys = cache.entries.filter { isCacheEntryExpired(it.value) }.map { it.key } expiredKeys.forEach { cache.remove(it) } } fun search(query: String): Flow> { val validation = SearchResultProvider.validateQuery(query) if (validation.isFailure) { return flow { emit(emptyList()) } } val cacheKey = query.hashCode().toString() // Clean expired entries periodically if (cache.size > maxCacheSize / 2) { cleanExpiredCacheEntries() } // Return cached results if available and not expired cache[cacheKey]?.let { entry -> if (!isCacheEntryExpired(entry)) { return flow { emit(entry.results) } } } return flow { val result = resultProvider.search(query) val results = result.getOrDefault(emptyList()) cache[cacheKey] = CacheEntry(results, System.currentTimeMillis()) emit(results) } } fun searchBySubscription(query: String, subscriptionId: String): Flow> { val validation = SearchResultProvider.validateQuery(query) if (validation.isFailure) { return flow { emit(emptyList()) } } return flow { val result = resultProvider.searchBySubscription(query, subscriptionId) emit(result.getOrDefault(emptyList())) } } suspend fun searchAndSave(query: String): List { val validation = SearchResultProvider.validateQuery(query) if (validation.isFailure) { return emptyList() } val result = resultProvider.search(query) val results = result.getOrDefault(emptyList()) // Save to search history saveSearchHistory(query) return results } suspend fun saveSearchHistory(query: String) { val searchHistory = SearchHistoryEntity( id = System.currentTimeMillis().toString(), query = query, filtersJson = null, timestamp = System.currentTimeMillis() ) searchHistoryDao.insertSearchHistory(searchHistory) } fun getSearchHistory(): Flow> { return searchHistoryDao.getAllSearchHistory() } suspend fun getRecentSearches(limit: Int = 10): List { return searchHistoryDao.getRecentSearches(limit).firstOrNull() ?: emptyList() } suspend fun clearSearchHistory() { searchHistoryDao.deleteAllSearchHistory() } fun getSearchSuggestions(query: String): Flow> { return searchHistoryDao.searchHistory(query) } fun clearCache() { cache.clear() } }